89
98
print 'Signed %d revisions' % (count,)
101
class cmd_verify_signatures(Command):
102
__doc__ = """Verify all commit signatures.
104
Verifies that all commits in the branch are signed by known GnuPG keys.
108
Option('acceptable-keys',
109
help='Comma separated list of GPG key patterns which are'
110
' acceptable for verification.',
118
def run(self, acceptable_keys=None, revision=None, verbose=None,
120
bzrdir = _mod_bzrdir.BzrDir.open_containing(directory)[0]
121
branch = bzrdir.open_branch()
122
repo = branch.repository
123
branch_config = branch.get_config()
124
gpg_strategy = gpg.GPGStrategy(branch_config)
126
acceptable_keys_config = branch_config.acceptable_keys()
128
acceptable_keys_config = str(acceptable_keys_config)
129
except UnicodeEncodeError:
130
raise errors.BzrCommandError('Only ASCII permitted in option names')
132
if acceptable_keys_config is not None:
133
gpg_strategy.set_acceptable_keys(acceptable_keys_config)
134
if acceptable_keys is not None: #command line overrides config
135
gpg_strategy.set_acceptable_keys(acceptable_keys)
137
count = {gpg.SIGNATURE_VALID: 0,
138
gpg.SIGNATURE_KEY_MISSING: 0,
139
gpg.SIGNATURE_NOT_VALID: 0,
140
gpg.SIGNATURE_NOT_SIGNED: 0}
144
if revision is not None:
145
if len(revision) == 1:
146
revno, rev_id = revision[0].in_history(branch)
147
revisions.append(rev_id)
148
elif len(revision) == 2:
149
from_revno, from_revid = revision[0].in_history(branch)
150
to_revno, to_revid = revision[1].in_history(branch)
152
to_revno = branch.revno()
153
if from_revno is None or to_revno is None:
154
raise errors.BzrCommandError('Cannot verify a range of '\
155
'non-revision-history revisions')
156
for revno in range(from_revno, to_revno + 1):
157
revisions.append(branch.get_rev_id(revno))
159
#all revisions by default including merges
160
graph = repo.get_graph()
163
for rev_id, parents in graph.iter_ancestry(
164
[branch.last_revision()]):
165
if _mod_revision.is_null(rev_id):
170
revisions.append(rev_id)
172
for rev_id in revisions:
173
verification_result, uid = repo.verify_revision(rev_id,gpg_strategy)
174
result.append([rev_id, verification_result, uid])
175
count[verification_result] += 1
177
if count[gpg.SIGNATURE_VALID] > 0 and \
178
count[gpg.SIGNATURE_KEY_MISSING] == 0 and \
179
count[gpg.SIGNATURE_NOT_VALID] == 0 and \
180
count[gpg.SIGNATURE_NOT_SIGNED] == 0:
181
note(gettext("All commits signed with verifiable keys"))
183
self._print_verbose_valid_message(result)
186
note(gettext("{0} commits with valid signatures").format(
187
count[gpg.SIGNATURE_VALID]))
189
self._print_verbose_valid_message(result)
190
#TODO verbose for the other types too
191
note(ngettext("{0} commit with unknown key",
192
"{0} commits with unknown keys",
193
count[gpg.SIGNATURE_KEY_MISSING]).format(
194
count[gpg.SIGNATURE_KEY_MISSING]))
196
self._print_verbose_missing_key_message(result)
197
note(ngettext("{0} commit not valid",
198
"{0} commits not valid",
199
count[gpg.SIGNATURE_NOT_VALID]).format(
200
count[gpg.SIGNATURE_NOT_VALID]))
202
self._print_verbose_not_valid(result, repo)
203
note(ngettext("{0} commit not signed",
204
"{0} commits not signed",
205
count[gpg.SIGNATURE_NOT_SIGNED]).format(
206
count[gpg.SIGNATURE_NOT_SIGNED]))
208
self._print_verbose_not_signed(result, repo)
211
def _print_verbose_not_valid(self, result, repo):
212
"""takes a verify result and prints out not signed commit info"""
214
for rev_id, validity, empty in result:
215
if validity == gpg.SIGNATURE_NOT_VALID:
216
revision = repo.get_revision(rev_id)
217
authors = ', '.join(revision.get_apparent_authors())
218
signers.setdefault(authors, 0)
219
signers[authors] += 1
220
for authors, number in signers.items():
221
note(gettext(ngettext(" {0} commit by author {1}",
222
" {0} commits by author {1}",
223
number)).format(number, authors))
225
def _print_verbose_not_signed(self, result, repo):
226
"""takes a verify result and prints out not signed commit info"""
228
for rev_id, validity, empty in result:
229
if validity == gpg.SIGNATURE_KEY_MISSING:
230
revision = repo.get_revision(rev_id)
231
authors = ', '.join(revision.get_apparent_authors())
232
signers.setdefault(authors, 0)
233
signers[authors] += 1
234
for authors, number in signers.items():
235
note(gettext(ngettext(" {0} commit by author {1}",
236
" {0} commits by author {1}",
237
number)).format(number, authors))
239
def _print_verbose_missing_key_message(self, result):
240
"""takes a verify result and prints out missing key info"""
242
for rev_id, validity, fingerprint in result:
243
if validity == gpg.SIGNATURE_KEY_MISSING:
244
signers.setdefault(fingerprint, 0)
245
signers[fingerprint] += 1
246
for fingerprint, number in signers.items():
247
note(gettext(ngettext(" Unknown key {0} signed {1} commit",
248
" Unknown key {0} signed {1} commits",
249
number)).format(fingerprint, number))
251
def _print_verbose_valid_message(self, result):
252
"""takes a verify result and prints out number of signed commits"""
254
for rev_id, validity, uid in result:
255
if validity == gpg.SIGNATURE_VALID:
256
signers.setdefault(uid, 0)
258
for uid, number in signers.items():
259
note(gettext(ngettext(" {0} signed {1} commit",
260
" {0} signed {1} commits",
261
number)).format(uid, number))