diff sat_frontends/jp/cmd_pubsub.py @ 3939:8ae3e870be94

cli (pubsub): new `secret` subcommands: commands are added to `share`, `revoke` and `rotate` secrets of an encrypted pubsub node. rel 380
author Goffi <goffi@goffi.org>
date Sat, 15 Oct 2022 20:38:30 +0200
parents 6939594ba77e
children 08c1d5485411
line wrap: on
line diff
--- 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,