OSDN Git Service

Fixing unsafe use of Thread.current variable :current_user
authorAngus MacArthur <amacarthur@blackberry.com>
Fri, 4 Oct 2013 19:11:50 +0000 (15:11 -0400)
committerAngus MacArthur <amacarthur@blackberry.com>
Wed, 16 Oct 2013 05:20:53 +0000 (01:20 -0400)
14 files changed:
app/controllers/application_controller.rb
db/fixtures/development/09_issues.rb
db/fixtures/development/10_merge_requests.rb
features/support/env.rb
lib/api/helpers.rb
lib/api/issues.rb
lib/api/merge_requests.rb
lib/api/milestones.rb
lib/api/notes.rb
spec/models/project_spec.rb
spec/requests/api/issues_spec.rb
spec/requests/api/milestones_spec.rb
spec/requests/api/notes_spec.rb
spec/support/test_env.rb

index 85b9586..cfa3cac 100644 (file)
@@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base
   before_filter :authenticate_user!
   before_filter :reject_blocked!
   before_filter :check_password_expiration
-  before_filter :set_current_user_for_thread
+  around_filter :set_current_user_for_thread
   before_filter :add_abilities
   before_filter :dev_tools if Rails.env == 'development'
   before_filter :default_headers
@@ -50,6 +50,11 @@ class ApplicationController < ActionController::Base
 
   def set_current_user_for_thread
     Thread.current[:current_user] = current_user
+    begin
+      yield
+    ensure
+      Thread.current[:current_user] = nil
+    end
   end
 
   def abilities
index 31ba772..1535c16 100644 (file)
@@ -11,17 +11,22 @@ Gitlab::Seeder.quiet do
     next unless user
 
     user_id = user.id
-    Thread.current[:current_user] = user
 
-    Issue.seed(:id, [{
-      id: i,
-      project_id: project.id,
-      author_id: user_id,
-      assignee_id: user_id,
-      state: ['opened', 'closed'].sample,
-      milestone: project.milestones.sample,
-      title: Faker::Lorem.sentence(6)
-    }])
+    begin
+      Thread.current[:current_user] = user
+
+      Issue.seed(:id, [{
+        id: i,
+        project_id: project.id,
+        author_id: user_id,
+        assignee_id: user_id,
+        state: ['opened', 'closed'].sample,
+        milestone: project.milestones.sample,
+        title: Faker::Lorem.sentence(6)
+      }])
+    ensure
+      Thread.current[:current_user] = nil
+    end
     print('.')
   end
 
index 1e61ea2..75f6e56 100644 (file)
@@ -17,19 +17,23 @@ Gitlab::Seeder.quiet do
     next if branches.uniq.size < 2
 
     user_id = user.id
-    Thread.current[:current_user] = user
-
-    MergeRequest.seed(:id, [{
-      id: i,
-      source_branch: branches.first,
-      target_branch: branches.last,
-      source_project_id: project.id,
-      target_project_id: project.id,
-      author_id: user_id,
-      assignee_id: user_id,
-      milestone: project.milestones.sample,
-      title: Faker::Lorem.sentence(6)
-    }])
+    begin
+      Thread.current[:current_user] = user
+
+      MergeRequest.seed(:id, [{
+        id: i,
+        source_branch: branches.first,
+        target_branch: branches.last,
+        source_project_id: project.id,
+        target_project_id: project.id,
+        author_id: user_id,
+        assignee_id: user_id,
+        milestone: project.milestones.sample,
+        title: Faker::Lorem.sentence(6)
+      }])
+    ensure
+      Thread.current[:current_user] = nil
+    end
     print('.')
   end
 end
index 8798e62..d27a73e 100644 (file)
@@ -51,4 +51,6 @@ Spinach.hooks.before_run do
   RSpec::Mocks::setup self
 
   include FactoryGirl::Syntax::Methods
+  MergeRequestObserver.any_instance.stub(current_user: create(:user))
 end
+
index 2b0c672..e09a466 100644 (file)
@@ -31,6 +31,15 @@ module API
       end
     end
 
+    def set_current_user_for_thread
+      Thread.current[:current_user] = current_user
+      begin
+        yield
+      ensure
+        Thread.current[:current_user] = nil
+      end
+    end
+
     def user_project
       @project ||= find_project(params[:id])
       @project || not_found!
index a15203d..3d15c35 100644 (file)
@@ -2,7 +2,6 @@ module API
   # Issues API
   class Issues < Grape::API
     before { authenticate! }
-    before { Thread.current[:current_user] = current_user }
 
     resource :issues do
       # Get currently authenticated user's issues
@@ -49,15 +48,17 @@ module API
       # Example Request:
       #   POST /projects/:id/issues
       post ":id/issues" do
