diff --git a/src/iam/dependencies.py b/src/iam/dependencies.py index 72cc683..6ebc4e5 100644 --- a/src/iam/dependencies.py +++ b/src/iam/dependencies.py @@ -61,3 +61,16 @@ def get_perm_model_body( perm_model_body_dependency = Annotated[type[Permission], Depends(get_perm_model_body)] + + +def get_perm_model_query( + db: db_dependency, perm_id: Annotated[int, Query(gt=0)] +) -> type[Permission]: + perm_model = db.get(Permission, perm_id) + if perm_model is None: + raise PermNotFoundException(perm_id) + + return perm_model + + +perm_model_query_dependency = Annotated[type[Permission], Depends(get_perm_model_query)] diff --git a/src/iam/router.py b/src/iam/router.py index c946464..d428e80 100644 --- a/src/iam/router.py +++ b/src/iam/router.py @@ -31,7 +31,10 @@ from src.auth.dependencies import ( super_admin_dependency, ) from src.user.models import User -from src.user.dependencies import user_model_body_dependency +from src.user.dependencies import ( + user_model_body_dependency, + user_model_query_dependency, +) from src.organisation.models import Organisation as Org from src.service.models import Service @@ -46,6 +49,7 @@ from src.iam.dependencies import ( group_model_query_dependency, group_model_body_dependency, perm_model_body_dependency, + perm_model_query_dependency, ) from src.iam.schemas import ( IAMGetGroupPermissionsResponse, @@ -57,14 +61,11 @@ from src.iam.schemas import ( IAMPutGroupPermissionResponse, IAMPutGroupUserRequest, IAMPutGroupUserResponse, - IAMDeleteGroupPermissionRequest, IAMDeleteGroupPermissionResponse, - IAMDeleteGroupUserRequest, IAMDeleteGroupUserResponse, IAMGetPermissionsResponse, IAMPostPermissionRequest, IAMPostPermissionResponse, - IAMDeletePermissionRequest, IAMGetPermissionsSearchRequest, IAMGetPermissionsSearchResponse, ) @@ -205,10 +206,9 @@ async def add_group_user( @router.delete("/group/permissions") async def remove_group_permissions( db: db_dependency, - group_model: group_model_body_dependency, - perm_model: perm_model_body_dependency, - org_model: org_model_root_claim_body_dependency, - request_model: IAMDeleteGroupPermissionRequest, + group_model: group_model_query_dependency, + perm_model: perm_model_query_dependency, + org_model: org_model_root_claim_query_dependency, ): if group_model.org_id != org_model.id: raise UnauthorizedException("Group does not belong to this organization") @@ -226,10 +226,9 @@ async def remove_group_permissions( @router.delete("/group/user") async def remove_group_user( db: db_dependency, - group_model: group_model_body_dependency, - user_model: user_model_body_dependency, - org_model: org_model_root_claim_body_dependency, - request_model: IAMDeleteGroupUserRequest, + group_model: group_model_query_dependency, + user_model: user_model_query_dependency, + org_model: org_model_root_claim_query_dependency, ): if group_model.org_id != org_model.id: raise UnauthorizedException("Group does not belong to this organization") @@ -285,8 +284,7 @@ async def create_new_permission( async def delete_permission( db: db_dependency, su: super_admin_dependency, - perm_model: perm_model_body_dependency, - request_model: IAMDeletePermissionRequest, + perm_model: perm_model_query_dependency, ): db.delete(perm_model) db.commit() diff --git a/src/iam/schemas.py b/src/iam/schemas.py index 16aaa29..fca76de 100644 --- a/src/iam/schemas.py +++ b/src/iam/schemas.py @@ -81,19 +81,11 @@ class IAMPutGroupUserResponse(CustomBaseModel): users: list[UserSchema] -class IAMDeleteGroupPermissionRequest(GroupIDMixin, PermIDMixin): - pass - - class IAMDeleteGroupPermissionResponse(CustomBaseModel): group: GroupSchema permissions: list[PermissionSchema] -class IAMDeleteGroupUserRequest(GroupIDMixin, UserIDMixin): - pass - - class IAMDeleteGroupUserResponse(CustomBaseModel): group: GroupSchema users: list[UserSchema] @@ -112,10 +104,6 @@ class IAMPostPermissionResponse(CustomBaseModel): permission: PermissionSchema -class IAMDeletePermissionRequest(PermIDMixin): - pass - - class IAMGetPermissionsSearchRequest(OrgIDMixin): service_id: Annotated[int | None, Field(gt=0)] = None resource: Optional[str] = None diff --git a/src/organisation/router.py b/src/organisation/router.py index 498d282..3f4fcbc 100644 --- a/src/organisation/router.py +++ b/src/organisation/router.py @@ -31,6 +31,7 @@ from src.database import db_dependency from src.user.dependencies import ( user_model_body_dependency, user_model_claims_dependency, + user_model_query_dependency, ) from src.auth.dependencies import ( super_admin_dependency, @@ -38,7 +39,10 @@ from src.auth.dependencies import ( org_model_root_claim_body_dependency, ) -from src.organisation.dependencies import org_model_body_dependency +from src.organisation.dependencies import ( + org_model_body_dependency, + org_model_query_dependency, +) from src.organisation.constants import ContactType from src.organisation.models import Organisation as Org from src.organisation.schemas import ( @@ -52,8 +56,6 @@ from src.organisation.schemas import ( OrgGetOrgResponse, OrgPatchRootRequest, OrgGetGroupResponse, - OrgDeleteUserRequest, - OrgDeleteOrgRequest, OrgPostOrgResponse, OrgPatchQuestionnaireResponse, OrgPatchStatusResponse, @@ -324,9 +326,8 @@ async def add_user_to_org( ) async def delete_organisation_by_id( db: db_dependency, - org_model: org_model_body_dependency, + org_model: org_model_query_dependency, su: super_admin_dependency, - request_model: OrgDeleteOrgRequest, ): """ Removes an organisation from the hub. @@ -411,9 +412,8 @@ async def get_org_groups(org_model: org_model_root_claim_query_dependency): ) async def remove_user_from_org( db: db_dependency, - org_model: org_model_root_claim_body_dependency, - user_model: user_model_body_dependency, - request_model: OrgDeleteUserRequest, + org_model: org_model_root_claim_query_dependency, + user_model: user_model_query_dependency, ): """ Revokes a user's membership in an organisation. diff --git a/src/organisation/schemas.py b/src/organisation/schemas.py index 31e59b9..9f120f3 100644 --- a/src/organisation/schemas.py +++ b/src/organisation/schemas.py @@ -88,10 +88,6 @@ class OrgPostUserResponse(CustomBaseModel): users: list[str] -class OrgDeleteUserRequest(OrgIDMixin, UserIDMixin): - pass - - class OrgPatchRootRequest(OrgIDMixin, UserIDMixin): pass @@ -133,7 +129,3 @@ class OrgGetOrgResponse(CustomBaseModel): billing_contact: Optional[str] = None security_contact: Optional[str] = None intake_questionnaire: Optional[Questionnaire] = None - - -class OrgDeleteOrgRequest(OrgIDMixin): - pass diff --git a/src/service/router.py b/src/service/router.py index 6f98e7f..bfd282e 100644 --- a/src/service/router.py +++ b/src/service/router.py @@ -20,7 +20,10 @@ from src.auth.dependencies import ( from src.service.models import Service from src.service.utils import generate_api_key -from src.service.dependencies import service_model_body_dependency +from src.service.dependencies import ( + service_model_body_dependency, + service_model_query_dependency, +) from src.service.schemas import ( ServiceGetServiceResponse, ServicePostServiceRequest, @@ -28,7 +31,6 @@ from src.service.schemas import ( ServiceWithKeySchema, ServicePatchKeyResponse, ServicePatchKeyRequest, - ServiceDeleteServiceRequest, ) router = APIRouter( @@ -137,9 +139,8 @@ async def regenerate_api_key( ) async def remove_service( db: db_dependency, - service_model: service_model_body_dependency, + service_model: service_model_query_dependency, su: super_admin_dependency, - request_model: ServiceDeleteServiceRequest, ): """ Removes a service from the hub. diff --git a/src/service/schemas.py b/src/service/schemas.py index 9c04840..47d47f5 100644 --- a/src/service/schemas.py +++ b/src/service/schemas.py @@ -45,7 +45,3 @@ class ServicePatchKeyRequest(ServiceIDMixin): class ServicePatchKeyResponse(CustomBaseModel): service: ServiceWithKeySchema - - -class ServiceDeleteServiceRequest(ServiceIDMixin): - pass diff --git a/src/user/router.py b/src/user/router.py index f380fb4..8613d47 100644 --- a/src/user/router.py +++ b/src/user/router.py @@ -11,7 +11,7 @@ Endpoints: from fastapi import APIRouter from starlette import status -from src.user.schemas import UserResponse, OIDCClaims, UserDeleteUserRequest +from src.user.schemas import UserResponse, OIDCClaims from src.user.dependencies import ( user_model_claims_dependency, user_model_query_dependency, @@ -92,9 +92,8 @@ async def get_user_by_id( ) async def delete_user_by_id( db: db_dependency, - user_model: user_model_body_dependency, + user_model: user_model_query_dependency, su: super_admin_dependency, - request_model: UserDeleteUserRequest, ): """ Deletes the user with the provided ID from the database. This will not remove them from OIDC, and they will be automatically readded on next login. diff --git a/src/user/schemas.py b/src/user/schemas.py index 5fd44df..8ef46df 100644 --- a/src/user/schemas.py +++ b/src/user/schemas.py @@ -55,7 +55,3 @@ class UserResponse(CustomBaseModel): class OrgResponse(CustomBaseModel): org_id: int name: str - - -class UserDeleteUserRequest(UserIDMixin): - pass diff --git a/test/test_iam.py b/test/test_iam.py index b61b4b7..1599d7e 100644 --- a/test/test_iam.py +++ b/test/test_iam.py @@ -723,3 +723,40 @@ async def test_post_perm_search_status_checks( resp = await default_client.post("/iam/permissions/search", json=body) assert resp.status_code == expected_status + + +@pytest.mark.anyio +async def test_delete_group_permissions_success(default_client: AsyncClient): + resp = await default_client.delete( + "/iam/group/permissions?org_id=1&group_id=1&perm_id=1" + ) + data = resp.json() + + assert resp.status_code == 200 + assert "permissions" in data + assert isinstance(data["permissions"], list) + assert len(data["permissions"]) == 0 + assert "group" in data + assert data["group"]["id"] == 1 + assert data["group"]["name"] == "Test Group" + + +@pytest.mark.anyio +async def test_delete_permissions_success(default_client: AsyncClient): + resp = await default_client.delete("/iam/permission?perm_id=1") + + assert resp.status_code == 204 + + +@pytest.mark.anyio +async def test_delete_group_users_success(default_client: AsyncClient): + resp = await default_client.delete("/iam/group/user?org_id=1&group_id=1&user_id=1") + data = resp.json() + + assert resp.status_code == 200 + assert "users" in data + assert isinstance(data["users"], list) + assert len(data["users"]) == 0 + assert "group" in data + assert data["group"]["id"] == 1 + assert data["group"]["name"] == "Test Group" diff --git a/test/test_organisation.py b/test/test_organisation.py index b179689..a8708ea 100644 --- a/test/test_organisation.py +++ b/test/test_organisation.py @@ -491,3 +491,26 @@ async def test_patch_org_contact_status_checks( resp = await default_client.patch("/org/contact", json=body) assert resp.status_code == expected_status + + +@pytest.mark.anyio +async def test_delete_org_success(default_client: AsyncClient): + resp = await default_client.delete("/org?org_id=1") + + assert resp.status_code == 204 + + +@pytest.mark.anyio +async def test_delete_org_users_success(db_session, default_client: AsyncClient): + db_session.add( + User( + email="user@test.org", + first_name="User", + last_name="Test", + oidc_id="abcd-efgh-ijkl-1234", + ) + ) + db_session.flush() + resp = await default_client.delete("/org/user?org_id=1&user_id=2") + + assert resp.status_code == 204 diff --git a/test/test_service.py b/test/test_service.py index a01d25b..bd8fe88 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -88,3 +88,10 @@ async def test_patch_services_status_checks( resp = await default_client.patch("/service/key", json=body) assert resp.status_code == expected_status + + +@pytest.mark.anyio +async def test_delete_service_success(default_client: AsyncClient): + resp = await default_client.delete("/service/?service_id=1") + + assert resp.status_code == 204 diff --git a/test/test_user.py b/test/test_user.py index 996c67a..4eadc3c 100644 --- a/test/test_user.py +++ b/test/test_user.py @@ -45,3 +45,10 @@ async def test_get_user_status_checks( resp = await default_client.get(f"/user/?{query}") assert resp.status_code == expected_status + + +@pytest.mark.anyio +async def test_delete_user_success(default_client: AsyncClient): + resp = await default_client.delete("/user/?user_id=1") + + assert resp.status_code == 204