Changeset View
Changeset View
Standalone View
Standalone View
swh/web/client/cli.py
# Copyright (C) 2020-2021 The Software Heritage developers | # Copyright (C) 2020-2021 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import os | import os | ||||
from typing import Any, Dict, List | from typing import Any, Dict, List | ||||
# WARNING: do not import unnecessary things here to keep cli startup time under | # WARNING: do not import unnecessary things here to keep cli startup time under | ||||
# control | # control | ||||
import click | import click | ||||
from click.core import Context | from click.core import Context | ||||
from swh.auth.cli import auth as auth_cli | |||||
from swh.auth.cli import generate_token as auth_generate_token | |||||
from swh.auth.cli import revoke_token as auth_revoke_token | |||||
from swh.core.cli import swh as swh_cli_group | from swh.core.cli import swh as swh_cli_group | ||||
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) | CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) | ||||
# TODO (T1410): All generic config code should reside in swh.core.config | # TODO (T1410): All generic config code should reside in swh.core.config | ||||
DEFAULT_CONFIG_PATH = os.environ.get( | DEFAULT_CONFIG_PATH = os.environ.get( | ||||
"SWH_CONFIG_FILE", os.path.join(click.get_app_dir("swh"), "global.yml") | "SWH_CONFIG_FILE", os.path.join(click.get_app_dir("swh"), "global.yml") | ||||
) | ) | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | for origin in sys.stdin: | ||||
except Exception as e: | except Exception as e: | ||||
logging.warning( | logging.warning( | ||||
"Issue for origin (%s, %s)\n%s", origin, visit_type, e, | "Issue for origin (%s, %s)\n%s", origin, visit_type, e, | ||||
) | ) | ||||
logging.debug("Origin saved: %s", len(processed_origins)) | logging.debug("Origin saved: %s", len(processed_origins)) | ||||
print(json.dumps(processed_origins)) | print(json.dumps(processed_origins)) | ||||
@web.group(name="auth", context_settings=CONTEXT_SETTINGS) | def _forward_context(ctx: Context, *args, **kwargs): | ||||
@click.option( | ctx.forward(*args, **kwargs) | ||||
"--oidc-server-url", | |||||
"oidc_server_url", | |||||
default="https://auth.softwareheritage.org/auth/", | @web.group(name="auth", context_settings=CONTEXT_SETTINGS, deprecated=True) | ||||
help=( | |||||
"URL of OpenID Connect server (default to " | |||||
'"https://auth.softwareheritage.org/auth/")' | |||||
), | |||||
) | |||||
@click.option( | |||||
"--realm-name", | |||||
"realm_name", | |||||
default="SoftwareHeritage", | |||||
help=( | |||||
"Name of the OpenID Connect authentication realm " | |||||
'(default to "SoftwareHeritage")' | |||||
), | |||||
) | |||||
@click.option( | |||||
"--client-id", | |||||
"client_id", | |||||
default="swh-web", | |||||
help=("OpenID Connect client identifier in the realm " '(default to "swh-web")'), | |||||
) | |||||
@click.pass_context | @click.pass_context | ||||
def auth(ctx: Context, oidc_server_url: str, realm_name: str, client_id: str): | def auth(ctx: Context): | ||||
""" | """ | ||||
Authenticate Software Heritage users with OpenID Connect. | Authenticate Software Heritage users with OpenID Connect. | ||||
This CLI tool eases the retrieval of a bearer token to authenticate | This CLI tool eases the retrieval of a bearer token to authenticate | ||||
a user querying the Software Heritage Web API. | a user querying the Software Heritage Web API. | ||||
""" | |||||
from swh.web.client.auth import OpenIDConnectSession | |||||
ctx.ensure_object(dict) | That command group is deprecated, use ``swh auth`` instead. | ||||
ctx.obj["oidc_session"] = OpenIDConnectSession( | """ | ||||
oidc_server_url, realm_name, client_id | _forward_context(ctx, auth_cli, client_id="swh-web") | ||||
) | |||||
@auth.command("generate-token") | @auth.command("generate-token", deprecated=True) | ||||
@click.argument("username") | @click.argument("username") | ||||
@click.pass_context | @click.pass_context | ||||
def generate_token(ctx: Context, username: str): | def generate_token(ctx: Context, username: str): | ||||
""" | """ | ||||
Generate a new bearer token for Web API authentication. | Generate a new bearer token for Web API authentication. | ||||
Login with USERNAME, create a new OpenID Connect session and get | Login with USERNAME, create a new OpenID Connect session and get | ||||
bearer token. | bearer token. | ||||
User will be prompted for his password and token will be printed | User will be prompted for his password and token will be printed | ||||
to standard output. | to standard output. | ||||
The created OpenID Connect session is an offline one so the provided | The created OpenID Connect session is an offline one so the provided | ||||
token has a much longer expiration time than classical OIDC | token has a much longer expiration time than classical OIDC | ||||
sessions (usually several dozens of days). | sessions (usually several dozens of days). | ||||
""" | """ | ||||
from getpass import getpass | _forward_context(ctx, auth_generate_token, username=username) | ||||
password = getpass() | |||||
oidc_info = ctx.obj["oidc_session"].login(username, password) | |||||
if "refresh_token" in oidc_info: | |||||
print(oidc_info["refresh_token"]) | |||||
else: | |||||
print(oidc_info) | |||||
@auth.command("login", deprecated=True) | |||||
@click.argument("username") | |||||
@click.pass_context | |||||
def login(ctx: Context, username: str): | |||||
""" | |||||
Alias for 'generate-token' | |||||
""" | |||||
ctx.forward(generate_token) | |||||
@auth.command("revoke-token") | @auth.command("revoke-token", deprecated=True) | ||||
@click.argument("token") | @click.argument("token") | ||||
@click.pass_context | @click.pass_context | ||||
def revoke_token(ctx: Context, token: str): | def revoke_token(ctx: Context, token: str): | ||||
""" | """ | ||||
Revoke a bearer token used for Web API authentication. | Revoke a bearer token used for Web API authentication. | ||||
Use TOKEN to logout from an offline OpenID Connect session. | Use TOKEN to logout from an offline OpenID Connect session. | ||||
The token is definitely revoked after that operation. | The token is definitely revoked after that operation. | ||||
""" | """ | ||||
ctx.obj["oidc_session"].logout(token) | _forward_context(ctx, auth_revoke_token, token=token) | ||||
print("Token successfully revoked.") | |||||
@auth.command("logout", deprecated=True) | |||||
@click.argument("token") | |||||
@click.pass_context | |||||
def logout(ctx: Context, token: str): | |||||
""" | |||||
Alias for 'revoke-token' | |||||
""" | |||||
ctx.forward(revoke_token) |