Page MenuHomeSoftware Heritage

D5955.id21384.diff
No OneTemporary

D5955.id21384.diff

diff --git a/conftest.py b/conftest.py
new file mode 100644
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1 @@
+pytest_plugins = ["swh.auth.pytest_plugin"]
diff --git a/docs/index.rst b/docs/index.rst
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -17,13 +17,13 @@
.. code-block:: text
- $ swh web auth
- Usage: swh web auth [OPTIONS] COMMAND [ARGS]...
+ $ swh auth
+ Usage: swh auth [OPTIONS] COMMAND [ARGS]...
- Authenticate Software Heritage users with OpenID Connect.
+ Software Heritage Authentication tools.
- This CLI tool eases the retrieval of bearer tokens to authenticate a user
- querying the Software Heritage Web API.
+ This CLI eases the retrieval of a bearer token to authenticate a user
+ querying Software Heritage Web APIs.
Options:
--oidc-server-url TEXT URL of OpenID Connect server (default to
@@ -38,10 +38,8 @@
-h, --help Show this message and exit.
Commands:
- generate-token Generate a new bearer token for Web API authentication.
- login Alias for 'generate-token'
- logout Alias for 'revoke-token'
- revoke-token Revoke a bearer token used for Web API authentication.
+ generate-token Generate a new bearer token for a Web API authentication.
+ revoke-token Revoke a bearer token used for a Web API authentication.
In order to get your tokens, you need to use the ``generate-token`` subcommand of
the CLI tool by passing your username as argument. You will be prompted
@@ -50,7 +48,7 @@
.. code-block:: text
- $ swh web auth generate-token <username>
+ $ swh auth --client-id swh-web generate-token <username>
Password:
eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmNjMzMD...
@@ -81,7 +79,7 @@
.. code-block:: text
- $ swh web auth revoke-token $REFRESH_TOKEN
+ $ swh auth --client-id swh-web revoke-token $REFRESH_TOKEN
Token successfully revoked.
API Reference
diff --git a/requirements-swh.txt b/requirements-swh.txt
--- a/requirements-swh.txt
+++ b/requirements-swh.txt
@@ -1,3 +1,4 @@
# Add here internal Software Heritage dependencies, one per line.
+swh.auth >= 0.6
swh.core >= 0.3
swh.model
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -69,6 +69,6 @@
},
entry_points="""
[swh.cli.subcommands]
- auth=swh.web.client.cli
+ web=swh.web.client.cli
""",
)
diff --git a/swh/web/client/auth.py b/swh/web/client/auth.py
deleted file mode 100644
--- a/swh/web/client/auth.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright (C) 2020 The Software Heritage developers
-# See the AUTHORS file at the top-level directory of this distribution
-# License: GNU General Public License version 3, or any later version
-# See top-level LICENSE file for more information
-
-from typing import Any, Dict
-from urllib.parse import urljoin
-
-import requests
-
-SWH_OIDC_SERVER_URL = "https://auth.softwareheritage.org/auth/"
-SWH_REALM_NAME = "SoftwareHeritage"
-SWH_WEB_CLIENT_ID = "swh-web"
-
-
-class AuthenticationError(Exception):
- """Authentication related error.
-
- Example: A bearer token has been revoked.
-
- """
-
- pass
-
-
-class OpenIDConnectSession:
- """
- Simple class wrapping requests sent to an OpenID Connect server.
-
- Args:
- oidc_server_url: URL of OpenID Connect server
- realm_name: name of the OpenID Connect authentication realm
- client_id: OpenID Connect client identifier in the realm
- """
-
- def __init__(
- self,
- oidc_server_url: str = SWH_OIDC_SERVER_URL,
- realm_name: str = SWH_REALM_NAME,
- client_id: str = SWH_WEB_CLIENT_ID,
- ):
- realm_url = urljoin(oidc_server_url, f"realms/{realm_name}/")
- self.client_id = client_id
- self.token_url = urljoin(realm_url, "protocol/openid-connect/token/")
- self.logout_url = urljoin(realm_url, "protocol/openid-connect/logout/")
-
- def login(self, username: str, password: str) -> Dict[str, Any]:
- """
- Login and create new offline OpenID Connect session.
-
- Args:
- username: an existing username in the realm
- password: password associated to username
-
- Returns:
- The OpenID Connect session info
- """
- return requests.post(
- url=self.token_url,
- data={
- "grant_type": "password",
- "client_id": self.client_id,
- "scope": "openid offline_access",
- "username": username,
- "password": password,
- },
- ).json()
-
- def logout(self, token: str):
- """
- Logout from an offline OpenID Connect session and invalidate
- previously emitted tokens.
-
- Args:
- token: a bearer token retrieved after login
- """
- requests.post(
- url=self.logout_url,
- data={
- "client_id": self.client_id,
- "scope": "openid",
- "refresh_token": token,
- },
- )
diff --git a/swh/web/client/cli.py b/swh/web/client/cli.py
--- a/swh/web/client/cli.py
+++ b/swh/web/client/cli.py
@@ -11,6 +11,9 @@
import click
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
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
@@ -178,48 +181,25 @@
print(json.dumps(processed_origins))
-@web.group(name="auth", context_settings=CONTEXT_SETTINGS)
-@click.option(
- "--oidc-server-url",
- "oidc_server_url",
- default="https://auth.softwareheritage.org/auth/",
- 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")'),
-)
+def _forward_context(ctx: Context, *args, **kwargs):
+ ctx.forward(*args, **kwargs)
+
+
+@web.group(name="auth", context_settings=CONTEXT_SETTINGS, deprecated=True)
@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.
This CLI tool eases the retrieval of a bearer token to authenticate
a user querying the Software Heritage Web API.
- """
- from swh.web.client.auth import OpenIDConnectSession
- ctx.ensure_object(dict)
- ctx.obj["oidc_session"] = OpenIDConnectSession(
- oidc_server_url, realm_name, client_id
- )
+ That command group is deprecated, use ``swh auth`` instead.
+ """
+ _forward_context(ctx, auth_cli, client_id="swh-web")
-@auth.command("generate-token")
+@auth.command("generate-token", deprecated=True)
@click.argument("username")
@click.pass_context
def generate_token(ctx: Context, username: str):
@@ -236,28 +216,10 @@
token has a much longer expiration time than classical OIDC
sessions (usually several dozens of days).
"""
- from getpass import getpass
-
- 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)
+ _forward_context(ctx, auth_generate_token, username=username)
-@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.pass_context
def revoke_token(ctx: Context, token: str):
@@ -268,15 +230,4 @@
The token is definitely revoked after that operation.
"""
- ctx.obj["oidc_session"].logout(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)
+ _forward_context(ctx, auth_revoke_token, token=token)
diff --git a/swh/web/client/tests/test_cli.py b/swh/web/client/tests/test_cli.py
--- a/swh/web/client/tests/test_cli.py
+++ b/swh/web/client/tests/test_cli.py
@@ -8,53 +8,37 @@
from click.testing import CliRunner
-from swh.web.client.cli import auth, web
+from swh.web.client.cli import auth_cli, auth_generate_token, auth_revoke_token, web
runner = CliRunner()
-oidc_profile = {
- "access_token": "some-access-token",
- "expires_in": 600,
- "refresh_expires_in": 0,
- "refresh_token": "some-refresh-token",
- "token_type": "bearer",
- "session_state": "some-state",
- "scope": "openid email profile offline_access",
-}
-
def test_auth_generate_token(mocker):
- mock_getpass = mocker.patch("getpass.getpass")
- mock_getpass.return_value = "password"
- mock_oidc_session = mocker.patch("swh.web.client.auth.OpenIDConnectSession")
- mock_login = mock_oidc_session.return_value.login
- mock_login.return_value = oidc_profile
-
- for command in ("generate-token", "login"):
- mock_login.side_effect = None
- result = runner.invoke(auth, [command, "username"], input="password\n")
- assert result.exit_code == 0
- assert oidc_profile["refresh_token"] in result.output
-
- mock_login.side_effect = Exception("Auth error")
-
- result = runner.invoke(auth, [command, "username"], input="password\n")
- assert result.exit_code == 1
+ forward_context = mocker.patch("swh.web.client.cli._forward_context")
+ runner.invoke(web, ["auth", "generate-token", "username"])
+ assert forward_context.call_count == 2
+ ctx = forward_context.call_args_list[0][0][0]
+ ctx2 = forward_context.call_args_list[1][0][0]
+ forward_context.assert_has_calls(
+ [
+ mocker.call(ctx, auth_cli, client_id="swh-web"),
+ mocker.call(ctx2, auth_generate_token, username="username"),
+ ]
+ )
def test_auth_revoke_token(mocker):
-
- mock_oidc_session = mocker.patch("swh.web.client.auth.OpenIDConnectSession")
- mock_logout = mock_oidc_session.return_value.logout
-
- for command in ("revoke-token", "logout"):
- mock_logout.side_effect = None
- result = runner.invoke(auth, [command, oidc_profile["refresh_token"]])
- assert result.exit_code == 0
-
- mock_logout.side_effect = Exception("Auth error")
- result = runner.invoke(auth, [command, oidc_profile["refresh_token"]])
- assert result.exit_code == 1
+ forward_context = mocker.patch("swh.web.client.cli._forward_context")
+ runner.invoke(web, ["auth", "revoke-token", "token"])
+ assert forward_context.call_count == 2
+ ctx = forward_context.call_args_list[0][0][0]
+ ctx2 = forward_context.call_args_list[1][0][0]
+ forward_context.assert_has_calls(
+ [
+ mocker.call(ctx, auth_cli, client_id="swh-web"),
+ mocker.call(ctx2, auth_revoke_token, token="token"),
+ ]
+ )
def test_save_code_now_through_cli(mocker, web_api_mock, tmp_path, cli_config_path):

File Metadata

Mime Type
text/plain
Expires
Wed, Jul 2, 10:41 AM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3217141

Event Timeline