187 lines
6.8 KiB
Python
187 lines
6.8 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
|
|
from sqlalchemy import or_
|
|
from sqlalchemy.orm import Session
|
|
|
|
from govoplan_access.backend.db.models import (
|
|
Account,
|
|
ExternalFunctionRoleAssignment,
|
|
Function,
|
|
FunctionAssignment,
|
|
FunctionDelegation,
|
|
FunctionRoleAssignment,
|
|
Identity,
|
|
IdentityAccountLink,
|
|
Role,
|
|
User,
|
|
)
|
|
from govoplan_core.core.idm import OrganizationFunctionAssignmentRef
|
|
from govoplan_core.security.time import utc_now
|
|
|
|
|
|
def primary_identity_for_account(session: Session, account_id: str) -> Identity | None:
|
|
return (
|
|
session.query(Identity)
|
|
.join(IdentityAccountLink, IdentityAccountLink.identity_id == Identity.id)
|
|
.filter(
|
|
IdentityAccountLink.account_id == account_id,
|
|
IdentityAccountLink.is_primary.is_(True),
|
|
Identity.is_active.is_(True),
|
|
)
|
|
.order_by(IdentityAccountLink.created_at.asc())
|
|
.first()
|
|
)
|
|
|
|
|
|
def identity_id_for_account(session: Session, account_id: str) -> str | None:
|
|
identity = primary_identity_for_account(session, account_id)
|
|
return identity.id if identity is not None else None
|
|
|
|
|
|
def ensure_identity_for_account(session: Session, account: Account, *, source: str = "local") -> Identity:
|
|
identity = primary_identity_for_account(session, account.id)
|
|
if identity is not None:
|
|
return identity
|
|
identity = Identity(display_name=account.display_name or account.email, source=source)
|
|
session.add(identity)
|
|
session.flush()
|
|
session.add(IdentityAccountLink(identity_id=identity.id, account_id=account.id, is_primary=True, source=source))
|
|
session.flush()
|
|
return identity
|
|
|
|
|
|
def active_function_assignments_for_account(
|
|
session: Session,
|
|
account_id: str,
|
|
*,
|
|
tenant_id: str | None = None,
|
|
) -> list[FunctionAssignment]:
|
|
now = utc_now()
|
|
query = (
|
|
session.query(FunctionAssignment)
|
|
.join(Function, Function.id == FunctionAssignment.function_id)
|
|
.filter(
|
|
FunctionAssignment.account_id == account_id,
|
|
FunctionAssignment.is_active.is_(True),
|
|
Function.is_active.is_(True),
|
|
or_(FunctionAssignment.valid_from.is_(None), FunctionAssignment.valid_from <= now),
|
|
or_(FunctionAssignment.valid_until.is_(None), FunctionAssignment.valid_until > now),
|
|
)
|
|
.order_by(FunctionAssignment.created_at.asc())
|
|
)
|
|
if tenant_id is not None:
|
|
query = query.filter(FunctionAssignment.tenant_id == tenant_id, Function.tenant_id == tenant_id)
|
|
return query.all()
|
|
|
|
|
|
def active_function_delegations_for_account(
|
|
session: Session,
|
|
account_id: str,
|
|
*,
|
|
tenant_id: str | None = None,
|
|
modes: Iterable[str] = ("delegate",),
|
|
) -> list[FunctionDelegation]:
|
|
now = utc_now()
|
|
mode_set = sorted({str(mode) for mode in modes})
|
|
if not mode_set:
|
|
return []
|
|
query = (
|
|
session.query(FunctionDelegation)
|
|
.join(FunctionAssignment, FunctionAssignment.id == FunctionDelegation.function_assignment_id)
|
|
.join(Function, Function.id == FunctionAssignment.function_id)
|
|
.filter(
|
|
FunctionDelegation.delegate_account_id == account_id,
|
|
FunctionDelegation.mode.in_(mode_set),
|
|
FunctionDelegation.is_active.is_(True),
|
|
FunctionDelegation.revoked_at.is_(None),
|
|
FunctionAssignment.is_active.is_(True),
|
|
Function.is_active.is_(True),
|
|
Function.delegable.is_(True),
|
|
or_(FunctionDelegation.valid_from.is_(None), FunctionDelegation.valid_from <= now),
|
|
or_(FunctionDelegation.valid_until.is_(None), FunctionDelegation.valid_until > now),
|
|
or_(FunctionAssignment.valid_from.is_(None), FunctionAssignment.valid_from <= now),
|
|
or_(FunctionAssignment.valid_until.is_(None), FunctionAssignment.valid_until > now),
|
|
)
|
|
.order_by(FunctionDelegation.created_at.asc())
|
|
)
|
|
if tenant_id is not None:
|
|
query = query.filter(FunctionDelegation.tenant_id == tenant_id, FunctionAssignment.tenant_id == tenant_id)
|
|
return query.all()
|
|
|
|
|
|
def collect_function_assignment_ids(session: Session, user: User) -> list[str]:
|
|
ids = [item.id for item in active_function_assignments_for_account(session, user.account_id, tenant_id=user.tenant_id)]
|
|
for delegation in active_function_delegations_for_account(session, user.account_id, tenant_id=user.tenant_id):
|
|
ids.append(delegation.function_assignment_id)
|
|
return sorted(dict.fromkeys(ids))
|
|
|
|
|
|
def collect_function_delegation_ids(session: Session, user: User) -> list[str]:
|
|
return [
|
|
item.id
|
|
for item in active_function_delegations_for_account(
|
|
session,
|
|
user.account_id,
|
|
tenant_id=user.tenant_id,
|
|
modes=("delegate", "act_in_place"),
|
|
)
|
|
]
|
|
|
|
|
|
def collect_function_roles(session: Session, user: User) -> list[Role]:
|
|
assignments = active_function_assignments_for_account(session, user.account_id, tenant_id=user.tenant_id)
|
|
delegated = active_function_delegations_for_account(session, user.account_id, tenant_id=user.tenant_id, modes=("delegate",))
|
|
assignment_ids = {assignment.id for assignment in assignments}
|
|
assignment_ids.update(delegation.function_assignment_id for delegation in delegated)
|
|
if not assignment_ids:
|
|
return []
|
|
function_ids = [
|
|
row[0]
|
|
for row in session.query(FunctionAssignment.function_id)
|
|
.filter(FunctionAssignment.tenant_id == user.tenant_id, FunctionAssignment.id.in_(assignment_ids))
|
|
.all()
|
|
]
|
|
if not function_ids:
|
|
return []
|
|
return (
|
|
session.query(Role)
|
|
.join(FunctionRoleAssignment, FunctionRoleAssignment.role_id == Role.id)
|
|
.filter(
|
|
FunctionRoleAssignment.tenant_id == user.tenant_id,
|
|
FunctionRoleAssignment.function_id.in_(function_ids),
|
|
Role.tenant_id == user.tenant_id,
|
|
)
|
|
.order_by(Role.name.asc())
|
|
.all()
|
|
)
|
|
|
|
|
|
def collect_external_function_roles(
|
|
session: Session,
|
|
user: User,
|
|
assignments: Iterable[OrganizationFunctionAssignmentRef],
|
|
*,
|
|
source_module: str = "organizations",
|
|
) -> list[Role]:
|
|
function_ids = sorted({
|
|
assignment.function_id
|
|
for assignment in assignments
|
|
if assignment.tenant_id == user.tenant_id and assignment.status == "active"
|
|
})
|
|
if not function_ids:
|
|
return []
|
|
return (
|
|
session.query(Role)
|
|
.join(ExternalFunctionRoleAssignment, ExternalFunctionRoleAssignment.role_id == Role.id)
|
|
.filter(
|
|
ExternalFunctionRoleAssignment.tenant_id == user.tenant_id,
|
|
ExternalFunctionRoleAssignment.source_module == source_module,
|
|
ExternalFunctionRoleAssignment.function_id.in_(function_ids),
|
|
Role.tenant_id == user.tenant_id,
|
|
)
|
|
.order_by(Role.name.asc())
|
|
.all()
|
|
)
|