Commit e997b22d authored by Douwe Maan's avatar Douwe Maan

Merge branch '51335-fail-early-when-user-cannot-be-identified' into 'master'

User not defined in PostReceive#process_project_changes

Closes #51335

See merge request gitlab-org/gitlab-ce!22519
parents f2e9148d 679d9b21
...@@ -29,15 +29,14 @@ class PostReceive ...@@ -29,15 +29,14 @@ class PostReceive
def process_project_changes(post_received) def process_project_changes(post_received)
changes = [] changes = []
refs = Set.new refs = Set.new
@user = post_received.identify
post_received.changes_refs do |oldrev, newrev, ref| unless @user
@user ||= post_received.identify(newrev) log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
return false
unless @user end
log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
return false # rubocop:disable Cop/AvoidReturnFromBlocks
end
post_received.changes_refs do |oldrev, newrev, ref|
if Gitlab::Git.tag_ref?(ref) if Gitlab::Git.tag_ref?(ref)
GitTagPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute GitTagPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute
elsif Gitlab::Git.branch_ref?(ref) elsif Gitlab::Git.branch_ref?(ref)
......
---
title: If user was not found, service hooks won't run on post receive background job
merge_request: 22519
author:
type: fixed
...@@ -11,8 +11,8 @@ module Gitlab ...@@ -11,8 +11,8 @@ module Gitlab
@changes = deserialize_changes(changes) @changes = deserialize_changes(changes)
end end
def identify(revision) def identify
super(identifier, project, revision) super(identifier)
end end
def changes_refs def changes_refs
......
# frozen_string_literal: true # frozen_string_literal: true
# Detect user based on identifier like # Detect user based on identifier like
# key-13 or user-36 or last commit # key-13 or user-36
module Gitlab module Gitlab
module Identifier module Identifier
def identify(identifier, project = nil, newrev = nil) def identify(identifier)
if identifier.blank? if identifier =~ /\Auser-\d+\Z/
identify_using_commit(project, newrev)
elsif identifier =~ /\Auser-\d+\Z/
# git push over http # git push over http
identify_using_user(identifier) identify_using_user(identifier)
elsif identifier =~ /\Akey-\d+\Z/ elsif identifier =~ /\Akey-\d+\Z/
...@@ -16,19 +14,6 @@ module Gitlab ...@@ -16,19 +14,6 @@ module Gitlab
end end
end end
# Tries to identify a user based on a commit SHA.
def identify_using_commit(project, ref)
return if project.nil? && ref.nil?
commit = project.commit(ref)
return if !commit || !commit.author_email
identify_with_cache(:email, commit.author_email) do
commit.author
end
end
# Tries to identify a user based on a user identifier (e.g. "user-123"). # Tries to identify a user based on a user identifier (e.g. "user-123").
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def identify_using_user(identifier) def identify_using_user(identifier)
......
...@@ -11,11 +11,8 @@ describe Gitlab::Identifier do ...@@ -11,11 +11,8 @@ describe Gitlab::Identifier do
describe '#identify' do describe '#identify' do
context 'without an identifier' do context 'without an identifier' do
it 'identifies the user using a commit' do it 'returns nil' do
expect(identifier).to receive(:identify_using_commit) expect(identifier.identify('')).to be nil
.with(project, '123')
identifier.identify('', project, '123')
end end
end end
...@@ -24,7 +21,7 @@ describe Gitlab::Identifier do ...@@ -24,7 +21,7 @@ describe Gitlab::Identifier do
expect(identifier).to receive(:identify_using_user) expect(identifier).to receive(:identify_using_user)
.with("user-#{user.id}") .with("user-#{user.id}")
identifier.identify("user-#{user.id}", project, '123') identifier.identify("user-#{user.id}")
end end
end end
...@@ -33,49 +30,11 @@ describe Gitlab::Identifier do ...@@ -33,49 +30,11 @@ describe Gitlab::Identifier do
expect(identifier).to receive(:identify_using_ssh_key) expect(identifier).to receive(:identify_using_ssh_key)
.with("key-#{key.id}") .with("key-#{key.id}")
identifier.identify("key-#{key.id}", project, '123') identifier.identify("key-#{key.id}")
end end
end end
end end
describe '#identify_using_commit' do
it "returns the User for an existing commit author's Email address" do
commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').and_return(commit)
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
it 'returns nil when no user could be found' do
allow(project).to receive(:commit).with('123').and_return(nil)
expect(identifier.identify_using_commit(project, '123')).to be_nil
end
it 'returns nil when the commit does not have an author Email' do
commit = double(:commit, author_email: nil)
expect(project).to receive(:commit).with('123').and_return(commit)
expect(identifier.identify_using_commit(project, '123')).to be_nil
end
it 'caches the found users per Email' do
commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').twice.and_return(commit)
2.times do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
end
it 'returns nil if the project & ref are not present' do
expect(identifier.identify_using_commit(nil, nil)).to be_nil
end
end
describe '#identify_using_user' do describe '#identify_using_user' do
it 'returns the User for an existing ID in the identifier' do it 'returns the User for an existing ID in the identifier' do
found = identifier.identify_using_user("user-#{user.id}") found = identifier.identify_using_user("user-#{user.id}")
......
...@@ -6,7 +6,7 @@ describe PostReceive do ...@@ -6,7 +6,7 @@ describe PostReceive do
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) } let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
let(:gl_repository) { "project-#{project.id}" } let(:gl_repository) { "project-#{project.id}" }
let(:key) { create(:key, user: project.owner) } let(:key) { create(:key, user: project.owner) }
let(:key_id) { key.shell_id } let!(:key_id) { key.shell_id }
let(:project) do let(:project) do
create(:project, :repository, auto_cancel_pending_pipelines: 'disabled') create(:project, :repository, auto_cancel_pending_pipelines: 'disabled')
...@@ -31,85 +31,108 @@ describe PostReceive do ...@@ -31,85 +31,108 @@ describe PostReceive do
end end
describe "#process_project_changes" do describe "#process_project_changes" do
before do context 'empty changes' do
allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner) it "does not call any PushService but runs after project hooks" do
expect(GitPushService).not_to receive(:new)
expect(GitTagPushService).not_to receive(:new)
expect_next_instance_of(SystemHooksService) { |service| expect(service).to receive(:execute_hooks) }
described_class.new.perform(gl_repository, key_id, "")
end
end end
context "branches" do context 'unidentified user' do
let(:changes) { "123456 789012 refs/heads/tést" } let!(:key_id) { "" }
it "calls GitTagPushService" do it 'returns false' do
expect_any_instance_of(GitPushService).to receive(:execute).and_return(true) expect(GitPushService).not_to receive(:new)
expect_any_instance_of(GitTagPushService).not_to receive(:execute) expect(GitTagPushService).not_to receive(:new)
described_class.new.perform(gl_repository, key_id, base64_changes)
expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be false
end end
end end
context "tags" do context 'with changes' do
let(:changes) { "123456 789012 refs/tags/tag" } before do
allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
end
context "branches" do
let(:changes) { "123456 789012 refs/heads/tést" }
it "calls GitTagPushService" do it "calls GitPushService" do
expect_any_instance_of(GitPushService).not_to receive(:execute) expect_any_instance_of(GitPushService).to receive(:execute).and_return(true)
expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true) expect_any_instance_of(GitTagPushService).not_to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
end
end end
end
context "merge-requests" do context "tags" do
let(:changes) { "123456 789012 refs/merge-requests/123" } let(:changes) { "123456 789012 refs/tags/tag" }
it "does not call any of the services" do it "calls GitTagPushService" do
expect_any_instance_of(GitPushService).not_to receive(:execute) expect_any_instance_of(GitPushService).not_to receive(:execute)
expect_any_instance_of(GitTagPushService).not_to receive(:execute) expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
end
end end
end
context "gitlab-ci.yml" do context "merge-requests" do
let(:changes) { "123456 789012 refs/heads/feature\n654321 210987 refs/tags/tag" } let(:changes) { "123456 789012 refs/merge-requests/123" }
subject { described_class.new.perform(gl_repository, key_id, base64_changes) } it "does not call any of the services" do
expect_any_instance_of(GitPushService).not_to receive(:execute)
expect_any_instance_of(GitTagPushService).not_to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
end
context "creates a Ci::Pipeline for every change" do context "gitlab-ci.yml" do
before do let(:changes) { "123456 789012 refs/heads/feature\n654321 210987 refs/tags/tag" }
stub_ci_pipeline_to_return_yaml_file
allow_any_instance_of(Project) subject { described_class.new.perform(gl_repository, key_id, base64_changes) }
.to receive(:commit)
.and_return(project.commit)
allow_any_instance_of(Repository) context "creates a Ci::Pipeline for every change" do
.to receive(:branch_exists?) before do
.and_return(true) stub_ci_pipeline_to_return_yaml_file
end
it { expect { subject }.to change { Ci::Pipeline.count }.by(2) } allow_any_instance_of(Project)
end .to receive(:commit)
.and_return(project.commit)
context "does not create a Ci::Pipeline" do allow_any_instance_of(Repository)
before do .to receive(:branch_exists?)
stub_ci_pipeline_yaml_file(nil) .and_return(true)
end
it { expect { subject }.to change { Ci::Pipeline.count }.by(2) }
end end
it { expect { subject }.not_to change { Ci::Pipeline.count } } context "does not create a Ci::Pipeline" do
before do
stub_ci_pipeline_yaml_file(nil)
end
it { expect { subject }.not_to change { Ci::Pipeline.count } }
end
end end
end
context 'after project changes hooks' do context 'after project changes hooks' do
let(:changes) { '123456 789012 refs/heads/tést' } let(:changes) { '123456 789012 refs/heads/tést' }
let(:fake_hook_data) { Hash.new(event_name: 'repository_update') } let(:fake_hook_data) { Hash.new(event_name: 'repository_update') }
before do before do
allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data) allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
# silence hooks so we can isolate # silence hooks so we can isolate
allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true) allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true)
allow_any_instance_of(GitPushService).to receive(:execute).and_return(true) allow_any_instance_of(GitPushService).to receive(:execute).and_return(true)
end end
it 'calls SystemHooksService' do it 'calls SystemHooksService' do
expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true) expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
end
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