import enum from collections.abc import Iterator from typing import Any from owsclient import OwsClient from pydantic import BaseModel class Cursor(BaseModel): cursor: str | None = None shorthand: str | None = None class TenantType(enum.StrEnum): ACCOUNT = "account" SUBACCOUNT = "subaccount" class Role(BaseModel): role: str class Tenant(BaseModel): tenant_type: str tenant_uuid: str tenant_id: int | None = None class TenantWithRoles(Tenant): roles: list[Role] class GetRolesResponse(BaseModel): cursor: Cursor tenants: dict[str, TenantWithRoles] class Resource(BaseModel): resource_id: str resource_type: str attributes: dict[str, Any] = {} class CheckResourceAction(BaseModel): resource: Resource action: str class CheckResourcesInput(BaseModel): resources: list[CheckResourceAction] class ResourceTypeAction(BaseModel): resource_type: str action: str class GetAllowedTenantsResult(BaseModel): resource_type: str action: str tenants: list[Tenant] class AuthEffect(enum.StrEnum): ALLOW = "allow" DENY = "deny" class CheckResourceActionResult(BaseModel): resource: Resource effect: AuthEffect class CheckResourcesResponse(BaseModel): resources: list[CheckResourceActionResult] class OwsPdpClient: service_name = "ows-pdp" def __init__(self, ows_client: OwsClient) -> None: self.ows_client = ows_client def get_roles(self, identity_id: str) -> list[TenantWithRoles]: return list(self.iter_roles(identity_id)) def iter_roles( self, identity_id: str, *, cursor: str | None = None ) -> Iterator[TenantWithRoles]: params = {} if cursor: params["cursor"] = cursor response = self.ows_client.get( self.service_name, path=f"/identity/{identity_id}/roles/", params=params, ) response.raise_for_status() response_obj = GetRolesResponse.model_validate_json(response.content) yield from response_obj.tenants.values() if response_obj.cursor.cursor: yield from self.iter_roles(identity_id, cursor=response_obj.cursor.cursor) def check_resources( self, identity_id: str, *, resources: CheckResourcesInput ) -> list[CheckResourceActionResult]: response = self.ows_client.post( self.service_name, path=f"/identity/{identity_id}/check/resources/", json=resources.model_dump(), ) response.raise_for_status() response_obj = CheckResourcesResponse.model_validate_json(response.content) return response_obj.resources def get_allowed_tenants( self, *, resource_type: str, action: str, ) -> list[Tenant]: response = self.ows_client.post( self.service_name, path="/identity/self/allowed-tenants/", json={ "resource_type": resource_type, "action": action, }, ) response.raise_for_status() response_obj = GetAllowedTenantsResult.model_validate_json(response.content) return response_obj.tenants