summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2017-07-27 15:29:39 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2017-07-27 15:29:39 +0000
commitac0cbe69706a2d3013d37adda7514824a2d06ed4 (patch)
tree7730163984394f4e79711c609a2d1da2bb4f807e /lib
parent9981814514742a2ee507d4dcc2fd71099fd96585 (diff)
parent5ebccab1eb74f7bf9f7f9d4f2d9a56fb81754cbe (diff)
downloadgitlab-ce-ac0cbe69706a2d3013d37adda7514824a2d06ed4.tar.gz
Merge branch 'feature/gpg-signed-commits' into 'master'
GPG signed commits Closes #20268 See merge request !9546
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/git/commit.rb11
-rw-r--r--lib/gitlab/gpg.rb62
-rw-r--r--lib/gitlab/gpg/commit.rb85
-rw-r--r--lib/gitlab/gpg/invalid_gpg_signature_updater.rb19
4 files changed, 176 insertions, 1 deletions
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 09511cc6504..ca7e3a7c4be 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -319,6 +319,15 @@ module Gitlab
end
end
+ # Get the gpg signature of this commit.
+ #
+ # Ex.
+ # commit.signature(repo)
+ #
+ def signature(repo)
+ Rugged::Commit.extract_signature(repo.rugged, sha)
+ end
+
def stats
Gitlab::Git::CommitStats.new(self)
end
@@ -327,7 +336,7 @@ module Gitlab
begin
raw_commit.to_mbox(options)
rescue Rugged::InvalidError => ex
- if ex.message =~ /Commit \w+ is a merge commit/
+ if ex.message =~ /commit \w+ is a merge commit/i
'Patch format is not currently supported for merge commits.'
end
end
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
new file mode 100644
index 00000000000..e1d1724295a
--- /dev/null
+++ b/lib/gitlab/gpg.rb
@@ -0,0 +1,62 @@
+module Gitlab
+ module Gpg
+ extend self
+
+ module CurrentKeyChain
+ extend self
+
+ def add(key)
+ GPGME::Key.import(key)
+ end
+
+ def fingerprints_from_key(key)
+ import = GPGME::Key.import(key)
+
+ return [] if import.imported == 0
+
+ import.imports.map(&:fingerprint)
+ end
+ end
+
+ def fingerprints_from_key(key)
+ using_tmp_keychain do
+ CurrentKeyChain.fingerprints_from_key(key)
+ end
+ end
+
+ def primary_keyids_from_key(key)
+ using_tmp_keychain do
+ fingerprints = CurrentKeyChain.fingerprints_from_key(key)
+
+ GPGME::Key.find(:public, fingerprints).map { |raw_key| raw_key.primary_subkey.keyid }
+ end
+ end
+
+ def user_infos_from_key(key)
+ using_tmp_keychain do
+ fingerprints = CurrentKeyChain.fingerprints_from_key(key)
+
+ GPGME::Key.find(:public, fingerprints).flat_map do |raw_key|
+ raw_key.uids.map { |uid| { name: uid.name, email: uid.email } }
+ end
+ end
+ end
+
+ def using_tmp_keychain
+ Dir.mktmpdir do |dir|
+ @original_dirs ||= [GPGME::Engine.dirinfo('homedir')]
+ @original_dirs.push(dir)
+
+ GPGME::Engine.home_dir = dir
+
+ return_value = yield
+
+ @original_dirs.pop
+
+ GPGME::Engine.home_dir = @original_dirs[-1]
+
+ return_value
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
new file mode 100644
index 00000000000..55428b85207
--- /dev/null
+++ b/lib/gitlab/gpg/commit.rb
@@ -0,0 +1,85 @@
+module Gitlab
+ module Gpg
+ class Commit
+ attr_reader :commit
+
+ def initialize(commit)
+ @commit = commit
+
+ @signature_text, @signed_text = commit.raw.signature(commit.project.repository)
+ end
+
+ def has_signature?
+ !!(@signature_text && @signed_text)
+ end
+
+ def signature
+ return unless has_signature?
+
+ cached_signature = GpgSignature.find_by(commit_sha: commit.sha)
+ return cached_signature if cached_signature.present?
+
+ using_keychain do |gpg_key|
+ create_cached_signature!(gpg_key)
+ end
+ end
+
+ def update_signature!(cached_signature)
+ using_keychain do |gpg_key|
+ cached_signature.update_attributes!(attributes(gpg_key))
+ end
+ end
+
+ private
+
+ def using_keychain
+ Gitlab::Gpg.using_tmp_keychain do
+ # first we need to get the keyid from the signature to query the gpg
+ # key belonging to the keyid.
+ # This way we can add the key to the temporary keychain and extract
+ # the proper signature.
+ gpg_key = GpgKey.find_by(primary_keyid: verified_signature.fingerprint)
+
+ if gpg_key
+ Gitlab::Gpg::CurrentKeyChain.add(gpg_key.key)
+ @verified_signature = nil
+ end
+
+ yield gpg_key
+ end
+ end
+
+ def verified_signature
+ @verified_signature ||= GPGME::Crypto.new.verify(@signature_text, signed_text: @signed_text) do |verified_signature|
+ break verified_signature
+ end
+ end
+
+ def create_cached_signature!(gpg_key)
+ GpgSignature.create!(attributes(gpg_key))
+ end
+
+ def attributes(gpg_key)
+ user_infos = user_infos(gpg_key)
+
+ {
+ commit_sha: commit.sha,
+ project: commit.project,
+ gpg_key: gpg_key,
+ gpg_key_primary_keyid: gpg_key&.primary_keyid || verified_signature.fingerprint,
+ gpg_key_user_name: user_infos[:name],
+ gpg_key_user_email: user_infos[:email],
+ valid_signature: gpg_signature_valid_signature_value(gpg_key)
+ }
+ end
+
+ def gpg_signature_valid_signature_value(gpg_key)
+ !!(gpg_key && gpg_key.verified? && verified_signature.valid?)
+ end
+
+ def user_infos(gpg_key)
+ gpg_key&.verified_user_infos&.first || gpg_key&.user_infos&.first || {}
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
new file mode 100644
index 00000000000..3bb491120ba
--- /dev/null
+++ b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
@@ -0,0 +1,19 @@
+module Gitlab
+ module Gpg
+ class InvalidGpgSignatureUpdater
+ def initialize(gpg_key)
+ @gpg_key = gpg_key
+ end
+
+ def run
+ GpgSignature
+ .select(:id, :commit_sha, :project_id)
+ .where('gpg_key_id IS NULL OR valid_signature = ?', false)
+ .where(gpg_key_primary_keyid: @gpg_key.primary_keyid)
+ .find_each do |gpg_signature|
+ Gitlab::Gpg::Commit.new(gpg_signature.commit).update_signature!(gpg_signature)
+ end
+ end
+ end
+ end
+end