/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6609.1.1 by Vincent Ladeuil
Fix the failing gpg test on wily.
1
# Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012, 2013, 2016 Canonical Ltd
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
17
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
18
"""GPG signing and checking logic."""
19
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
20
import os
21
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
22
from breezy.lazy_import import lazy_import
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
23
lazy_import(globals(), """
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
24
from breezy import (
6372.1.3 by Vincent Ladeuil
Fix gpg_signing_ket default values handling
25
    config,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
26
    trace,
1551.8.11 by Aaron Bentley
Clear terminal before signing
27
    ui,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
28
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
29
from breezy.i18n import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
30
    gettext,
6092.2.3 by Jonathan Riddell
improve formatting
31
    ngettext,
32
    )
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
33
""")
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
34
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
35
from . import (
36
    errors,
37
    )
6491.1.4 by Jelmer Vernooij
Deprecate GPGStrategy.do_verifications.
38
7143.15.2 by Jelmer Vernooij
Run autopep8.
39
# verification results
5971.1.1 by Jonathan Riddell
add a verify command
40
SIGNATURE_VALID = 0
41
SIGNATURE_KEY_MISSING = 1
42
SIGNATURE_NOT_VALID = 2
43
SIGNATURE_NOT_SIGNED = 3
6043.3.1 by Jonathan Riddell
Report commits signed with expired keys in "verify-signatures".
44
SIGNATURE_EXPIRED = 4
5971.1.1 by Jonathan Riddell
add a verify command
45
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
46
MODE_NORMAL = 0
47
MODE_DETACH = 1
48
MODE_CLEAR = 2
49
5971.1.1 by Jonathan Riddell
add a verify command
50
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
51
class GpgNotInstalled(errors.DependencyNotPresent):
52
7195.4.2 by Jelmer Vernooij
Clarify error messages.
53
    _fmt = ('python-gpg is not installed, it is needed to create or '
54
            'verify signatures. %(error)s')
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
55
56
    def __init__(self, error):
57
        errors.DependencyNotPresent.__init__(self, 'gpg', error)
58
59
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
60
class SigningFailed(errors.BzrError):
61
62
    _fmt = 'Failed to GPG sign data: "%(error)s"'
63
64
    def __init__(self, error):
65
        errors.BzrError.__init__(self, error=error)
66
67
68
class SignatureVerificationFailed(errors.BzrError):
69
70
    _fmt = 'Failed to verify GPG signature data with error "%(error)s"'
71
72
    def __init__(self, error):
73
        errors.BzrError.__init__(self, error=error)
74
75
6491.1.3 by Jelmer Vernooij
Make 'bzr verify-signatures' show a progress bar.
76
def bulk_verify_signatures(repository, revids, strategy,
7143.15.2 by Jelmer Vernooij
Run autopep8.
77
                           process_events_callback=None):
6491.1.3 by Jelmer Vernooij
Make 'bzr verify-signatures' show a progress bar.
78
    """Do verifications on a set of revisions
79
80
    :param repository: repository object
81
    :param revids: list of revision ids to verify
82
    :param strategy: GPG strategy to use
83
    :param process_events_callback: method to call for GUI frontends that
84
        want to keep their UI refreshed
85
86
    :return: count dictionary of results of each type,
87
             result list for each revision,
88
             boolean True if all results are verified successfully
89
    """
90
    count = {SIGNATURE_VALID: 0,
91
             SIGNATURE_KEY_MISSING: 0,
92
             SIGNATURE_NOT_VALID: 0,
93
             SIGNATURE_NOT_SIGNED: 0,
94
             SIGNATURE_EXPIRED: 0}
95
    result = []
96
    all_verifiable = True
97
    total = len(revids)
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
98
    with ui.ui_factory.nested_progress_bar() as pb:
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
99
        for i, (rev_id, verification_result, uid) in enumerate(
100
                repository.verify_revision_signatures(
101
                    revids, strategy)):
6491.1.3 by Jelmer Vernooij
Make 'bzr verify-signatures' show a progress bar.
102
            pb.update("verifying signatures", i, total)
