Prefer canonical identity and organization directories
This commit is contained in:
@@ -13,7 +13,14 @@ from govoplan_core.core.access import (
|
||||
OrganizationUnitRef,
|
||||
UserRef,
|
||||
)
|
||||
from govoplan_core.core.identity import IdentityDirectory, IdentityRef as DirectoryIdentityRef
|
||||
from govoplan_core.core.idm import IdmDirectory, OrganizationFunctionAssignmentRef as IdmFunctionAssignmentRef
|
||||
from govoplan_core.core.organizations import (
|
||||
ORGANIZATIONS_MODULE_ID,
|
||||
OrganizationDirectory,
|
||||
OrganizationFunctionRef as DirectoryFunctionRef,
|
||||
OrganizationUnitRef as DirectoryOrganizationUnitRef,
|
||||
)
|
||||
from govoplan_access.backend.db.models import (
|
||||
Account,
|
||||
Function,
|
||||
@@ -73,6 +80,16 @@ def _identity_ref(identity: Identity, account_links: list[IdentityAccountLink])
|
||||
)
|
||||
|
||||
|
||||
def _directory_identity_ref(identity: DirectoryIdentityRef) -> IdentityRef:
|
||||
return IdentityRef(
|
||||
id=identity.id,
|
||||
display_name=identity.display_name,
|
||||
primary_account_id=identity.primary_account_id,
|
||||
account_ids=tuple(identity.account_ids),
|
||||
status=identity.status, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
|
||||
def _organization_unit_ref(item: OrganizationUnit) -> OrganizationUnitRef:
|
||||
return OrganizationUnitRef(
|
||||
id=item.id,
|
||||
@@ -83,6 +100,16 @@ def _organization_unit_ref(item: OrganizationUnit) -> OrganizationUnitRef:
|
||||
)
|
||||
|
||||
|
||||
def _directory_organization_unit_ref(item: DirectoryOrganizationUnitRef) -> OrganizationUnitRef:
|
||||
return OrganizationUnitRef(
|
||||
id=item.id,
|
||||
tenant_id=item.tenant_id,
|
||||
name=item.name,
|
||||
parent_id=item.parent_id,
|
||||
status=item.status, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
|
||||
def _function_ref(function: Function, role_ids: Iterable[str]) -> FunctionRef:
|
||||
return FunctionRef(
|
||||
id=function.id,
|
||||
@@ -97,6 +124,20 @@ def _function_ref(function: Function, role_ids: Iterable[str]) -> FunctionRef:
|
||||
)
|
||||
|
||||
|
||||
def _directory_function_ref(function: DirectoryFunctionRef, role_ids: Iterable[str]) -> FunctionRef:
|
||||
return FunctionRef(
|
||||
id=function.id,
|
||||
tenant_id=function.tenant_id,
|
||||
organization_unit_id=function.organization_unit_id,
|
||||
slug=function.slug,
|
||||
name=function.name,
|
||||
role_ids=tuple(role_ids),
|
||||
delegable=function.delegable,
|
||||
act_in_place_allowed=function.act_in_place_allowed,
|
||||
status=function.status, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
|
||||
def _function_assignment_ref(item: FunctionAssignment) -> FunctionAssignmentRef:
|
||||
return FunctionAssignmentRef(
|
||||
id=item.id,
|
||||
@@ -134,8 +175,15 @@ def _idm_function_assignment_ref(item: IdmFunctionAssignmentRef, *, account_id:
|
||||
|
||||
|
||||
class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
def __init__(self, idm_directory: IdmDirectory | None = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
idm_directory: IdmDirectory | None = None,
|
||||
identity_directory: IdentityDirectory | None = None,
|
||||
organization_directory: OrganizationDirectory | None = None,
|
||||
) -> None:
|
||||
self._idm_directory = idm_directory
|
||||
self._identity_directory = identity_directory
|
||||
self._organization_directory = organization_directory
|
||||
|
||||
def get_account(self, account_id: str) -> AccountRef | None:
|
||||
with get_database().session() as session:
|
||||
@@ -230,6 +278,10 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
return subject.label or subject.id
|
||||
|
||||
def get_identity(self, identity_id: str) -> IdentityRef | None:
|
||||
if self._identity_directory is not None:
|
||||
identity = self._identity_directory.get_identity(identity_id)
|
||||
if identity is not None:
|
||||
return _directory_identity_ref(identity)
|
||||
with get_database().session() as session:
|
||||
identity = session.get(Identity, identity_id)
|
||||
if identity is None:
|
||||
@@ -243,6 +295,16 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
return _identity_ref(identity, links)
|
||||
|
||||
def accounts_for_identity(self, identity_id: str) -> tuple[AccountRef, ...]:
|
||||
if self._identity_directory is not None:
|
||||
links = tuple(self._identity_directory.accounts_for_identity(identity_id))
|
||||
if links:
|
||||
account_ids = [link.account_id for link in links]
|
||||
with get_database().session() as session:
|
||||
accounts = {
|
||||
account.id: account
|
||||
for account in session.query(Account).filter(Account.id.in_(account_ids)).all()
|
||||
}
|
||||
return tuple(_account_ref(accounts[account_id]) for account_id in account_ids if account_id in accounts)
|
||||
with get_database().session() as session:
|
||||
accounts = (
|
||||
session.query(Account)
|
||||
@@ -254,11 +316,19 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
return tuple(_account_ref(account) for account in accounts)
|
||||
|
||||
def get_organization_unit(self, organization_unit_id: str) -> OrganizationUnitRef | None:
|
||||
if self._organization_directory is not None:
|
||||
item = self._organization_directory.get_organization_unit(organization_unit_id)
|
||||
if item is not None:
|
||||
return _directory_organization_unit_ref(item)
|
||||
with get_database().session() as session:
|
||||
item = session.get(OrganizationUnit, organization_unit_id)
|
||||
return _organization_unit_ref(item) if item is not None else None
|
||||
|
||||
def organization_units_for_tenant(self, tenant_id: str) -> tuple[OrganizationUnitRef, ...]:
|
||||
if self._organization_directory is not None:
|
||||
items = tuple(self._organization_directory.organization_units_for_tenant(tenant_id))
|
||||
if items:
|
||||
return tuple(_directory_organization_unit_ref(item) for item in items)
|
||||
with get_database().session() as session:
|
||||
items = (
|
||||
session.query(OrganizationUnit)
|
||||
@@ -269,6 +339,10 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
return tuple(_organization_unit_ref(item) for item in items)
|
||||
|
||||
def get_function(self, function_id: str) -> FunctionRef | None:
|
||||
if self._organization_directory is not None:
|
||||
function = self._organization_directory.get_function(function_id)
|
||||
if function is not None:
|
||||
return _directory_function_ref(function, self._external_function_role_ids(function.id, tenant_id=function.tenant_id))
|
||||
with get_database().session() as session:
|
||||
function = session.get(Function, function_id)
|
||||
if function is None:
|
||||
@@ -288,6 +362,22 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
*,
|
||||
include_subunits: bool = False,
|
||||
) -> tuple[FunctionRef, ...]:
|
||||
if self._organization_directory is not None:
|
||||
functions = tuple(
|
||||
self._organization_directory.functions_for_organization_unit(
|
||||
organization_unit_id,
|
||||
include_subunits=include_subunits,
|
||||
)
|
||||
)
|
||||
if functions:
|
||||
role_ids_by_function = self._external_function_role_ids_by_function(
|
||||
[item.id for item in functions],
|
||||
tenant_id=functions[0].tenant_id,
|
||||
)
|
||||
return tuple(
|
||||
_directory_function_ref(item, role_ids_by_function.get(item.id, ()))
|
||||
for item in functions
|
||||
)
|
||||
with get_database().session() as session:
|
||||
unit_ids = {organization_unit_id}
|
||||
if include_subunits:
|
||||
@@ -345,3 +435,33 @@ class SqlAccessDirectory(AccessSemanticDirectory):
|
||||
)
|
||||
deduped = {item.id: item for item in assignments}
|
||||
return tuple(deduped.values())
|
||||
|
||||
def _external_function_role_ids(self, function_id: str, *, tenant_id: str) -> tuple[str, ...]:
|
||||
return self._external_function_role_ids_by_function([function_id], tenant_id=tenant_id).get(function_id, ())
|
||||
|
||||
def _external_function_role_ids_by_function(
|
||||
self,
|
||||
function_ids: Iterable[str],
|
||||
*,
|
||||
tenant_id: str,
|
||||
) -> dict[str, tuple[str, ...]]:
|
||||
ids = sorted({str(function_id) for function_id in function_ids if function_id})
|
||||
if not ids:
|
||||
return {}
|
||||
from govoplan_access.backend.db.models import ExternalFunctionRoleAssignment
|
||||
|
||||
with get_database().session() as session:
|
||||
rows = (
|
||||
session.query(ExternalFunctionRoleAssignment.function_id, ExternalFunctionRoleAssignment.role_id)
|
||||
.filter(
|
||||
ExternalFunctionRoleAssignment.tenant_id == tenant_id,
|
||||
ExternalFunctionRoleAssignment.source_module == ORGANIZATIONS_MODULE_ID,
|
||||
ExternalFunctionRoleAssignment.function_id.in_(ids),
|
||||
)
|
||||
.order_by(ExternalFunctionRoleAssignment.created_at.asc())
|
||||
.all()
|
||||
)
|
||||
result: dict[str, list[str]] = {}
|
||||
for function_id, role_id in rows:
|
||||
result.setdefault(function_id, []).append(role_id)
|
||||
return {function_id: tuple(role_ids) for function_id, role_ids in result.items()}
|
||||
|
||||
Reference in New Issue
Block a user