diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2017-07-27 15:29:39 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2017-07-27 15:29:39 +0000 |
commit | ac0cbe69706a2d3013d37adda7514824a2d06ed4 (patch) | |
tree | 7730163984394f4e79711c609a2d1da2bb4f807e /spec | |
parent | 9981814514742a2ee507d4dcc2fd71099fd96585 (diff) | |
parent | 5ebccab1eb74f7bf9f7f9d4f2d9a56fb81754cbe (diff) | |
download | gitlab-ce-ac0cbe69706a2d3013d37adda7514824a2d06ed4.tar.gz |
Merge branch 'feature/gpg-signed-commits' into 'master'
GPG signed commits
Closes #20268
See merge request !9546
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/gpg_keys.rb | 8 | ||||
-rw-r--r-- | spec/factories/gpg_signature.rb | 11 | ||||
-rw-r--r-- | spec/features/commits_spec.rb | 101 | ||||
-rw-r--r-- | spec/features/profiles/gpg_keys_spec.rb | 58 | ||||
-rw-r--r-- | spec/lib/gitlab/gpg/commit_spec.rb | 127 | ||||
-rw-r--r-- | spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb | 173 | ||||
-rw-r--r-- | spec/lib/gitlab/gpg_spec.rb | 83 | ||||
-rw-r--r-- | spec/mailers/emails/profile_spec.rb | 30 | ||||
-rw-r--r-- | spec/models/gpg_key_spec.rb | 157 | ||||
-rw-r--r-- | spec/models/gpg_signature_spec.rb | 28 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 20 | ||||
-rw-r--r-- | spec/services/git_push_service_spec.rb | 18 | ||||
-rw-r--r-- | spec/services/notification_service_spec.rb | 12 | ||||
-rw-r--r-- | spec/support/gpg_helpers.rb | 202 | ||||
-rw-r--r-- | spec/support/test_env.rb | 1 | ||||
-rw-r--r-- | spec/workers/create_gpg_signature_worker_spec.rb | 47 | ||||
-rw-r--r-- | spec/workers/invalid_gpg_signature_update_worker_spec.rb | 29 |
17 files changed, 1105 insertions, 0 deletions
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb new file mode 100644 index 00000000000..1258dce8940 --- /dev/null +++ b/spec/factories/gpg_keys.rb @@ -0,0 +1,8 @@ +require_relative '../support/gpg_helpers' + +FactoryGirl.define do + factory :gpg_key do + key GpgHelpers::User1.public_key + user + end +end diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb new file mode 100644 index 00000000000..a5aeffbe12d --- /dev/null +++ b/spec/factories/gpg_signature.rb @@ -0,0 +1,11 @@ +require_relative '../support/gpg_helpers' + +FactoryGirl.define do + factory :gpg_signature do + commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) } + project + gpg_key + gpg_key_primary_keyid { gpg_key.primary_keyid } + valid_signature true + end +end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 479fb713297..15ec6f20763 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -203,4 +203,105 @@ describe 'Commits' do end end end + + describe 'GPG signed commits', :js do + it 'changes from unverified to verified when the user changes his email to match the gpg key' do + user = create :user, email: 'unrelated.user@example.org' + project.team << [user, :master] + + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + sign_in(user) + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).not_to have_content 'Verified' + end + + # user changes his email which makes the gpg key verified + Sidekiq::Testing.inline! do + user.skip_reconfirmation! + user.update_attributes!(email: GpgHelpers::User1.emails.first) + end + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).to have_content 'Verified' + end + end + + it 'changes from unverified to verified when the user adds the missing gpg key' do + user = create :user, email: GpgHelpers::User1.emails.first + project.team << [user, :master] + + sign_in(user) + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).not_to have_content 'Verified' + end + + # user adds the gpg key which makes the signature valid + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).to have_content 'Verified' + end + end + + it 'shows popover badges' do + gpg_user = create :user, email: GpgHelpers::User1.emails.first, username: 'nannie.bernhard', name: 'Nannie Bernhard' + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: gpg_user + end + + user = create :user + project.team << [user, :master] + + sign_in(user) + visit project_commits_path(project, :'signed-commits') + + # unverified signature + click_on 'Unverified', match: :first + within '.popover' do + expect(page).to have_content 'This commit was signed with an unverified signature.' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}" + end + + # verified and the gpg user has a gitlab profile + click_on 'Verified' + within '.popover' do + expect(page).to have_content 'This commit was signed with a verified signature.' + expect(page).to have_content 'Nannie Bernhard' + expect(page).to have_content '@nannie.bernhard' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}" + end + + # verified and the gpg user's profile doesn't exist anymore + gpg_user.destroy! + + visit project_commits_path(project, :'signed-commits') + + click_on 'Verified' + within '.popover' do + expect(page).to have_content 'This commit was signed with a verified signature.' + expect(page).to have_content 'Nannie Bernhard' + expect(page).to have_content 'nannie.bernhard@example.com' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}" + end + end + end end diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb new file mode 100644 index 00000000000..6edc482b47e --- /dev/null +++ b/spec/features/profiles/gpg_keys_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +feature 'Profile > GPG Keys' do + let(:user) { create(:user, email: GpgHelpers::User2.emails.first) } + + before do + login_as(user) + end + + describe 'User adds a key' do + before do + visit profile_gpg_keys_path + end + + scenario 'saves the new key' do + fill_in('Key', with: GpgHelpers::User2.public_key) + click_button('Add key') + + expect(page).to have_content('bette.cartwright@example.com Verified') + expect(page).to have_content('bette.cartwright@example.net Unverified') + expect(page).to have_content(GpgHelpers::User2.fingerprint) + end + end + + scenario 'User sees their key' do + create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) + visit profile_gpg_keys_path + + expect(page).to have_content('bette.cartwright@example.com Verified') + expect(page).to have_content('bette.cartwright@example.net Unverified') + expect(page).to have_content(GpgHelpers::User2.fingerprint) + end + + scenario 'User removes a key via the key index' do + create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) + visit profile_gpg_keys_path + + click_link('Remove') + + expect(page).to have_content('Your GPG keys (0)') + end + + scenario 'User revokes a key via the key index' do + gpg_key = create :gpg_key, user: user, key: GpgHelpers::User2.public_key + gpg_signature = create :gpg_signature, gpg_key: gpg_key, valid_signature: true + + visit profile_gpg_keys_path + + click_link('Revoke') + + expect(page).to have_content('Your GPG keys (0)') + + expect(gpg_signature.reload).to have_attributes( + valid_signature: false, + gpg_key: nil + ) + end +end diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb new file mode 100644 index 00000000000..ddb8dd9f0f4 --- /dev/null +++ b/spec/lib/gitlab/gpg/commit_spec.rb @@ -0,0 +1,127 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Gpg::Commit do + describe '#signature' do + let!(:project) { create :project, :repository, path: 'sample-project' } + let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' } + + context 'unisgned commit' do + it 'returns nil' do + expect(described_class.new(project.commit).signature).to be_nil + end + end + + context 'known and verified public key' do + let!(:gpg_key) do + create :gpg_key, key: GpgHelpers::User1.public_key, user: create(:user, email: GpgHelpers::User1.emails.first) + end + + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, git_commit: raw_commit, project: project + end + + it 'returns a valid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + valid_signature: true + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + + context 'known but unverified public key' do + let!(:gpg_key) { create :gpg_key, key: GpgHelpers::User1.public_key } + + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, git_commit: raw_commit, project: project + end + + it 'returns an invalid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + valid_signature: false + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + + context 'unknown public key' do + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, + git_commit: raw_commit, + project: project + end + + it 'returns an invalid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: nil, + gpg_key_user_email: nil, + valid_signature: false + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + end +end diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb new file mode 100644 index 00000000000..c4e04ee46a2 --- /dev/null +++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb @@ -0,0 +1,173 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do + describe '#run' do + let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' } + let!(:project) { create :project, :repository, path: 'sample-project' } + let!(:raw_commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + + allow(raw_commit).to receive :save! + + raw_commit + end + + let!(:commit) do + create :commit, git_commit: raw_commit, project: project + end + + before do + allow_any_instance_of(Project).to receive(:commit).and_return(commit) + end + + context 'gpg signature did have an associated gpg key which was removed later' do + let!(:user) { create :user, email: GpgHelpers::User1.emails.first } + + let!(:valid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + end + + it 'assigns the gpg key to the signature when the missing gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(valid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'does not assign the gpg key when an unrelated gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + create :gpg_key, + key: GpgHelpers::User2.public_key, + user: user + + expect(valid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + end + + context 'gpg signature did not have an associated gpg key' do + let!(:user) { create :user, email: GpgHelpers::User1.emails.first } + + let!(:invalid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + end + + it 'updates the signature to being valid when the missing gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'keeps the signature at being invalid when an unrelated gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + create :gpg_key, + key: GpgHelpers::User2.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + end + end + + context 'gpg signature did have an associated unverified gpg key' do + let!(:user) do + create(:user, email: 'unrelated@example.com').tap do |user| + user.skip_reconfirmation! + end + end + + let!(:invalid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + end + + it 'updates the signature to being valid when the user updates the email address' do + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload.valid_signature).to be_falsey + + # InvalidGpgSignatureUpdater is called by the after_update hook + user.update_attributes!(email: GpgHelpers::User1.emails.first) + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'keeps the signature at being invalid when the changed email address is still unrelated' do + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + + # InvalidGpgSignatureUpdater is called by the after_update hook + user.update_attributes!(email: 'still.unrelated@example.com') + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + end + end + end +end diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb new file mode 100644 index 00000000000..8041518117d --- /dev/null +++ b/spec/lib/gitlab/gpg_spec.rb @@ -0,0 +1,83 @@ +require 'rails_helper' + +describe Gitlab::Gpg do + describe '.fingerprints_from_key' do + before do + # make sure that each method is using the temporary keychain + expect(described_class).to receive(:using_tmp_keychain).and_call_original + end + + it 'returns CurrentKeyChain.fingerprints_from_key' do + expect(Gitlab::Gpg::CurrentKeyChain).to receive(:fingerprints_from_key).with(GpgHelpers::User1.public_key) + + described_class.fingerprints_from_key(GpgHelpers::User1.public_key) + end + end + + describe '.primary_keyids_from_key' do + it 'returns the keyid' do + expect( + described_class.primary_keyids_from_key(GpgHelpers::User1.public_key) + ).to eq [GpgHelpers::User1.primary_keyid] + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.primary_keyids_from_key('bogus') + ).to eq [] + end + end + + describe '.user_infos_from_key' do + it 'returns the names and emails' do + user_infos = described_class.user_infos_from_key(GpgHelpers::User1.public_key) + expect(user_infos).to eq([{ + name: GpgHelpers::User1.names.first, + email: GpgHelpers::User1.emails.first + }]) + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.user_infos_from_key('bogus') + ).to eq [] + end + end +end + +describe Gitlab::Gpg::CurrentKeyChain do + around do |example| + Gitlab::Gpg.using_tmp_keychain do + example.run + end + end + + describe '.add' do + it 'stores the key in the keychain' do + expect(GPGME::Key.find(:public, GpgHelpers::User1.fingerprint)).to eq [] + + described_class.add(GpgHelpers::User1.public_key) + + keys = GPGME::Key.find(:public, GpgHelpers::User1.fingerprint) + expect(keys.count).to eq 1 + expect(keys.first).to have_attributes( + email: GpgHelpers::User1.emails.first, + fingerprint: GpgHelpers::User1.fingerprint + ) + end + end + + describe '.fingerprints_from_key' do + it 'returns the fingerprint' do + expect( + described_class.fingerprints_from_key(GpgHelpers::User1.public_key) + ).to eq [GpgHelpers::User1.fingerprint] + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.fingerprints_from_key('bogus') + ).to eq [] + end + end +end diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 8c1c9bf135f..09e5094cf84 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -91,6 +91,36 @@ describe Emails::Profile do end end + describe 'user added gpg key' do + let(:gpg_key) { create(:gpg_key) } + + subject { Notify.new_gpg_key_email(gpg_key.id) } + + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' + + it 'is sent to the new user' do + is_expected.to deliver_to gpg_key.user.email + end + + it 'has the correct subject' do + is_expected.to have_subject /^GPG key was added to your account$/i + end + + it 'contains the new gpg key title' do + is_expected.to have_body_text /#{gpg_key.fingerprint}/ + end + + it 'includes a link to gpg keys page' do + is_expected.to have_body_text /#{profile_gpg_keys_path}/ + end + + context 'with GPG key that does not exist' do + it { expect { Notify.new_gpg_key_email('foo') }.not_to raise_error } + end + end + describe 'user added email' do let(:email) { create(:email) } diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb new file mode 100644 index 00000000000..59c074199db --- /dev/null +++ b/spec/models/gpg_key_spec.rb @@ -0,0 +1,157 @@ +require 'rails_helper' + +describe GpgKey do + describe "associations" do + it { is_expected.to belong_to(:user) } + end + + describe "validation" do + it { is_expected.to validate_presence_of(:user) } + + it { is_expected.to validate_presence_of(:key) } + it { is_expected.to validate_uniqueness_of(:key) } + + it { is_expected.to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey\n-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey\n-----BEGIN PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK----------END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("key\n-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value('BEGIN PGP').for(:key) } + end + + context 'callbacks' do + describe 'extract_fingerprint' do + it 'extracts the fingerprint from the gpg key' do + gpg_key = described_class.new(key: GpgHelpers::User1.public_key) + gpg_key.valid? + expect(gpg_key.fingerprint).to eq GpgHelpers::User1.fingerprint + end + end + + describe 'extract_primary_keyid' do + it 'extracts the primary keyid from the gpg key' do + gpg_key = described_class.new(key: GpgHelpers::User1.public_key) + gpg_key.valid? + expect(gpg_key.primary_keyid).to eq GpgHelpers::User1.primary_keyid + end + end + end + + describe '#key=' do + it 'strips white spaces' do + key = <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mQENBFMOSOgBCADFCYxmnXFbrDhfvlf03Q/bQuT+nZu46BFGbo7XkUjDowFXJQhP + -----END PGP PUBLIC KEY BLOCK----- + KEY + + expect(described_class.new(key: " #{key} ").key).to eq(key) + end + + it 'does not strip when the key is nil' do + expect(described_class.new(key: nil).key).to be_nil + end + end + + describe '#user_infos' do + it 'returns the user infos from the gpg key' do + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key + expect(Gitlab::Gpg).to receive(:user_infos_from_key).with(gpg_key.key) + + gpg_key.user_infos + end + end + + describe '#verified_user_infos' do + it 'returns the user infos if it is verified' do + user = create :user, email: GpgHelpers::User1.emails.first + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key, user: user + + expect(gpg_key.verified_user_infos).to eq([{ + name: GpgHelpers::User1.names.first, + email: GpgHelpers::User1.emails.first + }]) + end + + it 'returns an empty array if the user info is not verified' do + user = create :user, email: 'unrelated@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key, user: user + + expect(gpg_key.verified_user_infos).to eq([]) + end + end + + describe '#emails_with_verified_status' do + it 'email is verified if the user has the matching email' do + user = create :user, email: 'bette.cartwright@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.emails_with_verified_status).to eq( + 'bette.cartwright@example.com' => true, + 'bette.cartwright@example.net' => false + ) + end + end + + describe '#verified?' do + it 'returns true one of the email addresses in the key belongs to the user' do + user = create :user, email: 'bette.cartwright@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.verified?).to be_truthy + end + + it 'returns false if one of the email addresses in the key does not belong to the user' do + user = create :user, email: 'someone.else@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.verified?).to be_falsey + end + end + + describe 'notification' do + include EmailHelpers + + let(:user) { create(:user) } + + it 'sends a notification' do + perform_enqueued_jobs do + create(:gpg_key, user: user) + end + + should_email(user) + end + end + + describe '#revoke' do + it 'invalidates all associated gpg signatures and destroys the key' do + gpg_key = create :gpg_key + gpg_signature = create :gpg_signature, valid_signature: true, gpg_key: gpg_key + + unrelated_gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key + unrelated_gpg_signature = create :gpg_signature, valid_signature: true, gpg_key: unrelated_gpg_key + + gpg_key.revoke + + expect(gpg_signature.reload).to have_attributes( + valid_signature: false, + gpg_key: nil + ) + + expect(gpg_key.destroyed?).to be true + + # unrelated signature is left untouched + expect(unrelated_gpg_signature.reload).to have_attributes( + valid_signature: true, + gpg_key: unrelated_gpg_key + ) + + expect(unrelated_gpg_key.destroyed?).to be false + end + end +end diff --git a/spec/models/gpg_signature_spec.rb b/spec/models/gpg_signature_spec.rb new file mode 100644 index 00000000000..9a9b1900aa5 --- /dev/null +++ b/spec/models/gpg_signature_spec.rb @@ -0,0 +1,28 @@ +require 'rails_helper' + +RSpec.describe GpgSignature do + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:gpg_key) } + end + + describe 'validation' do + subject { described_class.new } + it { is_expected.to validate_presence_of(:commit_sha) } + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:gpg_key_primary_keyid) } + end + + describe '#commit' do + it 'fetches the commit through the project' do + commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' + project = create :project + commit = create :commit, project: project + gpg_signature = create :gpg_signature, commit_sha: commit_sha + + expect_any_instance_of(Project).to receive(:commit).with(commit_sha).and_return(commit) + + gpg_signature.commit + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 71aadbb4186..ec98a3f3498 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -350,6 +350,26 @@ describe User do end end + describe 'after update hook' do + describe '.update_invalid_gpg_signatures' do + let(:user) do + create(:user, email: 'tula.torphy@abshire.ca').tap do |user| + user.skip_reconfirmation! + end + end + + it 'does nothing when the name is updated' do + expect(user).not_to receive(:update_invalid_gpg_signatures) + user.update_attributes!(name: 'Bette') + end + + it 'synchronizes the gpg keys when the email is updated' do + expect(user).to receive(:update_invalid_gpg_signatures) + user.update_attributes!(email: 'shawnee.ritchie@denesik.com') + end + end + end + describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") } let(:user) { create(:user) } diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 8e6e5864c9a..75329e9dda2 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -681,6 +681,24 @@ describe GitPushService do end end + describe '#update_signatures' do + let(:service) do + described_class.new( + project, + user, + oldrev: sample_commit.parent_id, + newrev: sample_commit.id, + ref: 'refs/heads/master' + ) + end + + it 'calls CreateGpgSignatureWorker.perform_async for each commit' do + expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id) + + execute_service(project, user, @oldrev, @newrev, @ref) + end + end + def execute_service(project, user, oldrev, newrev, ref) service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref ) service.execute diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index c98eb87b94e..49d6fc7853f 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -93,6 +93,18 @@ describe NotificationService do end end + describe 'GpgKeys' do + describe '#new_gpg_key' do + let!(:key) { create(:gpg_key) } + + it { expect(notification.new_gpg_key(key)).to be_truthy } + + it 'sends email to key owner' do + expect{ notification.new_gpg_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + end + end + end + describe 'Email' do describe '#new_email' do let!(:email) { create(:email) } diff --git a/spec/support/gpg_helpers.rb b/spec/support/gpg_helpers.rb new file mode 100644 index 00000000000..96ea6f28b30 --- /dev/null +++ b/spec/support/gpg_helpers.rb @@ -0,0 +1,202 @@ +module GpgHelpers + module User1 + extend self + + def signed_commit_signature + <<~SIGNATURE + -----BEGIN PGP SIGNATURE----- + Version: GnuPG v1 + + iJwEAAECAAYFAliu264ACgkQzPvhnwCsix1VXgP9F6zwAMb3OXKZzqGxJ4MQIBoL + OdiUSJpL/4sIA9uhFeIv3GIA+uhsG1BHHsG627+sDy7b8W9VWEd7tbcoz4Mvhf3P + 8g0AIt9/KJuStQZDrXwP1uP6Rrl759nDcNpoOKdSQ5EZ1zlRzeDROlZeDp7Ckfvw + GLmN/74Gl3pk0wfgHFY= + =wSgS + -----END PGP SIGNATURE----- + SIGNATURE + end + + def signed_commit_base_data + <<~SIGNEDDATA + tree ed60cfd202644fda1abaf684e7d965052db18c13 + parent caf6a0334a855e12f30205fff3d7333df1f65127 + author Nannie Bernhard <nannie.bernhard@example.com> 1487854510 +0100 + committer Nannie Bernhard <nannie.bernhard@example.com> 1487854510 +0100 + + signed commit, verified key/email + SIGNEDDATA + end + + def secret_key + <<~KEY.strip + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: GnuPG v1 + + lQHYBFiu1ScBBADUhWsrlWHp5e7ASlI5iMcA0XN43fivhVlGYJJy4Ii3Hr2i4f5s + VffHS8QyhgxxzSnPwe2OKnZWWL9cHzUFbiG3fHalEBTjpB+7pG4HBgU8R/tiDOu8 + vkAR+tfJbkuRs9XeG3dGKBX/8WRhIfRucYnM+04l2Myyo5zIx7thJmxXjwARAQAB + AAP/XUtcqrtfSnDYCK4Xvo4e3msUSAEZxOPDNzP51lhfbBQgp7qSGDj9Fw5ZyNwz + 5llse3nksT5OyMUY7HX+rq2UOs12a/piLqvhtX1okp/oTAETmKXNYkZLenv6t94P + NqLi0o2AnXAvL9ueXa7WUY3l4DkvuLcjT4+9Ut2Y71zIjeECAN7q9ohNL7E8tNkf + Elsbx+8KfyHRQXiSUYaQLlvDRq2lYCKIS7sogTqjZMEgbZx2mRX1fakRlvcmqOwB + QoX34zcCAPQPd+yTteNUV12uvDaj8V9DICktPPhbHdYYaUoHjF8RrIHCTRUPzk9E + KzCL9dUP8eXPPBV/ty+zjUwl69IgCmkB/3pnNZ0D4EJsNgu24UgI0N+c8H/PE1D6 + K+bGQ/jK83uYPMXJUsiojssCHLGNp7eBGHFn1PpEqZphgVI50ZMrZQWhJbQtTmFu + bmllIEJlcm5oYXJkIDxuYW5uaWUuYmVybmhhcmRAZXhhbXBsZS5jb20+iLgEEwEC + ACIFAliu1ScCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEMz74Z8ArIsd + p5ID/32hRalvTY+V+QAtzHlGdxugweSBzNgRT3A4UiC9chF6zBOEIw689lqmK6L4 + i3Il9XeKMl87wi9tsVy9TuOMYDTvcFvu1vMAQ5AsDXqZaAEtCUZpFZscNbi7AXG+ + QkoDQbMSxp0Rd6eIRJpk9zis5co87f78xJBZLZua+8awFMS6nQHYBFiu1ScBBADI + XkITf+kKCkD+n8tMsdTLInefu8KrJ8p7YRYCCabEXnWRsDb5zxUAG2VXCVUhYl6Q + XQybkNiBaduS+uxilz7gtYZUMFJvQ09+fV7D2N9B7u/1bGdIYz+cDFJnEJitLY4w + /nju2Sno5CL5Ead8sZuslKetSXPYHR/kbW462EOw5wARAQABAAP+IoZfU1XUdVbr + +RPWp3ny5SekviDPu8co9BZ4ANTh5+8wyfA3oNbGUxTlYthoU07MZYqq+/k63R28 + 6HgVGC3gdvCiRMGmryIQ6roLLRXkfzjXrI7Lgnhx4OtVjo62pAKDqdl45wEa1Q+M + v08CQF6XNpb5R9Xszz4aBC4eV0KjtjkCANlGSQHZ1B81g+iltj1FAhRHkyUFlrc1 + cqLVhNgxtHZ96+R57Uk2A7dIJBsE00eIYaHOfk5X5GD/95s1QvPcQskCAOwUk5xj + NeQ6VV/1+cI91TrWU6VnT2Yj8632fM/JlKKfaS15pp8t5Ha6pNFr3xD4KgQutchq + fPsEOjaU7nwQ/i8B/1rDPTYfNXFpRNt33WAB1XtpgOIHlpmOfaYYqf6lneTlZWBc + TgyO+j+ZsHAvP18ugIRkU8D192NflzgAGwXLryijyYifBBgBAgAJBQJYrtUnAhsM + AAoJEMz74Z8ArIsdlkUEALTl6QUutJsqwVF4ZXKmmw0IEk8PkqW4G+tYRDHJMs6Z + O0nzDS89BG2DL4/UlOs5wRvERnlJYz01TMTxq/ciKaBTEjygFIv9CgIEZh97VacZ + TIqcF40k9SbpJNnh3JLf94xsNxNRJTEhbVC3uruaeILue/IR7pBMEyCs49Gcguwy + =b6UD + -----END PGP PRIVATE KEY BLOCK----- + KEY + end + + def public_key + <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mI0EWK7VJwEEANSFayuVYenl7sBKUjmIxwDRc3jd+K+FWUZgknLgiLcevaLh/mxV + 98dLxDKGDHHNKc/B7Y4qdlZYv1wfNQVuIbd8dqUQFOOkH7ukbgcGBTxH+2IM67y+ + QBH618luS5Gz1d4bd0YoFf/xZGEh9G5xicz7TiXYzLKjnMjHu2EmbFePABEBAAG0 + LU5hbm5pZSBCZXJuaGFyZCA8bmFubmllLmJlcm5oYXJkQGV4YW1wbGUuY29tPoi4 + BBMBAgAiBQJYrtUnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDM++Gf + AKyLHaeSA/99oUWpb02PlfkALcx5RncboMHkgczYEU9wOFIgvXIReswThCMOvPZa + piui+ItyJfV3ijJfO8IvbbFcvU7jjGA073Bb7tbzAEOQLA16mWgBLQlGaRWbHDW4 + uwFxvkJKA0GzEsadEXeniESaZPc4rOXKPO3+/MSQWS2bmvvGsBTEuriNBFiu1ScB + BADIXkITf+kKCkD+n8tMsdTLInefu8KrJ8p7YRYCCabEXnWRsDb5zxUAG2VXCVUh + Yl6QXQybkNiBaduS+uxilz7gtYZUMFJvQ09+fV7D2N9B7u/1bGdIYz+cDFJnEJit + LY4w/nju2Sno5CL5Ead8sZuslKetSXPYHR/kbW462EOw5wARAQABiJ8EGAECAAkF + Aliu1ScCGwwACgkQzPvhnwCsix2WRQQAtOXpBS60myrBUXhlcqabDQgSTw+Spbgb + 61hEMckyzpk7SfMNLz0EbYMvj9SU6znBG8RGeUljPTVMxPGr9yIpoFMSPKAUi/0K + AgRmH3tVpxlMipwXjST1Jukk2eHckt/3jGw3E1ElMSFtULe6u5p4gu578hHukEwT + IKzj0ZyC7DI= + =Ug0r + -----END PGP PUBLIC KEY BLOCK----- + KEY + end + + def primary_keyid + fingerprint[-16..-1] + end + + def fingerprint + '5F7EA3981A5845B141ABD522CCFBE19F00AC8B1D' + end + + def names + ['Nannie Bernhard'] + end + + def emails + ['nannie.bernhard@example.com'] + end + end + + module User2 + extend self + + def private_key + <<~KEY.strip + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: GnuPG v1 + + lQHYBFiuqioBBADg46jkiATWMy9t1npxFWJ77xibPXdUo36LAZgZ6uGungSzcFL4 + 50bdEyMMGm5RJp6DCYkZlwQDlM//YEqwf0Cmq/AibC5m9bHr7hf5sMxl40ssJ4fj + dzT6odihO0vxD2ARSrtiwkESzFxjJ51mjOfdPvAGf0ucxzgeRfUlCrM3kwARAQAB + AAP8CJlDFnbywR9dWfqBxi19sFMOk/smCObNQanuTcx6CDcu4zHi0Yxx6BoNCQES + cDRCLX5HevnpZngzQB3qa7dga+yqxKzwO8v0P0hliL81B1ZVXUk9TWhBj3NS3m3v + +kf2XeTxuZFb9fj44/4HpfbQ2yazTs/Xa+/ZeMqFPCYSNEECAOtjIbwHdfjkpVWR + uiwphRkNimv5hdObufs63m9uqhpKPdPKmr2IXgahPZg5PooxqE0k9IXaX2pBsJUF + DyuL1dsCAPSVL+YAOviP8ecM1jvdKpkFDd67kR5C+7jEvOGl+c2aX3qLvKt62HPR + +DxvYE0Oy0xfoHT14zNSfqthmlhIPqkB/i4WyJaafQVvkkoA9+A5aXbyihOR+RTx + p+CMNYvaAplFAyey7nv8l5+la/N+Sv86utjaenLZmCf34nDQEZy7rFWny7QvQmV0 + dGUgQ2FydHdyaWdodCA8YmV0dGUuY2FydHdyaWdodEBleGFtcGxlLmNvbT6IuAQT + AQIAIgUCWK6qKgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQv52SX5Ee + /WVCGwP/QsOLTTyEJ6hl0Yy7DLY3kUxS6xiD9fW1FDoTQlxhiO+8TmghmhdtU3TI + ssP30/Su3pNKW3TkILtE9U8I2krEpsX5NkyMwmI6LXdeZjli2Lvtkx0Fm0Psd4HO + ORYJW5HqTx4jDLzeeIcYjqnobztDpfG8ONDvB0EI0GnCTOZNggG0L0JldHRlIENh + cnR3cmlnaHQgPGJldHRlLmNhcnR3cmlnaHRAZXhhbXBsZS5uZXQ+iLgEEwECACIF + AlivAsUCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEL+dkl+RHv1lXOwE + ANh7ce/vUjv6VMkO8o5OZXszhKE5+MSmYO8v/kkHcXNccC5wI6VF4K//r41p8Cyk + 9NzW7Kzjt2+14/BBqWugCx3xjWCuf88KH5PHbuSmfVYbzJmNSy6rfPmusZG5ePqD + xp5l2qQxMdRUX0Z36D/koM4N0ls6PAf6Xrdv9s6IBMMVnQHYBFiuqioBBADe5nUd + VOcbZlnxOjl0KBAT+A5bmyBLUT0BmLPsmA4PuXDSth7WvibPC8wcCdCYVk0IRMYn + eZUiWq/o5c4rthfLR4jg8kruvomQ4E4d4hyI6R0MLxXYZ3XMu67VuScFgbLURw1e + RZ16ANd3Nc1VuFW7ms0vCG0idB8iSZBoULaK8QARAQABAAP5AdCfUT/y2kmi75iF + ZX1ahSkax9LraEWW8TOCuolR6v2b7jFKrr2xX/P1A2DulID2Y1v4/5MJPHR/1G4D + l95Fkw+iGsTvKB5rPG5xye0vOYbbujRa6B9LL6s4Taf486shEegOrdjN9FIweM6f + vuVaDYzIk8Qwv5/sStEBxx8rxIkCAOBftFi56AY0gLniyEMAvVRjyVeOZPPJbS8i + v6L9asJB5wdsGJxJVyUZ/ylar5aCS7sroOcYTN2b1tOPoWuGqIkCAP5RlDRgm3Zg + xL6hXejqZp3G1/DXhKBSI/yUTR/D89H5/qNQe3W7dZqns9mSAJNtqOu+UMZ5UreY + Ond0/dmL5SkCAOO5r6gXM8ZDcNjydlQexCLnH70yVkCL6hG9Va1gOuFyUztRnCd+ + E35YRCEwZREZDr87BRr2Aak5t+lb1EFVqV+nvYifBBgBAgAJBQJYrqoqAhsMAAoJ + EL+dkl+RHv1lQggEANWwQwrlT2BFLWV8Fx+wlg31+mcjkTq0LaWu3oueAluoSl93 + 2B6ToruMh66JoxpSDU44x3JbCaZ/6poiYs5Aff8ZeyEVlfkVaQ7IWd5spjpXaS4i + oCOfkZepmbTuE7TPQWM4iBAtuIfiJGiwcpWWM+KIH281yhfCcbRzzFLsCVQx + =yEqv + -----END PGP PRIVATE KEY BLOCK----- + KEY + end + + def public_key + <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mI0EWK6qKgEEAODjqOSIBNYzL23WenEVYnvvGJs9d1SjfosBmBnq4a6eBLNwUvjn + Rt0TIwwablEmnoMJiRmXBAOUz/9gSrB/QKar8CJsLmb1sevuF/mwzGXjSywnh+N3 + NPqh2KE7S/EPYBFKu2LCQRLMXGMnnWaM590+8AZ/S5zHOB5F9SUKszeTABEBAAG0 + L0JldHRlIENhcnR3cmlnaHQgPGJldHRlLmNhcnR3cmlnaHRAZXhhbXBsZS5jb20+ + iLgEEwECACIFAliuqioCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEL+d + kl+RHv1lQhsD/0LDi008hCeoZdGMuwy2N5FMUusYg/X1tRQ6E0JcYYjvvE5oIZoX + bVN0yLLD99P0rt6TSlt05CC7RPVPCNpKxKbF+TZMjMJiOi13XmY5Yti77ZMdBZtD + 7HeBzjkWCVuR6k8eIwy83niHGI6p6G87Q6XxvDjQ7wdBCNBpwkzmTYIBtC9CZXR0 + ZSBDYXJ0d3JpZ2h0IDxiZXR0ZS5jYXJ0d3JpZ2h0QGV4YW1wbGUubmV0Poi4BBMB + AgAiBQJYrwLFAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRC/nZJfkR79 + ZVzsBADYe3Hv71I7+lTJDvKOTmV7M4ShOfjEpmDvL/5JB3FzXHAucCOlReCv/6+N + afAspPTc1uys47dvtePwQalroAsd8Y1grn/PCh+Tx27kpn1WG8yZjUsuq3z5rrGR + uXj6g8aeZdqkMTHUVF9Gd+g/5KDODdJbOjwH+l63b/bOiATDFbiNBFiuqioBBADe + 5nUdVOcbZlnxOjl0KBAT+A5bmyBLUT0BmLPsmA4PuXDSth7WvibPC8wcCdCYVk0I + RMYneZUiWq/o5c4rthfLR4jg8kruvomQ4E4d4hyI6R0MLxXYZ3XMu67VuScFgbLU + Rw1eRZ16ANd3Nc1VuFW7ms0vCG0idB8iSZBoULaK8QARAQABiJ8EGAECAAkFAliu + qioCGwwACgkQv52SX5Ee/WVCCAQA1bBDCuVPYEUtZXwXH7CWDfX6ZyOROrQtpa7e + i54CW6hKX3fYHpOiu4yHromjGlINTjjHclsJpn/qmiJizkB9/xl7IRWV+RVpDshZ + 3mymOldpLiKgI5+Rl6mZtO4TtM9BYziIEC24h+IkaLBylZYz4ogfbzXKF8JxtHPM + UuwJVDE= + =0vYo + -----END PGP PUBLIC KEY BLOCK----- + KEY + end + + def primary_keyid + fingerprint[-16..-1] + end + + def fingerprint + '6D494CA6FC90C0CAE0910E42BF9D925F911EFD65' + end + + def names + ['Bette Cartwright', 'Bette Cartwright'] + end + + def emails + ['bette.cartwright@example.com', 'bette.cartwright@example.net'] + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index c32c05b03e2..7682bdf8cd0 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -5,6 +5,7 @@ module TestEnv # When developing the seed repository, comment out the branch you will modify. BRANCH_SHA = { + 'signed-commits' => '5d4a1cb', 'not-merged-branch' => 'b83d6e3', 'branch-merged' => '498214d', 'empty-branch' => '7efb185', diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb new file mode 100644 index 00000000000..c6a17d77d73 --- /dev/null +++ b/spec/workers/create_gpg_signature_worker_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe CreateGpgSignatureWorker do + context 'when GpgKey is found' do + it 'calls Commit#signature' do + commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' + project = create :project + commit = instance_double(Commit) + + allow(Project).to receive(:find_by).with(id: project.id).and_return(project) + allow(project).to receive(:commit).with(commit_sha).and_return(commit) + + expect(commit).to receive(:signature) + + described_class.new.perform(commit_sha, project.id) + end + end + + context 'when Commit is not found' do + let(:nonexisting_commit_sha) { 'bogus' } + let(:project) { create :project } + + it 'does not raise errors' do + expect { described_class.new.perform(nonexisting_commit_sha, project.id) }.not_to raise_error + end + + it 'does not call Commit#signature' do + expect_any_instance_of(Commit).not_to receive(:signature) + + described_class.new.perform(nonexisting_commit_sha, project.id) + end + end + + context 'when Project is not found' do + let(:nonexisting_project_id) { -1 } + + it 'does not raise errors' do + expect { described_class.new.perform(anything, nonexisting_project_id) }.not_to raise_error + end + + it 'does not call Commit#signature' do + expect_any_instance_of(Commit).not_to receive(:signature) + + described_class.new.perform(anything, nonexisting_project_id) + end + end +end diff --git a/spec/workers/invalid_gpg_signature_update_worker_spec.rb b/spec/workers/invalid_gpg_signature_update_worker_spec.rb new file mode 100644 index 00000000000..5972696515b --- /dev/null +++ b/spec/workers/invalid_gpg_signature_update_worker_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe InvalidGpgSignatureUpdateWorker do + context 'when GpgKey is found' do + it 'calls NotificationService.new.run' do + gpg_key = create(:gpg_key) + invalid_signature_updater = double(:invalid_signature_updater) + + expect(Gitlab::Gpg::InvalidGpgSignatureUpdater).to receive(:new).with(gpg_key).and_return(invalid_signature_updater) + expect(invalid_signature_updater).to receive(:run) + + described_class.new.perform(gpg_key.id) + end + end + + context 'when GpgKey is not found' do + let(:nonexisting_gpg_key_id) { -1 } + + it 'does not raise errors' do + expect { described_class.new.perform(nonexisting_gpg_key_id) }.not_to raise_error + end + + it 'does not call NotificationService.new.run' do + expect(Gitlab::Gpg::InvalidGpgSignatureUpdater).not_to receive(:new) + + described_class.new.perform(nonexisting_gpg_key_id) + end + end +end |