Status: | Merged |
---|---|
Approved by: | Jonathan Riddell |
Approved revision: | no longer in the source branch. |
Merge reported by: | Jonathan Riddell |
Merged at revision: | not available |
Proposed branch: | lp:~jr/bzr/bzr-gpgme |
Merge into: | lp:bzr |
Diff against target: |
122 lines (+25/-16) 1 file modified
bzrlib/gpg.py (+25/-16) |
To merge this branch: | bzr merge lp:~jr/bzr/bzr-gpgme |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
bzr-core | Pending | ||
Review via email: mp+64859@code.launchpad.net |
Commit message
A new command ``bzr verify-signatures`` has been added to check that commits
are correctly signed with trusted keys by GPG. This requires python-gpgme to
be installed. ``bzr log`` has gained a ``--signatures`` option to list the
validity of signatures for each commit. New config options ``acceptable_keys``
and ``validate_
commands.
Description of the change
Implement bzr verify command
verifies that commits on a branch are signed by trusted keys
uses gpgme, a new (non-required) dependency for bzr
sign-my-commits.py should probably be renamed
still some tidying up to do but I'd like feedback on it so far
Jelmer Vernooij (jelmer) wrote : | # |
Jonathan Riddell (jr) wrote : | # |
Jelmer's suggestions added
Jelmer Vernooij (jelmer) wrote : | # |
I think you're on the right track with this; here are some more comments. It would be great if somebody else (with more GPG experience) could also comment.
113 + if from_revno is None or to_revno is None:
114 + raise errors.
115 + non-revision-
As far as I know this isn't possible - perhaps this should be an AssertionError rather than a BzrCommandError?
For consistency we've been adding --directory/-d option to most commands; it would be nice to support that to "bzr verify" as well.
I think the command name is perhaps a bit generic, perhaps it should be "bzr verify-signatures", with "verify" as one of its current aliases. We currently already have "bzr check" and to me, as a non-native speaker, these two words mean roughly the same thing in this context.
I'm unsure about supporting a --acceptable-keys argument to verify. I think it would be sufficient for verify to just check that the signatures match what is actually in the repository, leaving the issue of who to trust up to the user for the time being. I don't see myself using --acceptable-keys because I usually only care about the signature on a particular revision (not the history leading up to that point) or there are too many revisions and thus too many keys to specify (bzr has > 200 contributors, it's not practical to specify all their key ids on the command line).
We should watch out with displaying the signature status for each revision in "bzr log"; it has the potential to significantly slow down log if more people start signing their revisions.
Jonathan Riddell (jr) wrote : | # |
> 113 + if from_revno is None or to_revno is None:
> 114 + raise errors.
> 115 + non-revision-
> As far as I know this isn't possible - perhaps this should be an AssertionError rather than a BzrCommandError?
This happens if you specify versions from a merged revision, bzr verify -r 3.1.1..3.1.2 which isn't supported by this code
The code comes from builtins.
> For consistency we've been adding --directory/-d option to most commands; it would be nice to support that to "bzr verify" as well.
Added.
> I think the command name is perhaps a bit generic, perhaps it should be "bzr verify-signatures", with "verify" as one of its current aliases.
Done.
> I'm unsure about supporting a --acceptable-keys argument to verify.
I trust lots of people to have digital keys that match their ID, but
it doesn't mean I trust them to commit to my code branch, so there
does need to be a way to specify which keys are acceptable.
> We should watch out with displaying the signature status for each
revision in "bzr log"; it has the potential to significantly slow
down log if more people start signing their revisions.
That's why it's an option which isn't on by default.
Jonathan
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 6/21/2011 2:06 PM, Jonathan Riddell wrote:
>> 113 + if from_revno is None or to_revno is None:
>> 114 + raise errors.
>> 115 + non-revision-
>> As far as I know this isn't possible - perhaps this should be an AssertionError rather than a BzrCommandError?
>
> This happens if you specify versions from a merged revision, bzr verify -r 3.1.1..3.1.2 which isn't supported by this code
> The code comes from builtins.
>
Some thoughts:
[1 needs fixing] You are adding a call to "get_ancestry()" which Jelmer
just recently marked as deprecated. I imagine this is pretty easy to fix
following his examples.
[2 comment] You import 'gpgme' inside of builtins.py. I would have
expected that sort of thing to be hidden inside 'gpg.py'. Something like
"gpg.support_
[3 tweak] You have too much code in "cmd_verify_
that should probably be moved into gpg.py and the cmd_* code should be a
reasonably small shim. That usually makes it easier to re-use and test.
Especially if a GUI ever wants to borrow some of the verify code.
[4 needsinfo] GpgStrategy.
- From what I can tell, you create a new gpgme Context for every revision.
Which I *think* means spawning a new gpg process. It seems like a
scalable design would want to be able to hold the context for an
extended period of time. Could it be a lazy attribute of GPGStrategy?
[5 needsinfo] Would we want a way to distinguish from "signature is
properly signed by an un-acceptable user" from "signature not properly
signed"? The latter hints more of corruption. And certainly from a UI
level, it might prompt me to say "oh, I need to add this person" rather
than "why isn't there signature valid?".
Something like SIGNATURE_
SIGNATURE_
[6 needsinfo] You mention that it slows down "bzr log" quite a bit. Have
you tried --lsprof to see what that cause is? (Are we re-computing the
testament from scratch for each rev?) I think it would be reasonable to
consider being able to separate out "check the signatures are gpg-valid"
from "the revision matches its testament". Just because at present, the
latter is *very* expensive, but the former should be pretty cheap.
I believe that we want to make the latter cheaper so that it can scale
to more active use. However, I'm curious what the specific performance
overhead is.
[7 needsfixing] bzrlib/
You're adding one blank line per statement, which isn't preferred form.
You also have a continuation line which is a bit unnatural. Something
more like:
def test_verify_
wt = self.setup_tree()
self.
self.
out = self.run_
self.
'4 commits with valid signatures\n'
'0 commits with unknown keys\n'
'0 commits not valid'
'1 commit not signed\n'), out)
I'm also wondering why we are...
Jonathan Riddell (jr) wrote : | # |
On Wed, Jun 22, 2011 at 11:43:37AM -0000, John A Meinel wrote:
> [1 needs fixing] You are adding a call to "get_ancestry()" which Jelmer
> just recently marked as deprecated. I imagine this is pretty easy to fix
> following his examples.
This was a bad merge, now fixed.
> [2 comment] You import 'gpgme' inside of builtins.py. I would have
> expected that sort of thing to be hidden inside 'gpg.py'. Something like
> "gpg.support_
Done.
> [3 tweak] You have too much code in "cmd_verify_
> that should probably be moved into gpg.py and the cmd_* code should be a
> reasonably small shim. That usually makes it easier to re-use and test.
> Especially if a GUI ever wants to borrow some of the verify code.
Done.
> [4 needsinfo] GpgStrategy.
>
> - From what I can tell, you create a new gpgme Context for every revision.
> Which I *think* means spawning a new gpg process. It seems like a
> scalable design would want to be able to hold the context for an
> extended period of time. Could it be a lazy attribute of GPGStrategy?
I've made the context global to the GpgStrategy class (although I
don't think it starts a new gpg process for each one).
> [5 needsinfo] Would we want a way to distinguish from "signature is
> properly signed by an un-acceptable user" from "signature not properly
> signed"?
There's no such concept as an unacceptable user, only acceptable ones.
Anything else is an unknown key and listed as such.
> [6 needsinfo] You mention that it slows down "bzr log" quite a bit. Have
> you tried --lsprof to see what that cause is?
The main cause is calls to verify() and get_key() in gpgme according to --lsprof.
> [7 needsfixing] bzrlib/
> You're adding one blank line per statement, which isn't preferred form.
Done
> [8 needsfixing] I don't think we need the short-form of "verify". I
> agree with Jelmer that it sounds more like repository checking, and I
> don't think it is something people will run enough to need a short form for.
>
> The docs also need to be updated for this fact.
Done
> [9 needsfixing] We'll also want an entry in
> doc/en/
> doc/whatsnew/
Done
Jonathan
Jonathan Riddell (jr) wrote : | # |
From discussion with JAM, needs to handle expired keys more gracefully, should give different error for unknown and unacceptable key, gpg verify should be commented so it can be easier understood and checked over by someone knowledgeable in gpgme.
John A Meinel (jameinel) wrote : | # |
So I should clarify that while I agree with what needs to be done, we can
probably land this and do those bits as follow up cleanup. We don't want a
lot of drift, but I think this is incrementally better in all aspects, and
we shouldn't require it to be perfect. I'm sorry if I've been a bit
blockish, I have to remind myself sometimes. The rules for landing have
never been that it has to be perfect.
Merge: approve
John
=:->
On Jun 29, 2011 3:08 PM, "Jonathan Riddell" <email address hidden> wrote:
>>From discussion with JAM, needs to handle expired keys more gracefully,
should give different error for unknown and unacceptable key, gpg verify
should be commented so it can be easier understood and checked over by
someone knowledgeable in gpgme.
>
> --
> https:/
> Your team bzr-core is requested to review the proposed merge of
lp:~jr/bzr/bzr-gpgme into lp:bzr.
Jonathan Riddell (jr) wrote : | # |
sent to pqm by email
Jonathan Riddell (jr) wrote : | # |
sent to pqm by email
Jonathan Riddell (jr) wrote : | # |
sent to pqm by email
Preview Diff
1 | === modified file 'bzrlib/gpg.py' |
2 | --- bzrlib/gpg.py 2011-06-30 14:36:05 +0000 |
3 | +++ bzrlib/gpg.py 2011-06-30 16:17:30 +0000 |
4 | @@ -161,6 +161,9 @@ |
5 | |
6 | @staticmethod |
7 | def verify_signatures_available(): |
8 | + """ |
9 | + :return: boolean if this strategy can verify signatures |
10 | + """ |
11 | try: |
12 | import gpgme |
13 | return True |
14 | @@ -274,6 +277,7 @@ |
15 | if isinstance(acceptable_keys_config, unicode): |
16 | acceptable_keys_config = str(acceptable_keys_config) |
17 | except UnicodeEncodeError: |
18 | + #gpg Context.keylist(pattern) does not like unicode |
19 | raise errors.BzrCommandError('Only ASCII permitted in option names') |
20 | |
21 | if acceptable_keys_config is not None: |
22 | @@ -296,11 +300,14 @@ |
23 | "No GnuPG key results for pattern: {}" |
24 | ).format(pattern)) |
25 | |
26 | - def do_verifications(self, revisions, repository): |
27 | + def do_verifications(self, revisions, repository, |
28 | + process_events_callback=None): |
29 | """do verifications on a set of revisions |
30 | |
31 | :param revisions: list of revision ids to verify |
32 | :param repository: repository object |
33 | + :param process_events_callback: method to call for GUI frontends that |
34 | + want to keep their UI refreshed |
35 | |
36 | :return: count dictionary of results of each type, |
37 | result list for each revision, |
38 | @@ -319,6 +326,8 @@ |
39 | count[verification_result] += 1 |
40 | if verification_result != SIGNATURE_VALID: |
41 | all_verifiable = False |
42 | + if process_events_callback is not None: |
43 | + process_events_callback() |
44 | return (count, result, all_verifiable) |
45 | |
46 | def verbose_valid_message(self, result): |
47 | @@ -330,8 +339,8 @@ |
48 | signers[uid] += 1 |
49 | result = [] |
50 | for uid, number in signers.items(): |
51 | - result.append( i18n.ngettext("{0} signed {1} commit", |
52 | - "{0} signed {1} commits", |
53 | + result.append( i18n.ngettext(u"{0} signed {1} commit", |
54 | + u"{0} signed {1} commits", |
55 | number).format(uid, number) ) |
56 | return result |
57 | |
58 | @@ -347,8 +356,8 @@ |
59 | signers[authors] += 1 |
60 | result = [] |
61 | for authors, number in signers.items(): |
62 | - result.append( i18n.ngettext("{0} commit by author {1}", |
63 | - "{0} commits by author {1}", |
64 | + result.append( i18n.ngettext(u"{0} commit by author {1}", |
65 | + u"{0} commits by author {1}", |
66 | number).format(number, authors) ) |
67 | return result |
68 | |
69 | @@ -363,8 +372,8 @@ |
70 | signers[authors] += 1 |
71 | result = [] |
72 | for authors, number in signers.items(): |
73 | - result.append( i18n.ngettext("{0} commit by author {1}", |
74 | - "{0} commits by author {1}", |
75 | + result.append( i18n.ngettext(u"{0} commit by author {1}", |
76 | + u"{0} commits by author {1}", |
77 | number).format(number, authors) ) |
78 | return result |
79 | |
80 | @@ -377,33 +386,33 @@ |
81 | signers[fingerprint] += 1 |
82 | result = [] |
83 | for fingerprint, number in signers.items(): |
84 | - result.append( i18n.ngettext("Unknown key {0} signed {1} commit", |
85 | - "Unknown key {0} signed {1} commits", |
86 | + result.append( i18n.ngettext(u"Unknown key {0} signed {1} commit", |
87 | + u"Unknown key {0} signed {1} commits", |
88 | number).format(fingerprint, number) ) |
89 | return result |
90 | |
91 | def valid_commits_message(self, count): |
92 | """returns message for number of commits""" |
93 | - return i18n.gettext("{0} commits with valid signatures").format( |
94 | + return i18n.gettext(u"{0} commits with valid signatures").format( |
95 | count[SIGNATURE_VALID]) |
96 | |
97 | def unknown_key_message(self, count): |
98 | """returns message for number of commits""" |
99 | - return i18n.ngettext("{0} commit with unknown key", |
100 | - "{0} commits with unknown keys", |
101 | + return i18n.ngettext(u"{0} commit with unknown key", |
102 | + u"{0} commits with unknown keys", |
103 | count[SIGNATURE_KEY_MISSING]).format( |
104 | count[SIGNATURE_KEY_MISSING]) |
105 | |
106 | def commit_not_valid_message(self, count): |
107 | """returns message for number of commits""" |
108 | - return i18n.ngettext("{0} commit not valid", |
109 | - "{0} commits not valid", |
110 | + return i18n.ngettext(u"{0} commit not valid", |
111 | + u"{0} commits not valid", |
112 | count[SIGNATURE_NOT_VALID]).format( |
113 | count[SIGNATURE_NOT_VALID]) |
114 | |
115 | def commit_not_signed_message(self, count): |
116 | """returns message for number of commits""" |
117 | - return i18n.ngettext("{0} commit not signed", |
118 | - "{0} commits not signed", |
119 | + return i18n.ngettext(u"{0} commit not signed", |
120 | + u"{0} commits not signed", |
121 | count[SIGNATURE_NOT_SIGNED]).format( |
122 | count[SIGNATURE_NOT_SIGNED]) |
Thanks for taking this on.. it's one of the oldest unfinished features in bzr I think :) Not a full review, but some quick notes:
VerifyFailed seems a bit too generic a name for something that applies only to verifying signatures. Perhaps something like SignatureVerifi cationFailed ?
GpgmeNotInstalled should probably derive from DependencyNotPr esent (see also ParamikoNotPresent in bzrlib.errors).
Tests that use this should probably call self.requireFea ture(GPGMeFeatu re) or something along those lines to make sure the tests are skipped for people that do not have gpgme installed.