diff --git a/tests/conftest.py b/tests/conftest.py index ada9820..6023e51 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +"""Fixtures for tests.""" + import os import uuid @@ -7,6 +9,18 @@ from keycloak import KeycloakAdmin, KeycloakOpenID class KeycloakTestEnv(object): + """Wrapper for test Keycloak connection configuration. + + :param host: Hostname + :type host: str + :param port: Port + :type port: str + :param username: Admin username + :type username: str + :param password: Admin password + :type password: str + """ + def __init__( self, host: str = os.environ["KEYCLOAK_HOST"], @@ -14,6 +28,7 @@ class KeycloakTestEnv(object): username: str = os.environ["KEYCLOAK_ADMIN"], password: str = os.environ["KEYCLOAK_ADMIN_PASSWORD"], ): + """Init method.""" self.KEYCLOAK_HOST = host self.KEYCLOAK_PORT = port self.KEYCLOAK_ADMIN = username @@ -21,44 +36,54 @@ class KeycloakTestEnv(object): @property def KEYCLOAK_HOST(self): + """Hostname getter.""" return self._KEYCLOAK_HOST @KEYCLOAK_HOST.setter def KEYCLOAK_HOST(self, value: str): + """Hostname setter.""" self._KEYCLOAK_HOST = value @property def KEYCLOAK_PORT(self): + """Port getter.""" return self._KEYCLOAK_PORT @KEYCLOAK_PORT.setter def KEYCLOAK_PORT(self, value: str): + """Port setter.""" self._KEYCLOAK_PORT = value @property def KEYCLOAK_ADMIN(self): + """Admin username getter.""" return self._KEYCLOAK_ADMIN @KEYCLOAK_ADMIN.setter def KEYCLOAK_ADMIN(self, value: str): + """Admin username setter.""" self._KEYCLOAK_ADMIN = value @property def KEYCLOAK_ADMIN_PASSWORD(self): + """Admin password getter.""" return self._KEYCLOAK_ADMIN_PASSWORD @KEYCLOAK_ADMIN_PASSWORD.setter def KEYCLOAK_ADMIN_PASSWORD(self, value: str): + """Admin password setter.""" self._KEYCLOAK_ADMIN_PASSWORD = value @pytest.fixture def env(): + """Fixture for getting the test environment configuration object.""" return KeycloakTestEnv() @pytest.fixture def admin(env: KeycloakTestEnv): + """Fixture for initialized KeycloakAdmin class.""" return KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -68,11 +93,20 @@ def admin(env: KeycloakTestEnv): @pytest.fixture def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + """Fixture for initialized KeycloakOpenID class.""" # Set the realm admin.realm_name = realm # Create client client = str(uuid.uuid4()) - client_id = admin.create_client(payload={"name": client, "clientId": client}) + client_id = admin.create_client( + payload={ + "name": client, + "clientId": client, + "enabled": True, + "publicClient": True, + "protocol": "openid-connect", + } + ) # Return OID yield KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -83,16 +117,61 @@ def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): admin.delete_client(client_id=client_id) +@pytest.fixture +def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): + """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + # Set the realm + admin.realm_name = realm + # Create client + client = str(uuid.uuid4()) + client_id = admin.create_client( + payload={ + "name": client, + "clientId": client, + "enabled": True, + "publicClient": True, + "protocol": "openid-connect", + } + ) + # Create user + username = str(uuid.uuid4()) + password = str(uuid.uuid4()) + user_id = admin.create_user( + payload={ + "username": username, + "email": f"{username}@test.test", + "enabled": True, + "credentials": [{"type": "password", "value": password}], + } + ) + + yield ( + KeycloakOpenID( + server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", + realm_name=realm, + client_id=client, + ), + username, + password, + ) + + # Cleanup + admin.delete_client(client_id=client_id) + admin.delete_user(user_id=user_id) + + @pytest.fixture def realm(admin: KeycloakAdmin) -> str: + """Fixture for a new random realm.""" realm_name = str(uuid.uuid4()) - admin.create_realm(payload={"realm": realm_name}) + admin.create_realm(payload={"realm": realm_name, "enabled": True}) yield realm_name admin.delete_realm(realm_name=realm_name) @pytest.fixture def user(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random user.""" admin.realm_name = realm username = str(uuid.uuid4()) user_id = admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) @@ -102,6 +181,7 @@ def user(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def group(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random group.""" admin.realm_name = realm group_name = str(uuid.uuid4()) group_id = admin.create_group(payload={"name": group_name}) @@ -111,6 +191,7 @@ def group(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client(admin: KeycloakAdmin, realm: str) -> str: + """Fixture for a new random client.""" admin.realm_name = realm client = str(uuid.uuid4()) client_id = admin.create_client(payload={"name": client, "clientId": client}) diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 53a76dd..f01b91c 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -1,9 +1,13 @@ +"""Test module for KeycloakOpenID.""" +from unittest import mock + from keycloak.authorization import Authorization from keycloak.connection import ConnectionManager from keycloak.keycloak_openid import KeycloakOpenID def test_keycloak_openid_init(env): + """Test KeycloakOpenId's init method.""" oid = KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name="master", @@ -18,6 +22,7 @@ def test_keycloak_openid_init(env): def test_well_known(oid: KeycloakOpenID): + """Test the well_known method.""" res = oid.well_known() assert res is not None assert res != dict() @@ -77,3 +82,56 @@ def test_well_known(oid: KeycloakOpenID): "userinfo_signing_alg_values_supported", ]: assert key in res + + +def test_auth_url(env, oid: KeycloakOpenID): + """Test the auth_url method.""" + res = oid.auth_url(redirect_uri="http://test.test/*") + assert ( + res + == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}/realms/{oid.realm_name}" + + f"/protocol/openid-connect/auth?client_id={oid.client_id}&response_type=code" + + "&redirect_uri=http://test.test/*" + ) + + +def test_token(oid_with_credentials: tuple[KeycloakOpenID, str, str]): + """Test the token method.""" + oid, username, password = oid_with_credentials + token = oid.token(username=username, password=password) + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + } + + # Test with dummy totp + token = oid.token(username=username, password=password, totp="123456") + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + } + + # Test with extra param + token = oid.token(username=username, password=password, extra_param="foo") + assert token == { + "access_token": mock.ANY, + "expires_in": 300, + "not-before-policy": 0, + "refresh_expires_in": 1800, + "refresh_token": mock.ANY, + "scope": "profile email", + "session_state": mock.ANY, + "token_type": "Bearer", + }