blob: 9a6317e2b769516d165da80711154f4873c1b12b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# frozen_string_literal: true
module Gitlab
module Gpg
class Commit < Gitlab::SignedCommit
def signature
super
return @signature if @signature
cached_signature = lazy_signature&.itself
return @signature = cached_signature if cached_signature.present?
@signature = create_cached_signature!
end
def update_signature!(cached_signature)
using_keychain do |gpg_key|
cached_signature.update!(attributes(gpg_key))
@signature = cached_signature
end
end
private
def lazy_signature
BatchLoader.for(@commit.sha).batch do |shas, loader|
GpgSignature.by_commit_sha(shas).each do |signature|
loader.call(signature.commit_sha, signature)
end
end
end
def using_keychain
Gitlab::Gpg.using_tmp_keychain do
# first we need to get the fingerprint from the signature to query the gpg
# key belonging to the fingerprint.
# This way we can add the key to the temporary keychain and extract
# the proper signature.
# NOTE: the invoked method is #fingerprint but versions of GnuPG
# prior to 2.2.13 return 16 characters (the format used by keyid)
# instead of 40.
fingerprint = verified_signature&.fingerprint
break unless fingerprint
gpg_key = find_gpg_key(fingerprint)
if gpg_key
Gitlab::Gpg::CurrentKeyChain.add(gpg_key.key)
clear_memoization(:gpg_signatures)
end
yield gpg_key
end
end
def verified_signature
gpg_signatures.first
end
def create_cached_signature!
using_keychain do |gpg_key|
attributes = attributes(gpg_key)
break GpgSignature.new(attributes) if Gitlab::Database.read_only?
GpgSignature.safe_create!(attributes)
end
end
def gpg_signatures
strong_memoize(:gpg_signatures) do
signatures = []
GPGME::Crypto.new.verify(signature_text, signed_text: signed_text) do |verified_signature|
signatures << verified_signature
end
signatures
rescue GPGME::Error
[]
end
end
def multiple_signatures?
gpg_signatures.size > 1
end
def attributes(gpg_key)
user_infos = user_infos(gpg_key)
verification_status = verification_status(gpg_key)
{
commit_sha: @commit.sha,
project: @commit.project,
gpg_key: gpg_key,
gpg_key_primary_keyid: gpg_key&.keyid || verified_signature&.fingerprint,
gpg_key_user_name: user_infos[:name],
gpg_key_user_email: user_infos[:email],
verification_status: verification_status
}
end
def verification_status(gpg_key)
return :multiple_signatures if multiple_signatures? && Feature.enabled?(:multiple_gpg_signatures, @commit.project, default_enabled: :yaml)
return :unknown_key unless gpg_key
return :unverified_key unless gpg_key.verified?
return :unverified unless verified_signature&.valid?
if gpg_key.verified_and_belongs_to_email?(@commit.committer_email)
:verified
elsif gpg_key.user.all_emails.include?(@commit.committer_email)
:same_user_different_email
else
:other_user
end
end
def user_infos(gpg_key)
gpg_key&.verified_user_infos&.first || gpg_key&.user_infos&.first || {}
end
def find_gpg_key(fingerprint)
if fingerprint.length > 16
GpgKey.find_by_fingerprint(fingerprint) || GpgKeySubkey.find_by_fingerprint(fingerprint)
else
GpgKey.find_by_primary_keyid(fingerprint) || GpgKeySubkey.find_by_keyid(fingerprint)
end
end
end
end
end
|