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,7 +2,7 @@ class ApplicationController < ActionController::Base
2 before_filter :authenticate_user! 2 before_filter :authenticate_user!
3 before_filter :reject_blocked! 3 before_filter :reject_blocked!
4 before_filter :check_password_expiration 4 before_filter :check_password_expiration
5 - before_filter :set_current_user_for_thread 5 + around_filter :set_current_user_for_thread
6 before_filter :add_abilities 6 before_filter :add_abilities
7 before_filter :dev_tools if Rails.env == 'development' 7 before_filter :dev_tools if Rails.env == 'development'
8 before_filter :default_headers 8 before_filter :default_headers
@@ -50,6 +50,11 @@ class ApplicationController < ActionController::Base @@ -50,6 +50,11 @@ class ApplicationController < ActionController::Base
50 50
51 def set_current_user_for_thread 51 def set_current_user_for_thread
52 Thread.current[:current_user] = current_user 52 Thread.current[:current_user] = current_user
  53 + begin
  54 + yield
  55 + ensure
  56 + Thread.current[:current_user] = nil
  57 + end
53 end 58 end
54 59
55 def abilities 60 def abilities
db/fixtures/development/09_issues.rb
@@ -11,17 +11,22 @@ Gitlab::Seeder.quiet do @@ -11,17 +11,22 @@ Gitlab::Seeder.quiet do
11 next unless user 11 next unless user
12 12
13 user_id = user.id 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 print('.') 30 print('.')
26 end 31 end
27 32
db/fixtures/development/10_merge_requests.rb
@@ -17,19 +17,23 @@ Gitlab::Seeder.quiet do @@ -17,19 +17,23 @@ Gitlab::Seeder.quiet do
17 next if branches.uniq.size < 2 17 next if branches.uniq.size < 2
18 18
19 user_id = user.id 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 print('.') 37 print('.')
34 end 38 end
35 end 39 end
features/support/env.rb
@@ -51,4 +51,6 @@ Spinach.hooks.before_run do @@ -51,4 +51,6 @@ Spinach.hooks.before_run do
51 RSpec::Mocks::setup self 51 RSpec::Mocks::setup self
52 52
53 include FactoryGirl::Syntax::Methods 53 include FactoryGirl::Syntax::Methods
  54 + MergeRequestObserver.any_instance.stub(current_user: create(:user))
54 end 55 end
  56 +
