def main(key_path, test_name): with subprocess.Popen(['gpg', '--no-default-keyring', '--keyring', key_path, '--list-key', '--with-colons'], stdout=subprocess.PIPE) as s: key_colons, _ = s.communicate() assert s.wait() == 0 key_colons = key_colons.decode('ASCII') with io.StringIO(key_colons) as f: key_cls = process_gnupg_colons(f) assert len(key_cls) == 1 key_cls = key_cls[0] results = {} for k, spec in SPECS.items(): results[k] = check_key(key_cls, spec) print(''' class {test_name}(tests.key_base.BaseKeyTest): KEY_FILE = '{key_file}' GPG_COLONS = \'\'\' {gpg_colons}\'\'\' KEY = {key_cls} EXPECTED_RESULTS = {expected}'''.format( test_name=test_name, key_file=os.path.relpath(key_path, 'tests'), gpg_colons=key_colons, key_cls=pretty_key(key_cls), expected=pretty_results(results)))
def test_key_class(self): """ Test the key using predefined Key class. """ keys = [self.KEY] with unittest.mock.patch("datetime.datetime", PatchedDateTime): for spec, expected in self.EXPECTED_RESULTS.items(): with self.subTest(spec): self.assertListEqual(expected, list(clear_long_descs( check_key(keys[0], SPECS[spec]))))
def test_colons(self): """ Test the key using provided 'gpg --with-colons' output. """ keys = process_gnupg_colons(io.StringIO(self.GPG_COLONS)) assert len(keys) == 1 with unittest.mock.patch("datetime.datetime", PatchedDateTime): for spec, expected in self.EXPECTED_RESULTS.items(): with self.subTest(spec): self.assertListEqual(expected, list(clear_long_descs( check_key(keys[0], SPECS[spec]))))
def test_integration(self): """ Test the key using local installed GnuPG. """ if not get_gnupg_version(): raise unittest.SkipTest('GnuPG executable not found') keypath = os.path.join(os.path.dirname(__file__), self.KEY_FILE) with unittest.mock.patch("glep63.gnupg.subprocess.Popen", FakeTimePopen): keys = process_gnupg_key(keyrings=[keypath]) assert len(keys) == 1 with unittest.mock.patch("datetime.datetime", PatchedDateTime): for spec, expected in self.EXPECTED_RESULTS.items(): with self.subTest(spec): self.assertListEqual(expected, list(clear_long_descs( check_key(keys[0], SPECS[spec]))))
def main(): argp = argparse.ArgumentParser() act = argp.add_mutually_exclusive_group(required=True) act.add_argument('-a', '--all', action='store_true', help='Verify all public keys in the local keyring') act.add_argument('-d', '--developers', action='store_true', help='Fetch and verify keys for gentoo.git committers') act.add_argument('-D', '--all-developers', action='store_true', help='Fetch and verify keys for all Gentoo developers') act.add_argument( '-G', '--gnupg', nargs='+', metavar='FILE', type=argparse.FileType('r', encoding='UTF-8'), help='Process "gpg --with-colons" output from FILE(s) ("-" for stdin)') act.add_argument( '-k', '--key-id', nargs='+', help='Check local GnuPG keys matching specified query (IDs, names)') act.add_argument( '-K', '--keyring', nargs='+', help='Check all keys in specified keyrings (gpg --keyring syntax)') argp.add_argument('-S', '--spec', choices=SPECS, default=DEFAULT_SPEC, help='Spec to verify against') argp.add_argument('-e', '--errors-only', action='store_true', help='Print only errors (skip warnings)') argp.add_argument( '-i', '--ignore-extraneous-keys', action='store_true', help='Skip developers who have at least one good key (by UID)') argp.add_argument( '-m', '--machine-readable', action='store_true', help='Print only machine-readable data (skip human-readable desc)') argp.add_argument('-N', '--no-name', action='store_true', help='Print only e-mail addresses as UIDs') argp.add_argument( '-w', '--warnings-as-errors', action='store_true', help='Treat warnings as errors (return unsucessfully if any)') opts = argp.parse_args() keys = [] if opts.developers or opts.all_developers: keyring_url = ('https://qa-reports.gentoo.org/output/{}.gpg'.format( 'committing-devs' if opts.developers else 'active-devs')) with urllib.request.urlopen(keyring_url) as f: with tempfile.NamedTemporaryFile() as tmpf: shutil.copyfileobj(f, tmpf) tmpf.flush() keys.extend(process_gnupg_key([tmpf.name], opts.key_id)) elif opts.key_id is not None or opts.all or opts.keyring is not None: keys.extend(process_gnupg_key(opts.keyring, opts.key_id)) elif opts.gnupg is not None: for f in opts.gnupg: keys.extend(process_gnupg_colons(f)) out = [] for k in keys: keyret = check_key(k, SPECS[opts.spec]) if not keyret and opts.ignore_extraneous_keys: keyret = [GoodKey(k)] out.extend(keyret) ret = 0 good_devs = set() msgs = [] for i in out: # figure out a primary UID, preferring @gentoo.org primary_uid = i.key.uids[0].user_id for x in i.key.uids: if '@gentoo.org' in x.user_id: primary_uid = x.user_id break _, uid_addr = email.utils.parseaddr(primary_uid) if isinstance(i, GoodKey): good_devs.add(uid_addr) continue keyid = i.key.keyid if hasattr(i, 'subkey'): keyid += ':' + i.subkey.keyid elif hasattr(i, 'uid'): if opts.no_name: _, uid_fmt = email.utils.parseaddr(i.uid_user_id) else: uid_fmt = i.uid.user_id keyid += ':[{}]'.format(uid_fmt) if type(i) in FAIL: ret |= 1 cls = '[E]' else: assert type(i) in WARN cls = '[W]' if opts.errors_only: continue if opts.warnings_as_errors: ret |= 2 if opts.machine_readable: msg = [keyid, i.machine_desc] else: msg = [ keyid, '[{}]'.format(uid_addr if opts.no_name else primary_uid), cls, i.machine_desc, i.long_desc ] msgs.append((uid_addr, msg)) for addr, msg in msgs: if addr not in good_devs: print(' '.join(msg)) return ret