-        required_attributes! [:title]
-        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
-        attrs[:label_list] = params[:labels] if params[:labels].present?
-        @issue = user_project.issues.new attrs
-        @issue.author = current_user
-        if @issue.save
-          present @issue, with: Entities::Issue
-        else
-          not_found!
+        set_current_user_for_thread do
+          required_attributes! [:title]
+          attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
+          attrs[:label_list] = params[:labels] if params[:labels].present?
+          @issue = user_project.issues.new attrs
+          @issue.author = current_user
+          if @issue.save
+            present @issue, with: Entities::Issue
+          else
+            not_found!
+          end
         end
       end
 
@@ -75,16 +76,18 @@ module API
       # Example Request:
       #   PUT /projects/:id/issues/:issue_id
       put ":id/issues/:issue_id" do
-        @issue = user_project.issues.find(params[:issue_id])
-        authorize! :modify_issue, @issue
+        set_current_user_for_thread do
+          @issue = user_project.issues.find(params[:issue_id])
+          authorize! :modify_issue, @issue
 
-        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
-        attrs[:label_list] = params[:labels] if params[:labels].present?
+          attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
+          attrs[:label_list] = params[:labels] if params[:labels].present?
 
-        if @issue.update_attributes attrs
-          present @issue, with: Entities::Issue
-        else
-          not_found!
+          if @issue.update_attributes attrs
+            present @issue, with: Entities::Issue
+          else
+            not_found!
+          end
         end
       end
 
index d690f1d..3f4bec8 100644 (file)
@@ -2,7 +2,6 @@ module API
   # MergeRequest API
   class MergeRequests < Grape::API
     before { authenticate! }
-    before { Thread.current[:current_user] = current_user }
 
     resource :projects do
       helpers do
@@ -70,28 +69,30 @@ module API
       #   POST /projects/:id/merge_requests
       #
       post ":id/merge_requests" do
-        authorize! :write_merge_request, user_project
-        required_attributes! [:source_branch, :target_branch, :title]
-        attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
-        merge_request = user_project.merge_requests.new(attrs)
-        merge_request.author = current_user
-        merge_request.source_project = user_project
-        target_project_id = attrs[:target_project_id]
-        if not_fork?(target_project_id, user_project)
-          merge_request.target_project = user_project
-        else
-          if target_matches_fork(target_project_id,user_project)
-            merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
+        set_current_user_for_thread do
+          authorize! :write_merge_request, user_project
+          required_attributes! [:source_branch, :target_branch, :title]
+          attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
+          merge_request = user_project.merge_requests.new(attrs)
+          merge_request.author = current_user
+          merge_request.source_project = user_project
+          target_project_id = attrs[:target_project_id]
+          if not_fork?(target_project_id, user_project)
+            merge_request.target_project = user_project
           else
-            render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
+            if target_matches_fork(target_project_id,user_project)
+              merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
+            else
+              render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
+            end
           end
-        end
 
-        if merge_request.save
-          merge_request.reload_code
-          present merge_request, with: Entities::MergeRequest
-        else
-          handle_merge_request_errors! merge_request.errors
+          if merge_request.save
+            merge_request.reload_code
+            present merge_request, with: Entities::MergeRequest
+          else
+            handle_merge_request_errors! merge_request.errors
+          end
         end
       end
 
@@ -109,17 +110,19 @@ module API
       #   PUT /projects/:id/merge_request/:merge_request_id
       #
       put ":id/merge_request/:merge_request_id" do
-        attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
-        merge_request = user_project.merge_requests.find(params[:merge_request_id])
+        set_current_user_for_thread do
+          attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
+          merge_request = user_project.merge_requests.find(params[:merge_request_id])
 
-        authorize! :modify_merge_request, merge_request
+          authorize! :modify_merge_request, merge_request
 
-        if merge_request.update_attributes attrs
-          merge_request.reload_code
-          merge_request.mark_as_unchecked
-          present merge_request, with: Entities::MergeRequest
-        else
-          handle_merge_request_errors! merge_request.errors
+          if merge_request.update_attributes attrs
+            merge_request.reload_code
+            merge_request.mark_as_unchecked
+            present merge_request, with: Entities::MergeRequest
+          else
+            handle_merge_request_errors! merge_request.errors
+          end
         end
       end
 
@@ -133,16 +136,18 @@ module API
       #   POST /projects/:id/merge_request/:merge_request_id/comments
       #
       post ":id/merge_request/:merge_request_id/comments" do
-        required_attributes! [:note]
+        set_current_user_for_thread do
+          required_attributes! [:note]
 
-        merge_request = user_project.merge_requests.find(params[:merge_request_id])
-        note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
-        note.author = current_user
+          merge_request = user_project.merge_requests.find(params[:merge_request_id])
+          note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
+          note.author = current_user
 
-        if note.save
-          present note, with: Entities::MRNote
-        else
-          not_found!
+          if note.save
+            present note, with: Entities::MRNote
+          else
+            not_found!
+          end
         end
       end
 
index aee12e7..f7e63b2 100644 (file)
@@ -40,15 +40,17 @@ module API
       # Example Request:
       #   POST /projects/:id/milestones
       post ":id/milestones" do
