From aafc61a2009c1b5c45dcdd6326e9d4010ed7cba4 Mon Sep 17 00:00:00 2001 From: Konstantin Ryabitsev Date: Fri, 21 May 2021 14:50:23 -0400 Subject: Move --show-keys into its own kr subcommand There will be more keyring operations supported in the future, probably, but for now just move the --show-keys subcommand from "mbox" to "kr". Signed-off-by: Konstantin Ryabitsev --- b4/command.py | 32 ++++++++++++----- b4/kr.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ b4/mbox.py | 112 ++++++++++++++-------------------------------------------- man/b4.5 | 33 ++++++++++++++++- man/b4.5.rst | 21 ++++++++++- 5 files changed, 195 insertions(+), 96 deletions(-) create mode 100644 b4/kr.py diff --git a/b4/command.py b/b4/command.py index d3b0c95..3f130c8 100644 --- a/b4/command.py +++ b/b4/command.py @@ -13,23 +13,27 @@ import sys logger = b4.logger -def cmd_mbox_common_opts(sp): +def cmd_retrieval_common_opts(sp): sp.add_argument('msgid', nargs='?', help='Message ID to process, or pipe a raw message') - sp.add_argument('-o', '--outdir', default='.', - help='Output into this directory (or use - to output mailbox contents to stdout)') sp.add_argument('-p', '--use-project', dest='useproject', default=None, help='Use a specific project instead of guessing (linux-mm, linux-hardening, etc)') + sp.add_argument('-m', '--use-local-mbox', dest='localmbox', default=None, + help='Instead of grabbing a thread from lore, process this mbox file (or - for stdin)') + sp.add_argument('-C', '--no-cache', dest='nocache', action='store_true', default=False, + help='Do not use local cache') + + +def cmd_mbox_common_opts(sp): + cmd_retrieval_common_opts(sp) + sp.add_argument('-o', '--outdir', default='.', + help='Output into this directory (or use - to output mailbox contents to stdout)') sp.add_argument('-c', '--check-newer-revisions', dest='checknewer', action='store_true', default=False, help='Check if newer patch revisions exist') sp.add_argument('-n', '--mbox-name', dest='wantname', default=None, help='Filename to name the mbox destination') - sp.add_argument('-m', '--use-local-mbox', dest='localmbox', default=None, - help='Instead of grabbing a thread from lore, process this mbox file (or - for stdin)') sp.add_argument('-M', '--save-as-maildir', dest='maildir', action='store_true', default=False, help='Save as maildir (avoids mbox format ambiguities)') - sp.add_argument('-C', '--no-cache', dest='nocache', action='store_true', default=False, - help='Do not use local cache') def cmd_mbox(cmdargs): @@ -37,6 +41,11 @@ def cmd_mbox(cmdargs): b4.mbox.main(cmdargs) +def cmd_kr(cmdargs): + import b4.kr + b4.kr.main(cmdargs) + + def cmd_am(cmdargs): import b4.mbox b4.mbox.main(cmdargs) @@ -86,8 +95,6 @@ def cmd(): cmd_mbox_common_opts(sp_mbox) sp_mbox.add_argument('-f', '--filter-dupes', dest='filterdupes', action='store_true', default=False, help='When adding messages to existing maildir, filter out duplicates') - sp_mbox.add_argument('--show-keys', dest='showkeys', action='store_true', default=False, - help='Show all developer keys from the thread') sp_mbox.set_defaults(func=cmd_mbox) # b4 am @@ -199,6 +206,13 @@ def cmd(): help='Compare two mbx files prepared with "b4 am"') sp_diff.set_defaults(func=cmd_diff) + # b4 kr + sp_kr = subparsers.add_parser('kr', help='Keyring operations') + cmd_retrieval_common_opts(sp_kr) + sp_kr.add_argument('--show-keys', dest='showkeys', action='store_true', default=False, + help='Show all developer keys found in a thread') + sp_kr.set_defaults(func=cmd_kr) + cmdargs = parser.parse_args() logger.setLevel(logging.DEBUG) diff --git a/b4/kr.py b/b4/kr.py new file mode 100644 index 0000000..9f642bb --- /dev/null +++ b/b4/kr.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2020-2021 by the Linux Foundation +# +__author__ = 'Konstantin Ryabitsev ' + +import os +import sys +import pathlib +import re + +import b4 +import b4.mbox + + +logger = b4.logger + + +def main(cmdargs): + msgid, msgs = b4.mbox.get_msgs(cmdargs) + if cmdargs.showkeys: + logger.info('---') + try: + import patatt + except ModuleNotFoundError: + logger.info('--show-keys requires the patatt library') + sys.exit(1) + + keydata = set() + for msg in msgs: + xdk = msg.get('x-developer-key') + xds = msg.get('x-developer-signature') + if not xdk or not xds: + continue + # grab the selector they used + kdata = b4.LoreMessage.get_parts_from_header(xdk) + sdata = b4.LoreMessage.get_parts_from_header(xds) + algo = kdata.get('a') + identity = kdata.get('i') + selector = sdata.get('s', 'default') + if algo == 'openpgp': + keyinfo = kdata.get('fpr') + elif algo == 'ed25519': + keyinfo = kdata.get('pk') + else: + logger.debug('Unknown key type: %s', algo) + continue + keydata.add((identity, algo, selector, keyinfo)) + + if not keydata: + logger.info('No keys found in the thread.') + sys.exit(0) + krpath = os.path.join(b4.get_data_dir(), 'keyring') + pgp = False + ecc = False + for identity, algo, selector, keyinfo in keydata: + keypath = patatt.make_pkey_path(algo, identity, selector) + fullpath = os.path.join(krpath, keypath) + if os.path.exists(fullpath): + status = 'known' + else: + status = 'unknown' + if algo == 'openpgp': + try: + uids = b4.get_gpg_uids(keyinfo) + if len(uids): + status = 'in default keyring' + except KeyError: + pass + pathlib.Path(os.path.dirname(fullpath)).mkdir(parents=True, exist_ok=True) + + logger.info('%s: (%s)', identity, status) + logger.info(' keytype: %s', algo) + if algo == 'openpgp': + pgp = True + logger.info(' keyid: %s', keyinfo[-16:]) + logger.info(' fpr: %s', ':'.join(re.findall(r'.{4}', keyinfo))) + else: + ecc = True + logger.info(' pubkey: %s', keyinfo) + logger.info(' krpath: %s', keypath) + logger.info(' fullpath: %s', fullpath) + logger.info('---') + if pgp: + logger.info('For openpgp keys:') + logger.info(' gpg --recv-key [keyid]') + logger.info(' gpg -a --export [keyid] > [fullpath]') + if ecc: + logger.info('For ed25519 keys:') + logger.info(' echo [pubkey] > [fullpath]') + + sys.exit(0) diff --git a/b4/mbox.py b/b4/mbox.py index 55b456a..5a1670c 100644 --- a/b4/mbox.py +++ b/b4/mbox.py @@ -24,7 +24,7 @@ import xml.etree.ElementTree import b4 -from typing import Optional +from typing import Optional, Tuple logger = b4.logger @@ -535,12 +535,8 @@ def get_extra_series(msgs: list, direction: int = 1, wantvers: Optional[int] = N return msgs -def main(cmdargs): - if cmdargs.checknewer: - # Force nocache mode - cmdargs.nocache = True - - msgs = list() +def get_msgs(cmdargs) -> Tuple[Optional[str], Optional[list]]: + msgid = None if not cmdargs.localmbox: msgid = b4.get_msgid(cmdargs) if not msgid: @@ -548,13 +544,16 @@ def main(cmdargs): sys.exit(1) pickings = set() - if cmdargs.cherrypick == '_': - # Just that msgid, please - pickings = {msgid} + try: + if cmdargs.cherrypick == '_': + # Just that msgid, please + pickings = {msgid} + except AttributeError: + pass msgs = b4.get_pi_thread_by_msgid(msgid, useproject=cmdargs.useproject, nocache=cmdargs.nocache, onlymsgids=pickings) if not msgs: - return + return None, msgs else: if cmdargs.localmbox == '-': # The entire mbox is passed via stdin, so mailsplit it and use the first message for our msgid @@ -563,7 +562,6 @@ def main(cmdargs): if not len(msgs): logger.critical('Stdin did not contain any messages') sys.exit(1) - msgid = msgs[0].get('Message-ID', None).strip('<>') elif os.path.exists(cmdargs.localmbox): msgid = b4.get_msgid(cmdargs) @@ -583,6 +581,23 @@ def main(cmdargs): logger.critical('Mailbox %s does not exist', cmdargs.localmbox) sys.exit(1) + if not msgid and msgs: + for msg in msgs: + msgid = msg.get('Message-ID', None) + if msgid: + msgid = msgid.strip('<>') + break + + return msgid, msgs + + +def main(cmdargs): + if cmdargs.checknewer: + # Force nocache mode + cmdargs.nocache = True + + msgid, msgs = get_msgs(cmdargs) + if len(msgs) and cmdargs.checknewer: msgs = get_extra_series(msgs, direction=1) @@ -590,79 +605,6 @@ def main(cmdargs): make_am(msgs, cmdargs, msgid) return - if cmdargs.showkeys: - logger.info('---') - try: - import patatt - except ModuleNotFoundError: - logger.info('--show-keys requires the patatt library') - sys.exit(1) - - keydata = set() - for msg in msgs: - xdk = msg.get('x-developer-key') - xds = msg.get('x-developer-signature') - if not xdk or not xds: - continue - # grab the selector they used - kdata = b4.LoreMessage.get_parts_from_header(xdk) - sdata = b4.LoreMessage.get_parts_from_header(xds) - algo = kdata.get('a') - identity = kdata.get('i') - selector = sdata.get('s', 'default') - if algo == 'openpgp': - keyinfo = kdata.get('fpr') - elif algo == 'ed25519': - keyinfo = kdata.get('pk') - else: - logger.debug('Unknown key type: %s', algo) - continue - keydata.add((identity, algo, selector, keyinfo)) - - if not keydata: - logger.info('No keys found in the thread.') - sys.exit(0) - krpath = os.path.join(b4.get_data_dir(), 'keyring') - pgp = False - ecc = False - for identity, algo, selector, keyinfo in keydata: - keypath = patatt.make_pkey_path(algo, identity, selector) - fullpath = os.path.join(krpath, keypath) - if os.path.exists(fullpath): - status = 'known' - else: - status = 'unknown' - if algo == 'openpgp': - try: - uids = b4.get_gpg_uids(keyinfo) - if len(uids): - status = 'in default keyring' - except KeyError: - pass - pathlib.Path(os.path.dirname(fullpath)).mkdir(parents=True, exist_ok=True) - - logger.info('%s: (%s)', identity, status) - logger.info(' keytype: %s', algo) - if algo == 'openpgp': - pgp = True - logger.info(' keyid: %s', keyinfo[-16:]) - logger.info(' fpr: %s', ':'.join(re.findall(r'.{4}', keyinfo))) - else: - ecc = True - logger.info(' pubkey: %s', keyinfo) - logger.info(' krpath: %s', keypath) - logger.info(' fullpath: %s', fullpath) - logger.info('---') - if pgp: - logger.info('For openpgp keys:') - logger.info(' gpg --recv-key [keyid]') - logger.info(' gpg -a --export [keyid] > [fullpath]') - if ecc: - logger.info('For ed25519 keys:') - logger.info(' echo [pubkey] > [fullpath]') - - sys.exit(0) - logger.info('%s messages in the thread', len(msgs)) if cmdargs.outdir == '-': logger.info('---') diff --git a/man/b4.5 b/man/b4.5 index 0edae24..d8e971e 100644 --- a/man/b4.5 +++ b/man/b4.5 @@ -53,9 +53,11 @@ precursor to Lore and Data in the Star Trek universe. .IP \(bu 2 \fIb4 diff\fP: Show range\-diff style diffs between patch versions .IP \(bu 2 -\fIb4 ty\fP: (EXPERIMENTAL) Create templated replies for processed patches and pull requests +\fIb4 ty\fP: Create templated replies for processed patches and pull requests .IP \(bu 2 \fIb4 attest\fP: (EXPERIMENTAL) Add cryptographic attestation to patches +.IP \(bu 2 +\fIb4 kr\fP (EXPERIMENTAL) Operate on patatt\-compatible keyrings .UNINDENT .SH OPTIONS .INDENT 0.0 @@ -325,6 +327,35 @@ Compare two mbx files prepared with "b4 am" .UNINDENT .sp \fIExample\fP: b4 diff \fI\%20200526205322.23465\-1\-mic@digikod.net\fP +.SS b4 kr +.sp +usage: b4 kr [\-h] [\-p USEPROJECT] [\-m LOCALMBOX] [\-C] [\-\-show\-keys] [msgid] +.INDENT 0.0 +.TP +.B positional arguments: +msgid Message ID to process, or pipe a raw message +.TP +.B optional arguments: +.INDENT 7.0 +.TP +.B \-h\fP,\fB \-\-help +show this help message and exit +.TP +.BI \-p \ USEPROJECT\fR,\fB \ \-\-use\-project \ USEPROJECT +Use a specific project instead of guessing (linux\-mm, linux\-hardening, etc) +.TP +.BI \-m \ LOCALMBOX\fR,\fB \ \-\-use\-local\-mbox \ LOCALMBOX +Instead of grabbing a thread from lore, process this mbox file (or \- for stdin) +.TP +.B \-C\fP,\fB \-\-no\-cache +Do not use local cache +.TP +.B \-\-show\-keys +Show all developer keys from the thread +.UNINDENT +.UNINDENT +.sp +\fIExample\fP: b4 kr \-\-show\-keys \fI\%20210521184811.617875\-1\-konstantin@linuxfoundation.org\fP .SH CONFIGURATION .sp B4 configuration is handled via git\-config(1), so you can store it in diff --git a/man/b4.5.rst b/man/b4.5.rst index 2b803e0..ecbd3e8 100644 --- a/man/b4.5.rst +++ b/man/b4.5.rst @@ -31,8 +31,9 @@ SUBCOMMANDS * *b4 am*: Create an mbox file that is ready to git-am * *b4 pr*: Work with pull requests * *b4 diff*: Show range-diff style diffs between patch versions -* *b4 ty*: (EXPERIMENTAL) Create templated replies for processed patches and pull requests +* *b4 ty*: Create templated replies for processed patches and pull requests * *b4 attest*: (EXPERIMENTAL) Add cryptographic attestation to patches +* *b4 kr* (EXPERIMENTAL) Operate on patatt-compatible keyrings OPTIONS ------- @@ -205,6 +206,24 @@ optional arguments: *Example*: b4 diff 20200526205322.23465-1-mic@digikod.net +b4 kr +~~~~~ +usage: b4 kr [-h] [-p USEPROJECT] [-m LOCALMBOX] [-C] [--show-keys] [msgid] + +positional arguments: + msgid Message ID to process, or pipe a raw message + +optional arguments: + -h, --help show this help message and exit + -p USEPROJECT, --use-project USEPROJECT + Use a specific project instead of guessing (linux-mm, linux-hardening, etc) + -m LOCALMBOX, --use-local-mbox LOCALMBOX + Instead of grabbing a thread from lore, process this mbox file (or - for stdin) + -C, --no-cache Do not use local cache + --show-keys Show all developer keys from the thread + +*Example*: b4 kr --show-keys 20210521184811.617875-1-konstantin@linuxfoundation.org + CONFIGURATION ------------- B4 configuration is handled via git-config(1), so you can store it in -- cgit v1.2.3