diff --git a/swh/auth/django/utils.py b/swh/auth/django/utils.py --- a/swh/auth/django/utils.py +++ b/swh/auth/django/utils.py @@ -3,9 +3,11 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information +from datetime import datetime, timedelta from typing import Any, Dict, Optional from swh.auth.django.models import OIDCUser +from swh.auth.keycloak import KeycloakOpenIDConnect def oidc_user_from_decoded_token( @@ -55,3 +57,41 @@ user.sub = decoded_token["sub"] return user + + +def oidc_user_from_profile( + oidc_client: KeycloakOpenIDConnect, oidc_profile: Dict[str, Any] +) -> OIDCUser: + """Initialize an OIDCUser out of an oidc profile dict. + + Args: + oidc_client: KeycloakOpenIDConnect used to discuss with keycloak + oidc_profile: OIDC profile retrieved once connected to keycloak + + Returns: + OIDCUser instance parsed out of the token received. + + """ + + # decode JWT token + decoded_token = oidc_client.decode_token(oidc_profile["access_token"]) + + # create OIDCUser from decoded token + user = oidc_user_from_decoded_token(decoded_token, client_id=oidc_client.client_id) + + # get authentication init datetime + auth_datetime = datetime.fromtimestamp(decoded_token["auth_time"]) + exp_datetime = datetime.fromtimestamp(decoded_token["exp"]) + + # compute OIDC tokens expiration date + oidc_profile["expires_at"] = exp_datetime + oidc_profile["refresh_expires_at"] = auth_datetime + timedelta( + seconds=oidc_profile["refresh_expires_in"] + ) + + # add OIDC profile data to custom User proxy model + for key, val in oidc_profile.items(): + if hasattr(user, key): + setattr(user, key, val) + + return user diff --git a/swh/auth/tests/conftest.py b/swh/auth/tests/conftest.py new file mode 100644 --- /dev/null +++ b/swh/auth/tests/conftest.py @@ -0,0 +1,13 @@ +# Copyright (C) 2021 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU Affero General Public License version 3, or any later version +# See top-level LICENSE file for more information + + +from swh.auth.pytest_plugin import keycloak_mock_factory +from swh.auth.tests.sample_data import CLIENT_ID, REALM_NAME, SERVER_URL + +# keycloak fixture used within tests (cf. test_keycloak.py, test_utils.py) +keycloak_mock = keycloak_mock_factory( + server_url=SERVER_URL, realm_name=REALM_NAME, client_id=CLIENT_ID, +) diff --git a/swh/auth/tests/test_keycloak.py b/swh/auth/tests/test_keycloak.py --- a/swh/auth/tests/test_keycloak.py +++ b/swh/auth/tests/test_keycloak.py @@ -12,22 +12,9 @@ import yaml from swh.auth.keycloak import KeycloakOpenIDConnect -from swh.auth.pytest_plugin import keycloak_mock_factory -from swh.auth.tests.sample_data import ( - CLIENT_ID, - DECODED_TOKEN, - OIDC_PROFILE, - REALM_NAME, - SERVER_URL, - USER_INFO, -) +from swh.auth.tests.sample_data import CLIENT_ID, DECODED_TOKEN, OIDC_PROFILE, USER_INFO from swh.core.config import read -# Make keycloak fixture to use for tests below. -keycloak_mock = keycloak_mock_factory( - server_url=SERVER_URL, realm_name=REALM_NAME, client_id=CLIENT_ID, -) - def test_keycloak_well_known(keycloak_mock): well_known_result = keycloak_mock.well_known() diff --git a/swh/auth/tests/test_utils.py b/swh/auth/tests/test_utils.py --- a/swh/auth/tests/test_utils.py +++ b/swh/auth/tests/test_utils.py @@ -4,9 +4,10 @@ # See top-level LICENSE file for more information from copy import copy +from datetime import datetime -from swh.auth.django.utils import oidc_user_from_decoded_token -from swh.auth.tests.sample_data import CLIENT_ID, DECODED_TOKEN +from swh.auth.django.utils import oidc_user_from_decoded_token, oidc_user_from_profile +from swh.auth.tests.sample_data import CLIENT_ID, DECODED_TOKEN, OIDC_PROFILE def test_oidc_user_from_decoded_token(): @@ -39,3 +40,24 @@ assert user.is_staff is True assert user.permissions == {"read-api"} assert user.sub == "feacd344-b468-4a65-a236-14f61e6b7200" + + +def test_oidc_user_from_profile(keycloak_mock): + date_now = datetime.now() + + user = oidc_user_from_profile(keycloak_mock, OIDC_PROFILE) + + assert user.id == 338521271020811424925120118444075479552 + assert user.username == "johndoe" + assert user.password == "" + assert user.first_name == "John" + assert user.last_name == "Doe" + assert user.email == "john.doe@example.com" + assert user.is_staff is False + assert user.permissions == set() + assert user.sub == "feacd344-b468-4a65-a236-14f61e6b7200" + + assert isinstance(user.expires_at, datetime) + assert date_now <= user.expires_at + assert isinstance(user.refresh_expires_at, datetime) + assert date_now <= user.refresh_expires_at