lib/api/helpers.rb
@@ -31,6 +31,15 @@ module API @@ -31,6 +31,15 @@ module API
31 end 31 end
32 end 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 def user_project 43 def user_project
35 @project ||= find_project(params[:id]) 44 @project ||= find_project(params[:id])
36 @project || not_found! 45 @project || not_found!
lib/api/issues.rb
@@ -2,7 +2,6 @@ module API @@ -2,7 +2,6 @@ module API
2 # Issues API 2 # Issues API
3 class Issues < Grape::API 3 class Issues < Grape::API
4 before { authenticate! } 4 before { authenticate! }
5 - before { Thread.current[:current_user] = current_user }  
6 5
7 resource :issues do 6 resource :issues do
8 # Get currently authenticated user's issues 7 # Get currently authenticated user's issues
@@ -49,15 +48,17 @@ module API @@ -49,15 +48,17 @@ module API
49 # Example Request: 48 # Example Request:
50 # POST /projects/:id/issues 49 # POST /projects/:id/issues
51 post ":id/issues" do 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 end 62 end
62 end 63 end
63 64
@@ -75,16 +76,18 @@ module API @@ -75,16 +76,18 @@ module API
75 # Example Request: 76 # Example Request:
76 # PUT /projects/:id/issues/:issue_id 77 # PUT /projects/:id/issues/:issue_id
77 put ":id/issues/:issue_id" do 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 end 91 end
89 end 92 end
90 93
lib/api/merge_requests.rb
@@ -2,7 +2,6 @@ module API @@ -2,7 +2,6 @@ module API
2 # MergeRequest API 2 # MergeRequest API
3 class MergeRequests < Grape::API 3 class MergeRequests < Grape::API
4 before { authenticate! } 4 before { authenticate! }
5 - before { Thread.current[:current_user] = current_user }  
6 5
7 resource :projects do 6 resource :projects do
8 helpers do 7 helpers do
@@ -70,28 +69,30 @@ module API @@ -70,28 +69,30 @@ module API
70 # POST /projects/:id/merge_requests 69 # POST /projects/:id/merge_requests
71 # 70 #
72 post ":id/merge_requests" do 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 else 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 end 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 end 96 end
96 end 97 end
97 98
@@ -109,17 +110,19 @@ module API @@ -109,17 +110,19 @@ module API
109 # PUT /projects/:id/merge_request/:merge_request_id 110 # PUT /projects/:id/merge_request/:merge_request_id
110 # 111 #
111 put ":id/merge_request/:merge_request_id" do 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 end 126 end
124 end 127 end
125 128
@@ -133,16 +136,18 @@ module API @@ -133,16 +136,18 @@ module API
133 # POST /projects/:id/merge_request/:merge_request_id/comments 136 # POST /projects/:id/merge_request/:merge_request_id/comments
134 # 137 #
135 post ":id/merge_request/:merge_request_id/comments" do 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 end 151 end
147 end 152 end
148 153
lib/api/milestones.rb
@@ -40,15 +40,17 @@ module API @@ -40,15 +40,17 @@ module API
40 # Example Request: 40 # Example Request:
41 # POST /projects/:id/milestones 41 # POST /projects/:id/milestones
42 post ":id/milestones" do 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 end 54 end
53 end 55 end
54 56
@@ -64,14 +66,16 @@ module API @@ -64,14 +66,16 @@ module API
64 # Example Request: 66 # Example Request:
65 # PUT /projects/:id/milestones/:milestone_id 67 # PUT /projects/:id/milestones/:milestone_id
66 put ":id/milestones/:milestone_id" do 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 end 79 end
76 end 80 end
77 end 81 end
lib/api/notes.rb
@@ -41,17 +41,19 @@ module API @@ -41,17 +41,19 @@ module API
41 # Example Request: 41 # Example Request:
42 # POST /projects/:id/notes 42 # POST /projects/:id/notes
43 post ":id/notes" do 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 end 57 end
56 end 58 end
57 59
@@ -97,17 +99,19 @@ module API @@ -97,17 +99,19 @@ module API
97 # POST /projects/:id/issues/:noteable_id/notes 99 # POST /projects/:id/issues/:noteable_id/notes
98 # POST /projects/:id/snippets/:noteable_id/notes 100 # POST /projects/:id/snippets/:noteable_id/notes
99 post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do 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 end 115 end
112 end 116 end
113 end 117 end
spec/models/project_spec.rb
@@ -27,14 +27,8 @@ @@ -27,14 +27,8 @@
27 require 'spec_helper' 27 require 'spec_helper'
28 28
29 describe Project do 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 describe "Associations" do 33 describe "Associations" do
40 it { should belong_to(:group) } 34 it { should belong_to(:group) }
spec/requests/api/issues_spec.rb
@@ -100,4 +100,16 @@ describe API::API do @@ -100,4 +100,16 @@ describe API::API do
100 response.status.should == 405 100 response.status.should == 405
101 end 101 end
102 end 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 end 115 end
spec/requests/api/milestones_spec.rb
@@ -90,4 +90,16 @@ describe API::API do @@ -90,4 +90,16 @@ describe API::API do
90 json_response['state'].should == 'closed' 90 json_response['state'].should == 'closed'
91 end 91 end
92 end 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 end 105 end
spec/requests/api/notes_spec.rb
@@ -176,4 +176,16 @@ describe API::API do @@ -176,4 +176,16 @@ describe API::API do
176 end 176 end
177 end 177 end
178 end 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 end 191 end
spec/support/test_env.rb
@@ -84,6 +84,11 @@ module TestEnv @@ -84,6 +84,11 @@ module TestEnv
84 Repository.any_instance.stub( 84 Repository.any_instance.stub(
85 size: 12.45 85 size: 12.45
86 ) 86 )
  87 +
  88 + ActivityObserver.any_instance.stub(
  89 + current_user: double("current_user", id: 1)
  90 + )
  91 +
87 end 92 end
88 93
89 def clear_repo_dir(namespace, name) 94 def clear_repo_dir(namespace, name)