Commit aefe2e952f33267ce38fb9270400f4f6f194d37b

Authored by Angus MacArthur
1 parent a8eb525e

Fixing unsafe use of Thread.current variable :current_user

app/controllers/application_controller.rb
... ... @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base
2 2 before_filter :authenticate_user!
3 3 before_filter :reject_blocked!
4 4 before_filter :check_password_expiration
5   - before_filter :set_current_user_for_thread
  5 + around_filter :set_current_user_for_thread
6 6 before_filter :add_abilities
7 7 before_filter :dev_tools if Rails.env == 'development'
8 8 before_filter :default_headers
... ... @@ -50,6 +50,11 @@ class ApplicationController < ActionController::Base
50 50  
51 51 def set_current_user_for_thread
52 52 Thread.current[:current_user] = current_user
  53 + begin
  54 + yield
  55 + ensure
  56 + Thread.current[:current_user] = nil
  57 + end
53 58 end
54 59  
55 60 def abilities
... ...
db/fixtures/development/09_issues.rb
... ... @@ -11,17 +11,22 @@ Gitlab::Seeder.quiet do
11 11 next unless user
12 12  
13 13 user_id = user.id
14   - Thread.current[:current_user] = user
15 14  
16   - Issue.seed(:id, [{
17   - id: i,
18   - project_id: project.id,
19   - author_id: user_id,
20   - assignee_id: user_id,
21   - state: ['opened', 'closed'].sample,
22   - milestone: project.milestones.sample,
23   - title: Faker::Lorem.sentence(6)
24   - }])
  15 + begin
  16 + Thread.current[:current_user] = user
  17 +
  18 + Issue.seed(:id, [{
  19 + id: i,
  20 + project_id: project.id,
  21 + author_id: user_id,
  22 + assignee_id: user_id,
  23 + state: ['opened', 'closed'].sample,
  24 + milestone: project.milestones.sample,
  25 + title: Faker::Lorem.sentence(6)
  26 + }])
  27 + ensure
  28 + Thread.current[:current_user] = nil
  29 + end
25 30 print('.')
26 31 end
27 32  
... ...
db/fixtures/development/10_merge_requests.rb
... ... @@ -17,19 +17,23 @@ Gitlab::Seeder.quiet do
17 17 next if branches.uniq.size < 2
18 18  
19 19 user_id = user.id
20   - Thread.current[:current_user] = user
21   -
22   - MergeRequest.seed(:id, [{
23   - id: i,
24   - source_branch: branches.first,
25   - target_branch: branches.last,
26   - source_project_id: project.id,
27   - target_project_id: project.id,
28   - author_id: user_id,
29   - assignee_id: user_id,
30   - milestone: project.milestones.sample,
31   - title: Faker::Lorem.sentence(6)
32   - }])
  20 + begin
  21 + Thread.current[:current_user] = user
  22 +
  23 + MergeRequest.seed(:id, [{
  24 + id: i,
  25 + source_branch: branches.first,
  26 + target_branch: branches.last,
  27 + source_project_id: project.id,
  28 + target_project_id: project.id,
  29 + author_id: user_id,
  30 + assignee_id: user_id,
  31 + milestone: project.milestones.sample,
  32 + title: Faker::Lorem.sentence(6)
  33 + }])
  34 + ensure
  35 + Thread.current[:current_user] = nil
  36 + end
33 37 print('.')
34 38 end
35 39 end
... ...
features/support/env.rb
... ... @@ -51,4 +51,6 @@ Spinach.hooks.before_run do
51 51 RSpec::Mocks::setup self
52 52  
53 53 include FactoryGirl::Syntax::Methods
  54 + MergeRequestObserver.any_instance.stub(current_user: create(:user))
54 55 end
  56 +
... ...
lib/api/helpers.rb
... ... @@ -31,6 +31,15 @@ module API
31 31 end
32 32 end
33 33  
  34 + def set_current_user_for_thread
  35 + Thread.current[:current_user] = current_user
  36 + begin
  37 + yield
  38 + ensure
  39 + Thread.current[:current_user] = nil
  40 + end
  41 + end
  42 +
