Loading src/portal_service/api/api_clients/routes.py +6 −0 Changes for src/portal_service/api/api_clients/routes.py: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ from portal_service.domain.errors import ( IdentityProviderUnavailableError, ProductNotHeldError, ProvisionerRejectedError, RegistrationTokenRejectedError, ) api_clients_router = APIRouter(prefix="/api/api-clients", tags=["api-clients"]) Loading @@ -52,6 +53,11 @@ def _answering_refusals() -> Iterator[None]: status.HTTP_503_SERVICE_UNAVAILABLE, "API client provisioning is misconfigured: ask the operator to check it", ) from exc except RegistrationTokenRejectedError as exc: raise HTTPException( status.HTTP_503_SERVICE_UNAVAILABLE, "The API client could not be deleted. Ask an administrator to check it.", ) from exc except IdentityProviderUnavailableError as exc: raise HTTPException( status.HTTP_503_SERVICE_UNAVAILABLE, Loading src/portal_service/application/api_client_service.py +0 −7 Changes for src/portal_service/application/api_client_service.py: 0 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ from portal_service.domain.errors import ( ApiClientNotFoundError, CreatorNotAllowedError, ProductNotHeldError, RegistrationTokenRejectedError, ) from portal_service.domain.models import ( ApiClient, Loading Loading @@ -136,13 +135,7 @@ class ApiClientService: raise ApiClientNotFoundError("This API client no longer exists") token = decrypt(api_client.registration_token, self._encryption_key).decode() try: await self._registry.delete(api_client.keycloak_client_id, token) except RegistrationTokenRejectedError: # Disabled or removed in the Keycloak console: nothing more can be done there. logger.warning( "keycloak client left behind", keycloak_client_id=api_client.keycloak_client_id ) now = datetime.now(UTC) for entitlement in await self._entitlements.list_by_client(api_client.id): Loading tests/unit/test_api_client_routes.py +20 −1 Changes for tests/unit/test_api_client_routes.py: 20 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -8,7 +8,11 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from portal_service.api.dependencies import get_api_client_service, get_current_user from portal_service.domain.errors import IdentityProviderUnavailableError, ProvisionerRejectedError from portal_service.domain.errors import ( IdentityProviderUnavailableError, ProvisionerRejectedError, RegistrationTokenRejectedError, ) from portal_service.domain.models import TokenClaims from tests.api_clients import PROVISIONING_ENV, Setup from tests.conftest import _apply_env Loading Loading @@ -93,6 +97,21 @@ def test_delete_answers_204_and_the_client_leaves_the_list(client: TestClient) - assert client.get("/api/api-clients").json() == [] def test_rejected_deletion_is_a_503_and_the_client_stays_listed( client: TestClient, s: Setup ) -> None: created = client.post("/api/api-clients", json=BODY).json() s.registry.delete_error = RegistrationTokenRejectedError("token replaced in the console") response = client.delete(f"/api/api-clients/{created['id']}") assert response.status_code == 503 assert "administrator" in response.json()["detail"] assert client.get("/api/api-clients").json() == [ {key: value for key, value in created.items() if key != "client_secret"} ] def test_someone_elses_client_looks_like_no_client( client: TestClient, app: FastAPI, s: Setup ) -> None: Loading tests/unit/test_api_client_service.py +5 −4 Changes for tests/unit/test_api_client_service.py: 5 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -204,14 +204,15 @@ async def test_delete_refuses_unknown_foreign_and_already_deleted_clients(s: Set assert s.registry.deleted == [created.keycloak_client_id] async def test_delete_still_succeeds_when_keycloak_rejects_the_token(s: Setup) -> None: async def test_delete_changes_nothing_when_keycloak_rejects_the_token(s: Setup) -> None: created = await _create(s) s.registry.delete_error = RegistrationTokenRejectedError("disabled in the console") s.registry.delete_error = RegistrationTokenRejectedError("token replaced in the console") with pytest.raises(RegistrationTokenRejectedError): await s.service.delete(creator="maria", api_client_id=created.id) assert (await _stored(s, created.id)).deleted_at is not None assert s.entitlements.rows[0].state is EntitlementState.REVOKED assert (await _stored(s, created.id)).deleted_at is None assert s.entitlements.rows[0].state is EntitlementState.APPROVED async def test_delete_changes_nothing_when_keycloak_is_unavailable(s: Setup) -> None: Loading Loading
src/portal_service/api/api_clients/routes.py +6 −0 Changes for src/portal_service/api/api_clients/routes.py: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ from portal_service.domain.errors import ( IdentityProviderUnavailableError, ProductNotHeldError, ProvisionerRejectedError, RegistrationTokenRejectedError, ) api_clients_router = APIRouter(prefix="/api/api-clients", tags=["api-clients"]) Loading @@ -52,6 +53,11 @@ def _answering_refusals() -> Iterator[None]: status.HTTP_503_SERVICE_UNAVAILABLE, "API client provisioning is misconfigured: ask the operator to check it", ) from exc except RegistrationTokenRejectedError as exc: raise HTTPException( status.HTTP_503_SERVICE_UNAVAILABLE, "The API client could not be deleted. Ask an administrator to check it.", ) from exc except IdentityProviderUnavailableError as exc: raise HTTPException( status.HTTP_503_SERVICE_UNAVAILABLE, Loading
src/portal_service/application/api_client_service.py +0 −7 Changes for src/portal_service/application/api_client_service.py: 0 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ from portal_service.domain.errors import ( ApiClientNotFoundError, CreatorNotAllowedError, ProductNotHeldError, RegistrationTokenRejectedError, ) from portal_service.domain.models import ( ApiClient, Loading Loading @@ -136,13 +135,7 @@ class ApiClientService: raise ApiClientNotFoundError("This API client no longer exists") token = decrypt(api_client.registration_token, self._encryption_key).decode() try: await self._registry.delete(api_client.keycloak_client_id, token) except RegistrationTokenRejectedError: # Disabled or removed in the Keycloak console: nothing more can be done there. logger.warning( "keycloak client left behind", keycloak_client_id=api_client.keycloak_client_id ) now = datetime.now(UTC) for entitlement in await self._entitlements.list_by_client(api_client.id): Loading
tests/unit/test_api_client_routes.py +20 −1 Changes for tests/unit/test_api_client_routes.py: 20 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -8,7 +8,11 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from portal_service.api.dependencies import get_api_client_service, get_current_user from portal_service.domain.errors import IdentityProviderUnavailableError, ProvisionerRejectedError from portal_service.domain.errors import ( IdentityProviderUnavailableError, ProvisionerRejectedError, RegistrationTokenRejectedError, ) from portal_service.domain.models import TokenClaims from tests.api_clients import PROVISIONING_ENV, Setup from tests.conftest import _apply_env Loading Loading @@ -93,6 +97,21 @@ def test_delete_answers_204_and_the_client_leaves_the_list(client: TestClient) - assert client.get("/api/api-clients").json() == [] def test_rejected_deletion_is_a_503_and_the_client_stays_listed( client: TestClient, s: Setup ) -> None: created = client.post("/api/api-clients", json=BODY).json() s.registry.delete_error = RegistrationTokenRejectedError("token replaced in the console") response = client.delete(f"/api/api-clients/{created['id']}") assert response.status_code == 503 assert "administrator" in response.json()["detail"] assert client.get("/api/api-clients").json() == [ {key: value for key, value in created.items() if key != "client_secret"} ] def test_someone_elses_client_looks_like_no_client( client: TestClient, app: FastAPI, s: Setup ) -> None: Loading
tests/unit/test_api_client_service.py +5 −4 Changes for tests/unit/test_api_client_service.py: 5 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -204,14 +204,15 @@ async def test_delete_refuses_unknown_foreign_and_already_deleted_clients(s: Set assert s.registry.deleted == [created.keycloak_client_id] async def test_delete_still_succeeds_when_keycloak_rejects_the_token(s: Setup) -> None: async def test_delete_changes_nothing_when_keycloak_rejects_the_token(s: Setup) -> None: created = await _create(s) s.registry.delete_error = RegistrationTokenRejectedError("disabled in the console") s.registry.delete_error = RegistrationTokenRejectedError("token replaced in the console") with pytest.raises(RegistrationTokenRejectedError): await s.service.delete(creator="maria", api_client_id=created.id) assert (await _stored(s, created.id)).deleted_at is not None assert s.entitlements.rows[0].state is EntitlementState.REVOKED assert (await _stored(s, created.id)).deleted_at is None assert s.entitlements.rows[0].state is EntitlementState.APPROVED async def test_delete_changes_nothing_when_keycloak_is_unavailable(s: Setup) -> None: Loading