103
            result.append([rev_id, verification_result, uid])
104
            count[verification_result] += 1
105
            if verification_result != SIGNATURE_VALID:
106
                all_verifiable = False
107
            if process_events_callback is not None:
108
                process_events_callback()
109
    return (count, result, all_verifiable)
110
111
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
112
class DisabledGPGStrategy(object):
113
    """A GPG Strategy that makes everything fail."""
114
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
115
    @staticmethod
116
    def verify_signatures_available():
117
        return True
118
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
119
    def __init__(self, ignored):
120
        """Real strategies take a configuration."""
121
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
122
    def sign(self, content, mode):
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
123
        raise SigningFailed('Signing is disabled.')
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
124
6883.11.2 by Jelmer Vernooij
Change signature API.
125
    def verify(self, signed_data, signature=None):
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
126
        raise SignatureVerificationFailed('Signature verification is \
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
127
disabled.')
5971.1.6 by Jonathan Riddell
fix methods for dummy gpg strategies
128
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
129
    def set_acceptable_keys(self, command_line_input):
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
130
        pass
131
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
132
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
133
class LoopbackGPGStrategy(object):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
134
    """A GPG Strategy that acts like 'cat' - data is just passed through.
5971.1.86 by Jonathan Riddell
doc string formatting
135
    Used in tests.
136
    """
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
137
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
138
    @staticmethod
139
    def verify_signatures_available():
140
        return True
141
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
142
    def __init__(self, ignored):
143
        """Real strategies take a configuration."""
144
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
145
    def sign(self, content, mode):
7143.15.2 by Jelmer Vernooij
Run autopep8.
146
        return (b"-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content
147
                + b"-----END PSEUDO-SIGNED CONTENT-----\n")
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
148
6883.11.2 by Jelmer Vernooij
Change signature API.
149
    def verify(self, signed_data, signature=None):
7143.15.2 by Jelmer Vernooij
Run autopep8.
150
        plain_text = signed_data.replace(
151
            b"-----BEGIN PSEUDO-SIGNED CONTENT-----\n", b"")
152
        plain_text = plain_text.replace(
153
            b"-----END PSEUDO-SIGNED CONTENT-----\n", b"")
6883.11.3 by Jelmer Vernooij
Fix tests.
154
        return SIGNATURE_VALID, None, plain_text
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
155
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
156
    def set_acceptable_keys(self, command_line_input):
157
        if command_line_input is not None:
158
            patterns = command_line_input.split(",")
159
            self.acceptable_keys = []
160
            for pattern in patterns:
161
                if pattern == "unknown":
162
                    pass
163
                else:
164
                    self.acceptable_keys.append(pattern)
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
165
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
166
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
167
def _set_gpg_tty():
168
    tty = os.environ.get('TTY')
169
    if tty is not None:
170
        os.environ['GPG_TTY'] = tty
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
171
        trace.mutter('setting GPG_TTY=%s', tty)
172
    else:
173
        # This is not quite worthy of a warning, because some people
174
        # don't need GPG_TTY to be set. But it is worthy of a big mark
7344.2.1 by Martin
Relocate the bzr log file out of $HOME
175
        # in brz.log, so that people can debug it if it happens to them
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
176
        trace.mutter('** Env var TTY empty, cannot set GPG_TTY.'
177
                     '  Is TTY exported?')
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
178
179
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
180
class GPGStrategy(object):
181
    """GPG Signing and checking facilities."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
182
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
183
    acceptable_keys = None
184
6351.3.9 by Vincent Ladeuil
Move __init__ at the beginning of the class since that's the current idiom.
185
    def __init__(self, config_stack):
186
        self._config_stack = config_stack
187
        try:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
188
            import gpg
189
            self.context = gpg.Context()
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
190
            self.context.armor = True
6883.11.6 by Jelmer Vernooij
Support tests on machines without gpg.
191
            self.context.signers = self._get_signing_keys()
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
192
        except ImportError:
7143.15.2 by Jelmer Vernooij
Run autopep8.
193
            pass  # can't use verify()
6351.3.9 by Vincent Ladeuil
Move __init__ at the beginning of the class since that's the current idiom.
194
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
195
    def _get_signing_keys(self):
196
        import gpg
197
        keyname = self._config_stack.get('gpg_signing_key')
7239.3.1 by Jelmer Vernooij
Support gpg_signing_key=default to indicate using the default GPG key.
198
        if keyname == 'default':
199
            # Leave things to gpg
200
            return []
201
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
202
        if keyname:
203
            try:
204
                return [self.context.get_key(keyname)]
205
            except gpg.errors.KeyNotFound:
206
                pass
207
7239.3.1 by Jelmer Vernooij
Support gpg_signing_key=default to indicate using the default GPG key.
208
        if keyname is None:
209
            # not setting gpg_signing_key at all means we should
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
210
            # use the user email address
7143.15.2 by Jelmer Vernooij
Run autopep8.
211
            keyname = config.extract_email_address(
212
                self._config_stack.get('email'))
7240.9.1 by Jelmer Vernooij
Fix GPG signature validation on Python 3.
213
        if keyname == 'default':
214
            return []
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
215
        possible_keys = self.context.keylist(keyname, secret=True)
216
        try:
6973.10.3 by Jelmer Vernooij
Port GPG to python3.
217
            return [next(possible_keys)]
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
218
        except StopIteration:
219
            return []
220
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
221
    @staticmethod
222
    def verify_signatures_available():
5971.1.82 by Jonathan Riddell
method doc
223
        """
5971.1.86 by Jonathan Riddell
doc string formatting
224
        check if this strategy can verify signatures
225
5971.1.82 by Jonathan Riddell
method doc
226
        :return: boolean if this strategy can verify signatures
227
        """
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
228
        try:
7143.11.1 by Jelmer Vernooij
Remove some unused imports.
229
            import gpg  # noqa: F401
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
230
            return True
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
231
        except ImportError:
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
232
            return False
233
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
234
    def sign(self, content, mode):
7195.4.1 by Jelmer Vernooij
Raise GpgNotInstalled.
235
        try:
236
            import gpg
237
        except ImportError as error:
7195.4.2 by Jelmer Vernooij
Clarify error messages.
238
            raise GpgNotInstalled(
239
                'Set create_signatures=no to disable creating signatures.')
7195.4.1 by Jelmer Vernooij
Raise GpgNotInstalled.
240
7479.2.1 by Jelmer Vernooij
Drop python2 support.
241
        if isinstance(content, str):
2273.1.1 by John Arbash Meinel
``GPGStrategy.sign()`` will now raise ``BzrBadParameterUnicode`` if
242
            raise errors.BzrBadParameterUnicode('content')
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
243
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
244
        plain_text = gpg.Data(content)
1442.1.58 by Robert Collins
gpg signing of content
245
        try:
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
246
            output, result = self.context.sign(
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
247
                plain_text, mode={
248
                    MODE_DETACH: gpg.constants.sig.mode.DETACH,
249
                    MODE_CLEAR: gpg.constants.sig.mode.CLEAR,
250
                    MODE_NORMAL: gpg.constants.sig.mode.NORMAL,
251
                    }[mode])
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
252
        except gpg.errors.GPGMEError as error:
253
            raise SigningFailed(str(error))
7490.158.1 by Jelmer Vernooij
Handle gpg.errors.InvalidSigners.
254
        except gpg.errors.InvalidSigners as error:
255
            raise SigningFailed(str(error))
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
256
257
        return output
5971.1.1 by Jonathan Riddell
add a verify command
258
6883.11.2 by Jelmer Vernooij
Change signature API.
259
    def verify(self, signed_data, signature=None):
5971.1.7 by Jonathan Riddell
add method docs
260
        """Check content has a valid signature.
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
261
6883.11.2 by Jelmer Vernooij
Change signature API.
262
        :param signed_data; Signed data
263
        :param signature: optional signature (if detached)
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
264
6883.11.2 by Jelmer Vernooij
Change signature API.
265
        :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid, plain text
5971.1.7 by Jonathan Riddell
add method docs
266
        """
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
267
        try:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
268
            import gpg
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
269
        except ImportError as error:
7195.4.2 by Jelmer Vernooij
Clarify error messages.
270
            raise GpgNotInstalled(
271
                'Set check_signatures=ignore to disable verifying signatures.')
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
272
6883.11.2 by Jelmer Vernooij
Change signature API.
273
        signed_data = gpg.Data(signed_data)
274
        if signature:
275
            signature = gpg.Data(signature)
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
276
        try:
6883.11.2 by Jelmer Vernooij
Change signature API.
277
            plain_output, result = self.context.verify(signed_data, signature)
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
278
        except gpg.errors.BadSignatures as error:
279
            fingerprint = error.result.signatures[0].fpr
280
            if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_EXPIRED:
7143.15.2 by Jelmer Vernooij
Run autopep8.
281
                expires = self.context.get_key(
282
                    error.result.signatures[0].fpr).subkeys[0].expires
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
283
                if expires > error.result.signatures[0].timestamp:
284
                    # The expired key was not expired at time of signing.
285
                    # test_verify_expired_but_valid()
6883.11.2 by Jelmer Vernooij
Change signature API.
286
                    return SIGNATURE_EXPIRED, fingerprint[-8:], None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
287
                else:
288
                    # I can't work out how to create a test where the signature
289
                    # was expired at the time of signing.
6883.11.2 by Jelmer Vernooij
Change signature API.
290
                    return SIGNATURE_NOT_VALID, None, None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
291
292
            # GPG does not know this key.
293
            # test_verify_unknown_key()
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
294
            if (error.result.signatures[0].summary &
295
                    gpg.constants.SIGSUM_KEY_MISSING):
6883.11.2 by Jelmer Vernooij
Change signature API.
296
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
297
6883.11.2 by Jelmer Vernooij
Change signature API.
298
            return SIGNATURE_NOT_VALID, None, None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
299
        except gpg.errors.GPGMEError as error:
6883.11.2 by Jelmer Vernooij
Change signature API.
300
            raise SignatureVerificationFailed(error)
5971.1.1 by Jonathan Riddell
add a verify command
301
6883.11.3 by Jelmer Vernooij
Fix tests.
302
        # No result if input is invalid.
303
        # test_verify_invalid()
304
        if len(result.signatures) == 0:
305
            return SIGNATURE_NOT_VALID, None, plain_output
306
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
307
        # User has specified a list of acceptable keys, check our result is in
308
        # it.  test_verify_unacceptable_key()
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
309
        fingerprint = result.signatures[0].fpr
5971.1.13 by Jonathan Riddell
return missing if not in acceptable keys
310
        if self.acceptable_keys is not None:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
311
            if fingerprint not in self.acceptable_keys:
6883.11.2 by Jelmer Vernooij
Change signature API.
312
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], plain_output
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
313
        # Yay gpg set the valid bit.
6043.2.15 by Jonathan Riddell
turn comments into sentences
314
        # Can't write a test for this one as you can't set a key to be
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
315
        # trusted using gpg.
316
        if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
5971.1.61 by Jonathan Riddell
make gpgme context global to class
317
            key = self.context.get_key(fingerprint)
5971.1.17 by Jonathan Riddell
add verbose option
318
            name = key.uids[0].name
7240.9.1 by Jelmer Vernooij
Fix GPG signature validation on Python 3.
319
            if isinstance(name, bytes):
320
                name = name.decode('utf-8')
5971.1.18 by Jonathan Riddell
add email to verbose output
321
            email = key.uids[0].email
7240.9.1 by Jelmer Vernooij
Fix GPG signature validation on Python 3.
322
            if isinstance(email, bytes):
323
                email = email.decode('utf-8')
324
            return (SIGNATURE_VALID, name + u" <" + email + u">", plain_output)
6043.2.15 by Jonathan Riddell
turn comments into sentences
325
        # Sigsum_red indicates a problem, unfortunatly I have not been able
326
        # to write any tests which actually set this.
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
327
        if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
6883.11.2 by Jelmer Vernooij
Change signature API.
328
            return SIGNATURE_NOT_VALID, None, plain_output
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
329
        # Summary isn't set if sig is valid but key is untrusted but if user
330
        # has explicity set the key as acceptable we can validate it.
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
331
        if (result.signatures[0].summary == 0 and
332
                self.acceptable_keys is not None):
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
333
            if fingerprint in self.acceptable_keys:
6043.2.15 by Jonathan Riddell
turn comments into sentences
334
                # test_verify_untrusted_but_accepted()
6883.11.2 by Jelmer Vernooij
Change signature API.
335
                return SIGNATURE_VALID, None, plain_output
6043.2.15 by Jonathan Riddell
turn comments into sentences
336
        # test_verify_valid_but_untrusted()
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
337
        if result.signatures[0].summary == 0 and self.acceptable_keys is None:
6883.11.2 by Jelmer Vernooij
Change signature API.
338
            return SIGNATURE_NOT_VALID, None, plain_output
6043.2.15 by Jonathan Riddell
turn comments into sentences
339
        # Other error types such as revoked keys should (I think) be caught by
340
        # SIGSUM_RED so anything else means something is buggy.
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
341
        raise SignatureVerificationFailed(
6609.1.1 by Vincent Ladeuil
Fix the failing gpg test on wily.
342
            "Unknown GnuPG key verification result")
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
343
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
344
    def set_acceptable_keys(self, command_line_input):
6351.3.2 by Jelmer Vernooij
Convert some gpg options to config stacks.
345
        """Set the acceptable keys for verifying with this GPGStrategy.
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
346
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
347
        :param command_line_input: comma separated list of patterns from
348
                                command line
349
        :return: nothing
350
        """
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
351
        patterns = None
6351.3.2 by Jelmer Vernooij
Convert some gpg options to config stacks.
352
        acceptable_keys_config = self._config_stack.get('acceptable_keys')
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
353
        if acceptable_keys_config is not None:
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
354
            patterns = acceptable_keys_config
7143.15.2 by Jelmer Vernooij
Run autopep8.
355
        if command_line_input is not None:  # command line overrides config
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
356
            patterns = command_line_input.split(',')
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
357
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
358
        if patterns:
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
359
            self.acceptable_keys = []
360
            for pattern in patterns:
361
                result = self.context.keylist(pattern)
362
                found_key = False
363
                for key in result:
364
                    found_key = True
365
                    self.acceptable_keys.append(key.subkeys[0].fpr)
366
                    trace.mutter("Added acceptable key: " + key.subkeys[0].fpr)
367
                if not found_key:
6092.2.1 by Jonathan Riddell
Use gettext.NullTranslations in i18n to allow use of i18n even when translations are not turned on
368
                    trace.note(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
369
                        "No GnuPG key results for pattern: {0}"
370
                        ).format(pattern))
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
371
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
372
373
def valid_commits_message(count):
374
    """returns message for number of commits"""
375
    return gettext(u"{0} commits with valid signatures").format(
7143.15.2 by Jelmer Vernooij
Run autopep8.
376
        count[SIGNATURE_VALID])
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
377
378
379
def unknown_key_message(count):
380
    """returns message for number of commits"""
381
    return ngettext(u"{0} commit with unknown key",
382
                    u"{0} commits with unknown keys",
383
                    count[SIGNATURE_KEY_MISSING]).format(
7143.15.2 by Jelmer Vernooij
Run autopep8.
384
        count[SIGNATURE_KEY_MISSING])
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
385
386
387
def commit_not_valid_message(count):
388
    """returns message for number of commits"""
389
    return ngettext(u"{0} commit not valid",
390
                    u"{0} commits not valid",
391
                    count[SIGNATURE_NOT_VALID]).format(
7143.15.2 by Jelmer Vernooij
Run autopep8.
392
        count[SIGNATURE_NOT_VALID])
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
393
394
395
def commit_not_signed_message(count):
396
    """returns message for number of commits"""
397
    return ngettext(u"{0} commit not signed",
398
                    u"{0} commits not signed",
399
                    count[SIGNATURE_NOT_SIGNED]).format(
7143.15.2 by Jelmer Vernooij
Run autopep8.
400
        count[SIGNATURE_NOT_SIGNED])
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
401
402
403
def expired_commit_message(count):
404
    """returns message for number of commits"""
405
    return ngettext(u"{0} commit with key now expired",
406
                    u"{0} commits with key now expired",
407
                    count[SIGNATURE_EXPIRED]).format(
7143.15.2 by Jelmer Vernooij
Run autopep8.
408
        count[SIGNATURE_EXPIRED])
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
409
410
411
def verbose_expired_key_message(result, repo):
412
    """takes a verify result and returns list of expired key info"""
413
    signers = {}
414
    fingerprint_to_authors = {}
415
    for rev_id, validity, fingerprint in result:
416
        if validity == SIGNATURE_EXPIRED:
417
            revision = repo.get_revision(rev_id)
418
            authors = ', '.join(revision.get_apparent_authors())
419
            signers.setdefault(fingerprint, 0)
420
            signers[fingerprint] += 1
421
            fingerprint_to_authors[fingerprint] = authors
422
    result = []
423
    for fingerprint, number in signers.items():
424
        result.append(
425
            ngettext(u"{0} commit by author {1} with key {2} now expired",
426
                     u"{0} commits by author {1} with key {2} now expired",
427
                     number).format(
428
                number, fingerprint_to_authors[fingerprint], fingerprint))
429
    return result
430
431
432
def verbose_valid_message(result):
433
    """takes a verify result and returns list of signed commits strings"""
434
    signers = {}
435
    for rev_id, validity, uid in result:
436
        if validity == SIGNATURE_VALID:
437
            signers.setdefault(uid, 0)
438
            signers[uid] += 1
439
    result = []
440
    for uid, number in signers.items():
7143.15.2 by Jelmer Vernooij
Run autopep8.
441
        result.append(ngettext(u"{0} signed {1} commit",
442
                               u"{0} signed {1} commits",
443
                               number).format(uid, number))
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
444
    return result
445
446
447
def verbose_not_valid_message(result, repo):
448
    """takes a verify result and returns list of not valid commit info"""
449
    signers = {}
450
    for rev_id, validity, empty in result:
451
        if validity == SIGNATURE_NOT_VALID:
452
            revision = repo.get_revision(rev_id)
453
            authors = ', '.join(revision.get_apparent_authors())
454
            signers.setdefault(authors, 0)
455
            signers[authors] += 1
456
    result = []
457
    for authors, number in signers.items():
458
        result.append(ngettext(u"{0} commit by author {1}",
459
                               u"{0} commits by author {1}",
460
                               number).format(number, authors))
461
    return result
462
463
464
def verbose_not_signed_message(result, repo):
465
    """takes a verify result and returns list of not signed commit info"""
466
    signers = {}
467
    for rev_id, validity, empty in result:
468
        if validity == SIGNATURE_NOT_SIGNED:
469
            revision = repo.get_revision(rev_id)
470
            authors = ', '.join(revision.get_apparent_authors())
471
            signers.setdefault(authors, 0)
472
            signers[authors] += 1
473
    result = []
474
    for authors, number in signers.items():
475
        result.append(ngettext(u"{0} commit by author {1}",
476
                               u"{0} commits by author {1}",
477
                               number).format(number, authors))
478
    return result
479
480
481
def verbose_missing_key_message(result):
482
    """takes a verify result and returns list of missing key info"""
483
    signers = {}
484
    for rev_id, validity, fingerprint in result:
485
        if validity == SIGNATURE_KEY_MISSING:
486
            signers.setdefault(fingerprint, 0)
487
            signers[fingerprint] += 1
488
    result = []
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
489
    for fingerprint, number in list(signers.items()):
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
490
        result.append(ngettext(u"Unknown key {0} signed {1} commit",
491
                               u"Unknown key {0} signed {1} commits",
492
                               number).format(fingerprint, number))
493
    return result