34 43 def user_project
35 44 @project ||= find_project(params[:id])
36 45 @project || not_found!
... ...
lib/api/issues.rb
... ... @@ -2,7 +2,6 @@ module API
2 2 # Issues API
3 3 class Issues < Grape::API
4 4 before { authenticate! }
5   - before { Thread.current[:current_user] = current_user }
6 5  
7 6 resource :issues do
8 7 # Get currently authenticated user's issues
... ... @@ -49,15 +48,17 @@ module API
49 48 # Example Request:
50 49 # POST /projects/:id/issues
51 50 post ":id/issues" do
52   - required_attributes! [:title]
53   - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
54   - attrs[:label_list] = params[:labels] if params[:labels].present?
55   - @issue = user_project.issues.new attrs
56   - @issue.author = current_user
57   - if @issue.save
58   - present @issue, with: Entities::Issue
59   - else
60   - not_found!
  51 + set_current_user_for_thread do
  52 + required_attributes! [:title]
  53 + attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
  54 + attrs[:label_list] = params[:labels] if params[:labels].present?
  55 + @issue = user_project.issues.new attrs
  56 + @issue.author = current_user
  57 + if @issue.save
  58 + present @issue, with: Entities::Issue
  59 + else
  60 + not_found!
  61 + end
61 62 end
62 63 end
63 64  
... ... @@ -75,16 +76,18 @@ module API
75 76 # Example Request:
76 77 # PUT /projects/:id/issues/:issue_id
77 78 put ":id/issues/:issue_id" do
78   - @issue = user_project.issues.find(params[:issue_id])
79   - authorize! :modify_issue, @issue
  79 + set_current_user_for_thread do
  80 + @issue = user_project.issues.find(params[:issue_id])
  81 + authorize! :modify_issue, @issue
80 82  
81   - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
82   - attrs[:label_list] = params[:labels] if params[:labels].present?
  83 + attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
  84 + attrs[:label_list] = params[:labels] if params[:labels].present?
83 85  
84   - if @issue.update_attributes attrs
85   - present @issue, with: Entities::Issue
86   - else
87   - not_found!
  86 + if @issue.update_attributes attrs
  87 + present @issue, with: Entities::Issue
  88 + else
  89 + not_found!
  90 + end
88 91 end
89 92 end
90 93  
... ...
lib/api/merge_requests.rb
... ... @@ -2,7 +2,6 @@ module API
2 2 # MergeRequest API
3 3 class MergeRequests < Grape::API
4 4 before { authenticate! }
5   - before { Thread.current[:current_user] = current_user }
6 5  
7 6 resource :projects do
8 7 helpers do
... ... @@ -70,28 +69,30 @@ module API
70 69 # POST /projects/:id/merge_requests
71 70 #
72 71 post ":id/merge_requests" do
73   - authorize! :write_merge_request, user_project
74   - required_attributes! [:source_branch, :target_branch, :title]
75   - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
76   - merge_request = user_project.merge_requests.new(attrs)
77   - merge_request.author = current_user
78   - merge_request.source_project = user_project
79   - target_project_id = attrs[:target_project_id]
80   - if not_fork?(target_project_id, user_project)
81   - merge_request.target_project = user_project
82   - else
83   - if target_matches_fork(target_project_id,user_project)
84   - merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
  72 + set_current_user_for_thread do
  73 + authorize! :write_merge_request, user_project
  74 + required_attributes! [:source_branch, :target_branch, :title]
  75 + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
  76 + merge_request = user_project.merge_requests.new(attrs)
  77 + merge_request.author = current_user
  78 + merge_request.source_project = user_project
  79 + target_project_id = attrs[:target_project_id]
  80 + if not_fork?(target_project_id, user_project)
  81 + merge_request.target_project = user_project
85 82 else
86   - render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
  83 + if target_matches_fork(target_project_id,user_project)
  84 + merge_request.target_project = Project.find_by_id(attrs[:target_project_id])
  85 + else
  86 + render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
  87 + end
87 88 end
88   - end
89 89  
90   - if merge_request.save
91   - merge_request.reload_code
92   - present merge_request, with: Entities::MergeRequest
93   - else
94   - handle_merge_request_errors! merge_request.errors
  90 + if merge_request.save
  91 + merge_request.reload_code
  92 + present merge_request, with: Entities::MergeRequest
  93 + else
  94 + handle_merge_request_errors! merge_request.errors
  95 + end