-        authorize! :admin_milestone, user_project
-        required_attributes! [:title]
+        set_current_user_for_thread do
+          authorize! :admin_milestone, user_project
+          required_attributes! [:title]
 
-        attrs = attributes_for_keys [:title, :description, :due_date]
-        @milestone = user_project.milestones.new attrs
-        if @milestone.save
-          present @milestone, with: Entities::Milestone
-        else
-          not_found!
+          attrs = attributes_for_keys [:title, :description, :due_date]
+          @milestone = user_project.milestones.new attrs
+          if @milestone.save
+            present @milestone, with: Entities::Milestone
+          else
+            not_found!
+          end
         end
       end
 
@@ -64,14 +66,16 @@ module API
       # Example Request:
       #   PUT /projects/:id/milestones/:milestone_id
       put ":id/milestones/:milestone_id" do
-        authorize! :admin_milestone, user_project
+        set_current_user_for_thread do
+          authorize! :admin_milestone, user_project
 
-        @milestone = user_project.milestones.find(params[:milestone_id])
-        attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
-        if @milestone.update_attributes attrs
-          present @milestone, with: Entities::Milestone
-        else
-          not_found!
+          @milestone = user_project.milestones.find(params[:milestone_id])
+          attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
+          if @milestone.update_attributes attrs
+            present @milestone, with: Entities::Milestone
+          else
+            not_found!
+          end
         end
       end
     end
index cb2bc76..f21907b 100644 (file)
@@ -41,17 +41,19 @@ module API
       # Example Request:
       #   POST /projects/:id/notes
       post ":id/notes" do
-        required_attributes! [:body]
+        set_current_user_for_thread do
+          required_attributes! [:body]
 
-        @note = user_project.notes.new(note: params[:body])
-        @note.author = current_user
+          @note = user_project.notes.new(note: params[:body])
+          @note.author = current_user
 
-        if @note.save
-          present @note, with: Entities::Note
-        else
-          # :note is exposed as :body, but :note is set on error
-          bad_request!(:note) if @note.errors[:note].any?
-          not_found!
+          if @note.save
+            present @note, with: Entities::Note
+          else
+            # :note is exposed as :body, but :note is set on error
+            bad_request!(:note) if @note.errors[:note].any?
+            not_found!
+          end
         end
       end
 
@@ -97,17 +99,19 @@ module API
         #   POST /projects/:id/issues/:noteable_id/notes
         #   POST /projects/:id/snippets/:noteable_id/notes
         post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
-          required_attributes! [:body]
+          set_current_user_for_thread do
+            required_attributes! [:body]
 
-          @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
-          @note = @noteable.notes.new(note: params[:body])
-          @note.author = current_user
-          @note.project = user_project
+            @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
+            @note = @noteable.notes.new(note: params[:body])
+            @note.author = current_user
+            @note.project = user_project
 
-          if @note.save
-            present @note, with: Entities::Note
-          else
-            not_found!
+            if @note.save
+              present @note, with: Entities::Note
+            else
+              not_found!
+            end
           end
         end
       end
index c726600..88ea692 100644 (file)
 require 'spec_helper'
 
 describe Project do
-  let(:user) { create(:user) }
-
-  before do
-    enable_observers
-    Thread.current[:current_user] = user
-  end
-
-  after  { disable_observers }
+  before { enable_observers }
+  after { disable_observers }
 
   describe "Associations" do
     it { should belong_to(:group) }
index e9422cd..c55025d 100644 (file)
@@ -100,4 +100,16 @@ describe API::API do
       response.status.should == 405
     end
   end
+
+  describe "PUT /projects/:id/issues/:issue_id to test observer on close" do
+    before { enable_observers }
+    after { disable_observers }
+
+    it "should create an activity event when an issue is closed" do
+      Event.should_receive(:create)
+
+      put api("/projects/#{project.id}/issues/#{issue.id}", user),
+        state_event: "close"
+    end
+  end
 end
index e79ce08..febfc63 100644 (file)
@@ -90,4 +90,16 @@ describe API::API do
       json_response['state'].should == 'closed'
     end
   end
+
+  describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
+    before { enable_observers }
+    after { disable_observers }
+
+    it "should create an activity event when an milestone is closed" do
+      Event.should_receive(:create)
+
+      put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
+          state_event: 'close'
+    end
+  end
 end
index ba18b12..6ed96eb 100644 (file)
@@ -176,4 +176,16 @@ describe API::API do
       end
     end
   end
+
+  describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
+    before { enable_observers }
+    after { disable_observers }
+
+    it "should create an activity event when an issue note is created" do
+      Event.should_receive(:create)
+
+      post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
+    end
+  end
+
 end
index 203e661..6bb5ce6 100644 (file)
@@ -84,6 +84,11 @@ module TestEnv
     Repository.any_instance.stub(
       size: 12.45
     )
+
+    ActivityObserver.any_instance.stub(
+        current_user: double("current_user", id: 1)
+    )
+
   end
 
   def clear_repo_dir(namespace, name)