# HG changeset patch # User Goffi # Date 1665859110 -7200 # Node ID 8ae3e870be9429fa9643e98d8eb5d88839f3e7e4 # Parent 6939594ba77e5d59cb6720cee8ef682bc691b3e2 cli (pubsub): new `secret` subcommands: commands are added to `share`, `revoke` and `rotate` secrets of an encrypted pubsub node. rel 380 diff -r 6939594ba77e -r 8ae3e870be94 sat_frontends/jp/cmd_pubsub.py --- a/sat_frontends/jp/cmd_pubsub.py Sat Oct 15 20:37:00 2022 +0200 +++ b/sat_frontends/jp/cmd_pubsub.py Sat Oct 15 20:38:30 2022 +0200 @@ -2563,6 +2563,166 @@ ) +class SecretShare(base.CommandBase): + def __init__(self, host): + super().__init__( + host, + "share", + use_pubsub=True, + pubsub_flags={C.NODE}, + help=_("share a secret to let other entity encrypt or decrypt items"), + ) + + def add_parser_options(self): + self.parser.add_argument( + "-k", "--key", metavar="ID", dest="secret_ids", action="append", default=[], + help=_( + "only share secrets with those IDs (default: share all secrets of the " + "node)" + ) + ) + self.parser.add_argument( + "recipient", metavar="JID", help=_("entity who must get the shared secret") + ) + + async def start(self): + try: + await self.host.bridge.psSecretShare( + self.args.recipient, + self.args.service, + self.args.node, + self.args.secret_ids, + self.profile, + ) + except Exception as e: + self.disp(f"can't share secret: {e}", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + else: + self.disp("secrets have been shared") + self.host.quit(C.EXIT_OK) + + +class SecretRevoke(base.CommandBase): + def __init__(self, host): + super().__init__( + host, + "revoke", + use_pubsub=True, + pubsub_flags={C.NODE}, + help=_("revoke an encrypted node secret"), + ) + + def add_parser_options(self): + self.parser.add_argument( + "secret_id", help=_("ID of the secrets to revoke") + ) + self.parser.add_argument( + "-r", "--recipient", dest="recipients", metavar="JID", action="append", + default=[], help=_( + "entity who must get the revocation notification (default: send to all " + "entities known to have the shared secret)" + ) + ) + + async def start(self): + try: + await self.host.bridge.psSecretRevoke( + self.args.service, + self.args.node, + self.args.secret_id, + self.args.recipients, + self.profile, + ) + except Exception as e: + self.disp(f"can't revoke secret: {e}", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + else: + self.disp("secret {self.args.secret_id} has been revoked.") + self.host.quit(C.EXIT_OK) + + +class SecretRotate(base.CommandBase): + def __init__(self, host): + super().__init__( + host, + "rotate", + use_pubsub=True, + pubsub_flags={C.NODE}, + help=_("revoke existing secrets, create a new one and send notifications"), + ) + + def add_parser_options(self): + self.parser.add_argument( + "-r", "--recipient", dest="recipients", metavar="JID", action="append", + default=[], help=_( + "entity who must get the revocation and shared secret notifications " + "(default: send to all entities known to have the shared secret)" + ) + ) + + async def start(self): + try: + await self.host.bridge.psSecretRotate( + self.args.service, + self.args.node, + self.args.recipients, + self.profile, + ) + except Exception as e: + self.disp(f"can't rotate secret: {e}", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + else: + self.disp("secret has been rotated") + self.host.quit(C.EXIT_OK) + + +class SecretList(base.CommandBase): + def __init__(self, host): + super().__init__( + host, + "list", + use_pubsub=True, + use_verbose=True, + pubsub_flags={C.NODE}, + help=_("list known secrets for a pubsub node"), + use_output=C.OUTPUT_LIST_DICT + ) + + def add_parser_options(self): + pass + + async def start(self): + try: + secrets = data_format.deserialise(await self.host.bridge.psSecretsList( + self.args.service, + self.args.node, + self.profile, + ), type_check=list) + except Exception as e: + self.disp(f"can't list node secrets: {e}", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + else: + if not self.verbosity: + # we don't print key if verbosity is not a least one, to avoid showing it + # on the screen accidentally + for secret in secrets: + del secret["key"] + await self.output(secrets) + self.host.quit(C.EXIT_OK) + + +class Secret(base.CommandBase): + subcommands = (SecretShare, SecretRevoke, SecretRotate, SecretList) + + def __init__(self, host): + super().__init__( + host, + "secret", + use_profile=False, + help=_("handle encrypted nodes secrets"), + ) + + class HookCreate(base.CommandBase): def __init__(self, host): base.CommandBase.__init__( @@ -2725,6 +2885,7 @@ Search, Transform, Attachments, + Secret, Hook, Uri, Node,