95 96 end
96 97 end
97 98  
... ... @@ -109,17 +110,19 @@ module API
109 110 # PUT /projects/:id/merge_request/:merge_request_id
110 111 #
111 112 put ":id/merge_request/:merge_request_id" do
112   - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
113   - merge_request = user_project.merge_requests.find(params[:merge_request_id])
  113 + set_current_user_for_thread do
  114 + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
  115 + merge_request = user_project.merge_requests.find(params[:merge_request_id])
114 116  
115   - authorize! :modify_merge_request, merge_request
  117 + authorize! :modify_merge_request, merge_request
116 118  
117   - if merge_request.update_attributes attrs
118   - merge_request.reload_code
119   - merge_request.mark_as_unchecked
120   - present merge_request, with: Entities::MergeRequest
121   - else
122   - handle_merge_request_errors! merge_request.errors
  119 + if merge_request.update_attributes attrs
  120 + merge_request.reload_code
  121 + merge_request.mark_as_unchecked
  122 + present merge_request, with: Entities::MergeRequest
  123 + else
  124 + handle_merge_request_errors! merge_request.errors
  125 + end
123 126 end
124 127 end
125 128  
... ... @@ -133,16 +136,18 @@ module API
133 136 # POST /projects/:id/merge_request/:merge_request_id/comments
134 137 #
135 138 post ":id/merge_request/:merge_request_id/comments" do
136   - required_attributes! [:note]
  139 + set_current_user_for_thread do
  140 + required_attributes! [:note]
137 141  
138   - merge_request = user_project.merge_requests.find(params[:merge_request_id])
139   - note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
140   - note.author = current_user
  142 + merge_request = user_project.merge_requests.find(params[:merge_request_id])
  143 + note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
  144 + note.author = current_user
141 145  
142   - if note.save
143   - present note, with: Entities::MRNote
144   - else
145   - not_found!
  146 + if note.save
  147 + present note, with: Entities::MRNote
  148 + else
  149 + not_found!
  150 + end
146 151 end
147 152 end
148 153  
... ...
lib/api/milestones.rb
... ... @@ -40,15 +40,17 @@ module API
40 40 # Example Request:
41 41 # POST /projects/:id/milestones
42 42 post ":id/milestones" do
43   - authorize! :admin_milestone, user_project
44   - required_attributes! [:title]
  43 + set_current_user_for_thread do
  44 + authorize! :admin_milestone, user_project
  45 + required_attributes! [:title]
45 46  
46   - attrs = attributes_for_keys [:title, :description, :due_date]
47   - @milestone = user_project.milestones.new attrs
48   - if @milestone.save
49   - present @milestone, with: Entities::Milestone
50   - else
51   - not_found!
  47 + attrs = attributes_for_keys [:title, :description, :due_date]
  48 + @milestone = user_project.milestones.new attrs
  49 + if @milestone.save
  50 + present @milestone, with: Entities::Milestone
  51 + else
  52 + not_found!
  53 + end
52 54 end
53 55 end
54 56  
... ... @@ -64,14 +66,16 @@ module API
64 66 # Example Request:
65 67 # PUT /projects/:id/milestones/:milestone_id
66 68 put ":id/milestones/:milestone_id" do
67   - authorize! :admin_milestone, user_project
  69 + set_current_user_for_thread do
  70 + authorize! :admin_milestone, user_project
68 71  
69   - @milestone = user_project.milestones.find(params[:milestone_id])
70   - attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
71   - if @milestone.update_attributes attrs
72   - present @milestone, with: Entities::Milestone
73   - else
74   - not_found!
  72 + @milestone = user_project.milestones.find(params[:milestone_id])
  73 + attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
  74 + if @milestone.update_attributes attrs
  75 + present @milestone, with: Entities::Milestone
  76 + else
  77 + not_found!
  78 + end
75 79 end
76 80 end
77 81 end
... ...
lib/api/notes.rb
... ... @@ -41,17 +41,19 @@ module API
41 41 # Example Request:
42 42 # POST /projects/:id/notes
43 43 post ":id/notes" do
44   - required_attributes! [:body]
  44 + set_current_user_for_thread do
  45 + required_attributes! [:body]
