"""Handlers for identity endpoints.""" import logging import uuid from typing import Dict, Union from uuid import UUID from cerbos.sdk.client import AsyncCerbosClient from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request from pydantic import UUID4 from pdp.connectors.dynamo import DynamoDbConnector from pdp.connectors.features import SplitioClient from pdp.connectors.features.wrappers import BooleanFeature from pdp.connectors.ows_account import OwsAccountClient from pdp.connectors.ows_participant import OwsParticipantClient from pdp.connectors.redis_client import PydanticSchemaSerializer, RedisConnector from pdp.constants.constants import MESSAGE_BUST_IDENTITY_CACHES from pdp.constants.features import FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID from pdp.fastapi.auth import ( check_authorization, filter_for_identity_tenants, get_identity_pdp_tenant_roles_from_scope, get_principal_ows_permissions_from_scope, get_principal_pdp_tenant_roles_from_scope, identity_uuid_from_scope, impersonated_by_identity_uuid_from_scope, user_type_from_scope, ) from pdp.fastapi.datasources import ( get_async_cerbos_client, get_boto_connector, get_ows_account_client, get_ows_participant_client, get_redis_connector, get_splitio_client, ) from pdp.fastapi.schemas import ( check_resource_type_actions as check_resource_type_actions_schema, ) from pdp.fastapi.schemas import check_resources as check_resources_schema from pdp.fastapi.schemas import deactivation as deactivation_schema from pdp.fastapi.schemas import get_allowed_tenants as get_allowed_tenants_schema from pdp.fastapi.schemas import identity as identity_schema from pdp.fastapi.schemas.cache import ( IdentityAllowedTenantsCacheObject, IdentityRolesResponseCacheObject, ) from pdp.fastapi.schemas.principal import Principal from pdp.logic import cerbos, identity from pdp.logic.cache import ( bust_identity_caches, get_object_from_cache, save_cache_object, ) from pdp.proxies.tenant_validators import ( assert_valid_attach_detach_request, ) router = APIRouter(prefix="/identity", tags=["identity"]) logger = logging.getLogger(__name__) @router.post( "/self/check/resources/", response_model=identity_schema.CheckResourcesResponse ) async def check_my_resources( check_resources_request: check_resources_schema.CheckResourcesRequest, identity_uuid: UUID4 = Depends(identity_uuid_from_scope), pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), splitio_client: SplitioClient = Depends(get_splitio_client), authenticated_identity_uuid: UUID = Depends(identity_uuid_from_scope), ) -> identity_schema.CheckResourcesResponse: """Check resources for the authenticated principal.""" await check_resources_request.update_with_id_to_uuid_exchange( redis_connector=redis_connector, ows_account_client=ows_account_client, ) pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) if pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or authenticated_identity_uuid) ): principal = Principal( identity_uuid=identity_uuid, user_type=user_type, pdp_tenant_roles=pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) check_response = await cerbos.check_resources( identity_uuid=str(identity_uuid), check_resources_request=check_resources_request, pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, include_resource_attributes=check_resources_request.include_resource_attributes_in_response, # noqa: E501 ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, authenticated_identity_uuid=authenticated_identity_uuid, principal=principal, splitio_client=splitio_client, ) else: check_response = await cerbos.check_resources( identity_uuid=str(identity_uuid), check_resources_request=check_resources_request, pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, include_resource_attributes=check_resources_request.include_resource_attributes_in_response, # noqa: E501 ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, authenticated_identity_uuid=authenticated_identity_uuid, splitio_client=splitio_client, ) return check_response @router.get("/self/roles/", response_model=identity_schema.RolesResponse) async def get_my_roles( identity_uuid: UUID4 = Depends(identity_uuid_from_scope), cursor: Union[str, None] = None, identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), redis_connector: RedisConnector = Depends(get_redis_connector), ) -> identity_schema.RolesResponse: """Get the authenticated user's roles.""" identity_roles_response_cache_object = IdentityRolesResponseCacheObject( principal_identity_uuid=identity_uuid, listed_identity_uuid=identity_uuid, cursor=cursor, ) cache_key = identity_roles_response_cache_object.to_cache_key() cached_roles_response: ( identity_schema.RolesResponse | None ) = await get_object_from_cache( cache_key, redis_connector=redis_connector, serializer=PydanticSchemaSerializer(IdentityRolesResponseCacheObject), cache_attribute_name="roles_response", ) if cached_roles_response: return cached_roles_response roles_response = identity.get_roles_by_identity( str(identity_uuid), cursor=cursor, identity_ddb_connector=identity_ddb_connector ) identity_roles_response_cache_object = IdentityRolesResponseCacheObject( principal_identity_uuid=identity_uuid, listed_identity_uuid=identity_uuid, cursor=cursor, ) identity_roles_response_cache_object.roles_response = roles_response await save_cache_object( cache_object=identity_roles_response_cache_object, cache_model_type=IdentityRolesResponseCacheObject, redis_connector=redis_connector, ) return roles_response @router.post( "/self/check/resource-type-actions/", response_model=check_resource_type_actions_schema.CheckResourceTypeActionsResponse, ) async def check_my_resource_type_actions( data: check_resource_type_actions_schema.CheckResourceTypeActionsRequest, identity_uuid: UUID4 = Depends(identity_uuid_from_scope), pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> check_resource_type_actions_schema.CheckResourceTypeActionsResponse: """Get authorization for authenticated principal's access to resource type actions. This endpoint currently only supports tenant_owned_resource types. """ pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) if pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or identity_uuid) ): principal = Principal( identity_uuid=identity_uuid, user_type=user_type, pdp_tenant_roles=pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) return await identity.check_resource_type_actions( request=data, identity_uuid=str(identity_uuid), pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, splitio_client=splitio_client, authenticated_identity_uuid=identity_uuid, principal=principal, ) return await identity.check_resource_type_actions( request=data, identity_uuid=str(identity_uuid), pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, splitio_client=splitio_client, authenticated_identity_uuid=identity_uuid, ) @router.post( "/self/allowed-tenants/", response_model=get_allowed_tenants_schema.GetAllowedTenantsResponse, ) async def get_my_allowed_tenants( data: get_allowed_tenants_schema.GetAllowedTenantsRequest, identity_uuid: UUID4 = Depends(identity_uuid_from_scope), pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), authenticated_identity_uuid: UUID = Depends(identity_uuid_from_scope), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> get_allowed_tenants_schema.GetAllowedTenantsResponse: """Get allowed tenants for the authenticated principal to `action` on the `resource_type`.""" # noqa: E501 identity_allowed_tenants_cache_object = IdentityAllowedTenantsCacheObject( identity_uuid=identity_uuid, **data.model_dump(), ) cache_key = identity_allowed_tenants_cache_object.to_cache_key() cached_get_allowed_tenants_response: ( get_allowed_tenants_schema.GetAllowedTenantsResponse | None ) = await get_object_from_cache( cache_key, redis_connector=redis_connector, serializer=PydanticSchemaSerializer(IdentityAllowedTenantsCacheObject), cache_attribute_name="get_allowed_tenants_response", ) if cached_get_allowed_tenants_response: return cached_get_allowed_tenants_response pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) ff_value = pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or authenticated_identity_uuid) ) if ff_value: principal = Principal( identity_uuid=identity_uuid, user_type=user_type, pdp_tenant_roles=pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) allowed_tenants_response = await identity.get_allowed_tenants( request=data, identity_uuid=str(identity_uuid), pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, authenticated_identity_uuid=identity_uuid, splitio_client=splitio_client, principal=principal, ) else: allowed_tenants_response = await identity.get_allowed_tenants( request=data, identity_uuid=str(identity_uuid), pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, authenticated_identity_uuid=identity_uuid, splitio_client=splitio_client, ) # DO PROXY EXCHANGE await allowed_tenants_response.update_with_uuid_to_id_exchange( redis_connector=redis_connector, ows_account_client=ows_account_client, ) identity_allowed_tenants_cache_object.get_allowed_tenants_response = ( allowed_tenants_response ) await save_cache_object( cache_object=identity_allowed_tenants_cache_object, cache_model_type=IdentityAllowedTenantsCacheObject, redis_connector=redis_connector, ) return allowed_tenants_response @router.get( "/{identity_uuid}/roles/", response_model=identity_schema.RolesResponse, dependencies=[Depends(check_authorization)], ) async def get_roles_by_identity( identity_uuid: UUID4, principal_identity_uuid: UUID4 = Depends(identity_uuid_from_scope), identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), ows_permissions_tenant_roles: Dict[ uuid.UUID, identity_schema.TenantRoles ] = Depends(get_principal_ows_permissions_from_scope), cursor: Union[str, None] = None, cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> identity_schema.RolesResponse: """Get roles for an identity uuid, filtered for the authenticated principal.""" identity_roles_response_cache_object = IdentityRolesResponseCacheObject( principal_identity_uuid=principal_identity_uuid, listed_identity_uuid=identity_uuid, cursor=cursor, ) cache_key = identity_roles_response_cache_object.to_cache_key() cached_roles_response: ( identity_schema.RolesResponse | None ) = await get_object_from_cache( cache_key, redis_connector=redis_connector, serializer=PydanticSchemaSerializer(IdentityRolesResponseCacheObject), cache_attribute_name="roles_response", ) if cached_roles_response: return cached_roles_response result = identity.get_roles_by_identity( str(identity_uuid), cursor=cursor, identity_ddb_connector=identity_ddb_connector ) result.tenants = await filter_for_identity_tenants( identity_uuid, result.tenants, action="view", user_type=user_type, impersonated_by_identity_uuid=impersonated_by_identity_uuid, principal_identity_uuid=principal_identity_uuid, pdp_tenant_roles=pdp_tenant_roles, ows_permissions_tenant_roles=ows_permissions_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, splitio_client=splitio_client, ) if not identity_roles_response_cache_object: identity_roles_response_cache_object = IdentityRolesResponseCacheObject( principal_identity_uuid=principal_identity_uuid, listed_identity_uuid=identity_uuid, cursor=cursor, ) identity_roles_response_cache_object.roles_response = result await save_cache_object( cache_object=identity_roles_response_cache_object, cache_model_type=IdentityRolesResponseCacheObject, redis_connector=redis_connector, ) return result @router.put( "/{identity_uuid}/tenant/{tenant_uuid}/attach-and-detach/roles/", response_model=identity_schema.RolesResponse, dependencies=[ Depends(assert_valid_attach_detach_request), Depends(check_authorization), ], ) async def attach_detach_roles_by_identity_tenant( identity_uuid: UUID4, tenant_uuid: UUID, data: identity_schema.AttachDetachRolesRequest, background_tasks: BackgroundTasks, authenticated_identity_uuid: UUID4 = Depends(identity_uuid_from_scope), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), principal_pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), redis_connector: RedisConnector = Depends(get_redis_connector), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> identity_schema.RolesResponse: """Attach and detach roles for an identity and tenant pair.""" # Validate the path_params.tenant_uuid and data.tenant_uuid match if tenant_uuid != data.tenant_uuid: raise HTTPException( status_code=400, detail="Inconsistent request", ) pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) if pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or authenticated_identity_uuid) ): principal = Principal( identity_uuid=authenticated_identity_uuid, user_type=user_type, pdp_tenant_roles=principal_pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) tenant_roles = await identity.attach_and_detach_roles( identity_uuid=str(identity_uuid), tenant_uuid=tenant_uuid, tenant_type=data.tenant_type, roles_to_attach=data.roles_to_attach, roles_to_detach=data.roles_to_detach, authenticated_identity_uuid=str(authenticated_identity_uuid), identity_ddb_connector=identity_ddb_connector, principal=principal, ) else: tenant_roles = await identity.attach_and_detach_roles( identity_uuid=str(identity_uuid), tenant_uuid=tenant_uuid, tenant_type=data.tenant_type, roles_to_attach=data.roles_to_attach, roles_to_detach=data.roles_to_detach, authenticated_identity_uuid=str(authenticated_identity_uuid), identity_ddb_connector=identity_ddb_connector, ) logger.info(MESSAGE_BUST_IDENTITY_CACHES) background_tasks.add_task( bust_identity_caches, [identity_uuid], redis_connector=redis_connector ) return identity_schema.RolesResponse(tenants={tenant_uuid: tenant_roles}) @router.post( "/{identity_uuid}/tenant/{tenant_uuid}/deactivate/", response_model=deactivation_schema.DeactivateOneResponse, dependencies=[Depends(check_authorization)], ) async def deactivate_one( identity_uuid: UUID4, tenant_uuid: UUID, data: deactivation_schema.DeactivateOneRequest, background_tasks: BackgroundTasks, authenticated_identity_uuid: UUID4 = Depends(identity_uuid_from_scope), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), principal_pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), redis_connector: RedisConnector = Depends(get_redis_connector), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> deactivation_schema.DeactivateOneResponse: """Endpoint to deactivate an identity's access to a tenant.""" # Validate the path_params.tenant_uuid and data.tenant_uuid match if tenant_uuid != data.tenant_uuid: raise HTTPException( status_code=400, detail="Inconsistent request, tenant_uuid in path and body do not match", ) if not identity.is_tenant_assigned( identity_uuid=str(identity_uuid), tenant_uuid=tenant_uuid, identity_ddb_connector=identity_ddb_connector, ): raise HTTPException( status_code=403, detail="Cannot deactivate this identity/tenant", ) pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) if pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or authenticated_identity_uuid) ): principal = Principal( identity_uuid=authenticated_identity_uuid, user_type=user_type, pdp_tenant_roles=principal_pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) summary = await identity.deactivate_many( identity_uuid=str(identity_uuid), tenants={ data.tenant_uuid: identity_schema.TenantRoles( tenant_uuid=data.tenant_uuid, tenant_type=data.tenant_type, roles=[], ) }, identity_ddb_connector=identity_ddb_connector, authenticated_identity_uuid=str(authenticated_identity_uuid), principal=principal, ) else: summary = await identity.deactivate_many( identity_uuid=str(identity_uuid), tenants={ data.tenant_uuid: identity_schema.TenantRoles( tenant_uuid=data.tenant_uuid, tenant_type=data.tenant_type, roles=[], ) }, identity_ddb_connector=identity_ddb_connector, authenticated_identity_uuid=str(authenticated_identity_uuid), ) logger.info(MESSAGE_BUST_IDENTITY_CACHES) background_tasks.add_task( bust_identity_caches, [identity_uuid], redis_connector=redis_connector ) return deactivation_schema.DeactivateOneResponse(summary=summary) @router.post( "/{identity_uuid}/check/resources/", response_model=identity_schema.CheckResourcesResponse, dependencies=[Depends(check_authorization)], ) async def check_identity_resources( request: Request, identity_uuid: UUID4, check_resources_request: check_resources_schema.CheckResourcesRequest, background_tasks: BackgroundTasks, identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), authenticated_identity_uuid: UUID4 = Depends(identity_uuid_from_scope), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> identity_schema.CheckResourcesResponse: """Check resources for a particular identity.""" pdp_tenant_roles = await get_identity_pdp_tenant_roles_from_scope( request, identity_uuid, identity_ddb_connector=identity_ddb_connector, redis_connector=redis_connector, ) await check_resources_request.update_with_id_to_uuid_exchange( redis_connector=redis_connector, ows_account_client=ows_account_client, ) check_response = await cerbos.check_resources( identity_uuid=str(identity_uuid), check_resources_request=check_resources_request, pdp_tenant_roles=pdp_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, include_resource_attributes=check_resources_request.include_resource_attributes_in_response, # noqa: E501 authenticated_identity_uuid=authenticated_identity_uuid, splitio_client=splitio_client, ) return check_response @router.delete( "/{identity_uuid}/deactivate/", response_model=deactivation_schema.DeactivateAllResponse, dependencies=[Depends(check_authorization)], ) async def deactivate_all( request: Request, identity_uuid: UUID4, background_tasks: BackgroundTasks, principal_identity_uuid: UUID4 = Depends(identity_uuid_from_scope), identity_ddb_connector: DynamoDbConnector = Depends(get_boto_connector), principal_pdp_tenant_roles: Dict[uuid.UUID, identity_schema.TenantRoles] = Depends( get_principal_pdp_tenant_roles_from_scope ), principal_ows_permissions_tenant_roles: Dict[ uuid.UUID, identity_schema.TenantRoles ] = Depends(get_principal_ows_permissions_from_scope), impersonated_by_identity_uuid: UUID4 | None = Depends( impersonated_by_identity_uuid_from_scope ), user_type: str = Depends(user_type_from_scope), cerbos_client: AsyncCerbosClient = Depends(get_async_cerbos_client), ows_account_client: OwsAccountClient = Depends(get_ows_account_client), ows_participant_client: OwsParticipantClient = Depends(get_ows_participant_client), redis_connector: RedisConnector = Depends(get_redis_connector), authenticated_identity_uuid: UUID = Depends(identity_uuid_from_scope), splitio_client: SplitioClient = Depends(get_splitio_client), ) -> deactivation_schema.DeactivateAllResponse: """Endpoint to deactivate roles for an identity.""" identity_pdp_tenant_roles = await get_identity_pdp_tenant_roles_from_scope( request, identity_uuid, identity_ddb_connector=identity_ddb_connector, redis_connector=redis_connector, ) if not identity_pdp_tenant_roles: logger.info( f"Identity {identity_uuid} has no tenants. Deactivate All is no-op." ) return deactivation_schema.DeactivateAllResponse( summary=deactivation_schema.DeactivationSummary( deleted=0, remaining=0, ) ) allowed_tenants = await filter_for_identity_tenants( identity_uuid, identity_pdp_tenant_roles, action="deactivate", user_type=user_type, impersonated_by_identity_uuid=impersonated_by_identity_uuid, principal_identity_uuid=principal_identity_uuid, pdp_tenant_roles=principal_pdp_tenant_roles, ows_permissions_tenant_roles=principal_ows_permissions_tenant_roles, cerbos_client=cerbos_client, ows_account_client=ows_account_client, ows_participant_client=ows_participant_client, redis_connector=redis_connector, splitio_client=splitio_client, ) if not allowed_tenants: raise HTTPException( status_code=403, detail=f"Principal {principal_identity_uuid} not authorized to deactivate all tenants from {identity_uuid}.", # noqa: E501 ) logger.info(MESSAGE_BUST_IDENTITY_CACHES) background_tasks.add_task( bust_identity_caches, [identity_uuid], redis_connector=redis_connector ) pp_send_impersonated_by_identity_uuid_feature = BooleanFeature( client=splitio_client, feature_name=FEATURE_FLAG_PP_SEND_IMPERSONATED_BY_IDENTITY_UUID, ) if pp_send_impersonated_by_identity_uuid_feature.is_on_for_identity( str(impersonated_by_identity_uuid or authenticated_identity_uuid) ): principal = Principal( identity_uuid=authenticated_identity_uuid, user_type=user_type, pdp_tenant_roles=principal_pdp_tenant_roles, impersonated_by_identity_uuid=impersonated_by_identity_uuid, ) return deactivation_schema.DeactivateAllResponse( summary=await identity.deactivate_many( identity_uuid=str(identity_uuid), tenants=allowed_tenants, identity_ddb_connector=identity_ddb_connector, authenticated_identity_uuid=str(authenticated_identity_uuid), principal=principal, ) ) else: return deactivation_schema.DeactivateAllResponse( summary=await identity.deactivate_many( identity_uuid=str(identity_uuid), tenants=allowed_tenants, identity_ddb_connector=identity_ddb_connector, authenticated_identity_uuid=str(authenticated_identity_uuid), ) )