diff --git a/doc/commands/logout.rst b/doc/commands/logout.rst index 0e31143c..37e30143 100644 --- a/doc/commands/logout.rst +++ b/doc/commands/logout.rst @@ -12,7 +12,9 @@ nextstrain logout .. code-block:: none - usage: nextstrain logout [-h] [] + usage: nextstrain logout [] + nextstrain logout --all + nextstrain logout --help Log out of Nextstrain.org (and other remotes) by deleting locally-saved @@ -45,3 +47,7 @@ options show this help message and exit +.. option:: --all + + Log out of all remotes for which there are locally-saved credentials + diff --git a/nextstrain/cli/authn/__init__.py b/nextstrain/cli/authn/__init__.py index fde7e04f..57539946 100644 --- a/nextstrain/cli/authn/__init__.py +++ b/nextstrain/cli/authn/__init__.py @@ -145,6 +145,29 @@ def logout(origin: Origin): print(f"Not logged in to {origin}.", file = stderr) +def logout_all(): + """ + Remove **all** locally-saved credentials. + + Equivalent to calling :func:`logout` on all origins found in the secrets + file. + """ + with config.write_lock(): + secrets = config.load(config.SECRETS) + + sections = [ + (section, _parse_section(section)) + for section in secrets + if _parse_section(section) ] + + for section, origin in sections: + del secrets[section] + print(f"Credentials for {origin} removed from {config.SECRETS}.", file = stderr) + print(f"Logged out of {origin}.", file = stderr) + + config.save(secrets, config.SECRETS) + + def current_user(origin: Origin) -> Optional[User]: """ Information about the currently logged in user for *origin*, if any. @@ -237,3 +260,12 @@ def _config_section(origin: Origin) -> str: if origin == "https://nextstrain.org": return CONFIG_SECTION return f"{CONFIG_SECTION} {origin}" + + +def _parse_section(section: str) -> Optional[Origin]: + if section == CONFIG_SECTION: + return Origin("https://nextstrain.org") + elif section.startswith(CONFIG_SECTION + " "): + return Origin(section.split(" ", 1)[1]) + else: + return None diff --git a/nextstrain/cli/command/logout.py b/nextstrain/cli/command/logout.py index b05c8eca..6dd2be26 100644 --- a/nextstrain/cli/command/logout.py +++ b/nextstrain/cli/command/logout.py @@ -9,10 +9,16 @@ Nextstrain.org (or other remotes). """ from inspect import cleandoc +from .. import authn from ..remote import parse_remote_path def register_parser(subparser): + """ + %(prog)s [] + %(prog)s --all + %(prog)s --help + """ parser = subparser.add_parser("logout", help = "Log out of Nextstrain.org (and other remotes)") parser.add_argument( @@ -27,14 +33,20 @@ def register_parser(subparser): nargs = "?", default = "nextstrain.org") - # XXX TODO: Supporting `nextstrain logout --all` would be nice. - # -trs, 15 Nov 2023 + parser.add_argument( + "--all", + help = "Log out of all remotes for which there are locally-saved credentials", + action = "store_true") return parser def run(opts): - remote, url = parse_remote_path(opts.remote) - assert url.origin + if opts.all: + authn.logout_all() + + else: + remote, url = parse_remote_path(opts.remote) + assert url.origin - remote.logout(url.origin) + remote.logout(url.origin)