45 46  
46   - @note = user_project.notes.new(note: params[:body])
47   - @note.author = current_user
  47 + @note = user_project.notes.new(note: params[:body])
  48 + @note.author = current_user
48 49  
49   - if @note.save
50   - present @note, with: Entities::Note
51   - else
52   - # :note is exposed as :body, but :note is set on error
53   - bad_request!(:note) if @note.errors[:note].any?
54   - not_found!
  50 + if @note.save
  51 + present @note, with: Entities::Note
  52 + else
  53 + # :note is exposed as :body, but :note is set on error
  54 + bad_request!(:note) if @note.errors[:note].any?
  55 + not_found!
  56 + end
55 57 end
56 58 end
57 59  
... ... @@ -97,17 +99,19 @@ module API
97 99 # POST /projects/:id/issues/:noteable_id/notes
98 100 # POST /projects/:id/snippets/:noteable_id/notes
99 101 post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
100   - required_attributes! [:body]
  102 + set_current_user_for_thread do
  103 + required_attributes! [:body]
101 104  
102   - @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
103   - @note = @noteable.notes.new(note: params[:body])
104   - @note.author = current_user
105   - @note.project = user_project
  105 + @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
  106 + @note = @noteable.notes.new(note: params[:body])
  107 + @note.author = current_user
  108 + @note.project = user_project
106 109  
107   - if @note.save
108   - present @note, with: Entities::Note
109   - else
110   - not_found!
  110 + if @note.save
  111 + present @note, with: Entities::Note
  112 + else
  113 + not_found!
  114 + end
111 115 end
112 116 end
113 117 end
... ...
spec/models/project_spec.rb
... ... @@ -27,14 +27,8 @@
27 27 require 'spec_helper'
28 28  
29 29 describe Project do
30   - let(:user) { create(:user) }
31   -
32   - before do
33   - enable_observers
34   - Thread.current[:current_user] = user
35   - end
36   -
37   - after { disable_observers }
  30 + before { enable_observers }
  31 + after { disable_observers }
38 32  
39 33 describe "Associations" do
40 34 it { should belong_to(:group) }
... ...
spec/requests/api/issues_spec.rb
... ... @@ -100,4 +100,16 @@ describe API::API do
100 100 response.status.should == 405
101 101 end
102 102 end
  103 +
  104 + describe "PUT /projects/:id/issues/:issue_id to test observer on close" do
  105 + before { enable_observers }
  106 + after { disable_observers }
  107 +
  108 + it "should create an activity event when an issue is closed" do
  109 + Event.should_receive(:create)
  110 +
  111 + put api("/projects/#{project.id}/issues/#{issue.id}", user),
  112 + state_event: "close"
  113 + end
  114 + end
103 115 end
... ...
spec/requests/api/milestones_spec.rb
... ... @@ -90,4 +90,16 @@ describe API::API do
90 90 json_response['state'].should == 'closed'
91 91 end
92 92 end
  93 +
  94 + describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
  95 + before { enable_observers }
  96 + after { disable_observers }
  97 +
  98 + it "should create an activity event when an milestone is closed" do
  99 + Event.should_receive(:create)
  100 +
  101 + put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
  102 + state_event: 'close'
  103 + end
  104 + end
93 105 end
... ...
spec/requests/api/notes_spec.rb
... ... @@ -176,4 +176,16 @@ describe API::API do
176 176 end
177 177 end
178 178 end
  179 +
  180 + describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
  181 + before { enable_observers }
  182 + after { disable_observers }
  183 +
  184 + it "should create an activity event when an issue note is created" do
  185 + Event.should_receive(:create)
  186 +
  187 + post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
  188 + end
  189 + end
  190 +
179 191 end
... ...
spec/support/test_env.rb
... ... @@ -84,6 +84,11 @@ module TestEnv
84 84 Repository.any_instance.stub(
85 85 size: 12.45
86 86 )
  87 +
  88 + ActivityObserver.any_instance.stub(
  89 + current_user: double("current_user", id: 1)
  90 + )
  91 +
87 92 end
88 93  
89 94 def clear_repo_dir(namespace, name)
... ...