Commit 0cd5edf3 authored by Gabriel Mazetto's avatar Gabriel Mazetto

Backported minimal safewebhook implementation to GitLab CE

parent 40c38644
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class ProjectHook < WebHook class ProjectHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class ServiceHook < WebHook class ServiceHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class SystemHook < WebHook class SystemHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
...@@ -43,23 +44,17 @@ class WebHook < ActiveRecord::Base ...@@ -43,23 +44,17 @@ class WebHook < ActiveRecord::Base
if parsed_url.userinfo.blank? if parsed_url.userinfo.blank?
response = WebHook.post(url, response = WebHook.post(url,
body: data.to_json, body: data.to_json,
headers: { headers: build_headers(hook_name),
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: enable_ssl_verification) verify: enable_ssl_verification)
else else
post_url = url.gsub("#{parsed_url.userinfo}@", "") post_url = url.gsub("#{parsed_url.userinfo}@", '')
auth = { auth = {
username: CGI.unescape(parsed_url.user), username: CGI.unescape(parsed_url.user),
password: CGI.unescape(parsed_url.password), password: CGI.unescape(parsed_url.password),
} }
response = WebHook.post(post_url, response = WebHook.post(post_url,
body: data.to_json, body: data.to_json,
headers: { headers: build_headers(hook_name),
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: enable_ssl_verification, verify: enable_ssl_verification,
basic_auth: auth) basic_auth: auth)
end end
...@@ -73,4 +68,15 @@ class WebHook < ActiveRecord::Base ...@@ -73,4 +68,15 @@ class WebHook < ActiveRecord::Base
def async_execute(data, hook_name) def async_execute(data, hook_name)
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name) Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name)
end end
private
def build_headers(hook_name)
headers = {
'Content-Type' => 'application/json',
'X-Gitlab-Event' => hook_name.singularize.titleize
}
headers['X-Gitlab-Token'] = token if token.present?
headers
end
end end
class AddTokenToWebHooks < ActiveRecord::Migration
def change
add_column :web_hooks, :token, :string
end
end
...@@ -1025,6 +1025,7 @@ ActiveRecord::Schema.define(version: 20160421130527) do ...@@ -1025,6 +1025,7 @@ ActiveRecord::Schema.define(version: 20160421130527) do
t.boolean "enable_ssl_verification", default: true t.boolean "enable_ssl_verification", default: true
t.boolean "build_events", default: false, null: false t.boolean "build_events", default: false, null: false
t.boolean "wiki_page_events", default: false, null: false t.boolean "wiki_page_events", default: false, null: false
t.string "token"
end end
add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
......
FactoryGirl.define do FactoryGirl.define do
factory :project_hook do factory :project_hook do
url { FFaker::Internet.uri('http') } url { FFaker::Internet.uri('http') }
trait :token do
token { SecureRandom.hex(10) }
end
end end
end end
...@@ -43,51 +43,65 @@ describe WebHook, models: true do ...@@ -43,51 +43,65 @@ describe WebHook, models: true do
end end
describe "execute" do describe "execute" do
let(:project) { create(:project) }
let(:project_hook) { create(:project_hook) }
before(:each) do before(:each) do
@project_hook = create(:project_hook) project.hooks << [project_hook]
@project = create(:project)
@project.hooks << [@project_hook]
@data = { before: 'oldrev', after: 'newrev', ref: 'ref' } @data = { before: 'oldrev', after: 'newrev', ref: 'ref' }
WebMock.stub_request(:post, @project_hook.url) WebMock.stub_request(:post, project_hook.url)
end
context 'when token is defined' do
let(:project_hook) { create(:project_hook, :token) }
it 'POSTs to the webhook URL' do
project_hook.execute(@data, 'push_hooks')
expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: { 'Content-Type' => 'application/json',
'X-Gitlab-Event' => 'Push Hook',
'X-Gitlab-Token' => project_hook.token }
).once
end
end end
it "POSTs to the webhook URL" do it "POSTs to the webhook URL" do
@project_hook.execute(@data, 'push_hooks') project_hook.execute(@data, 'push_hooks')
expect(WebMock).to have_requested(:post, @project_hook.url).with( expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' }
).once ).once
end end
it "POSTs the data as JSON" do it "POSTs the data as JSON" do
@project_hook.execute(@data, 'push_hooks') project_hook.execute(@data, 'push_hooks')
expect(WebMock).to have_requested(:post, @project_hook.url).with( expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' }
).once ).once
end end
it "catches exceptions" do it "catches exceptions" do
expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") expect(WebHook).to receive(:post).and_raise("Some HTTP Post error")
expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) expect { project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError)
end end
it "handles SSL exceptions" do it "handles SSL exceptions" do
expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error')) expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error'))
expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) expect(project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error'])
end end
it "handles 200 status code" do it "handles 200 status code" do
WebMock.stub_request(:post, @project_hook.url).to_return(status: 200, body: "Success") WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "Success")
expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
end end
it "handles 2xx status codes" do it "handles 2xx status codes" do
WebMock.stub_request(:post, @project_hook.url).to_return(status: 201, body: "Success") WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: "Success")
expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment