feat: add access module boundary migrations
This commit is contained in:
156
src/govoplan_access/backend/semantic.py
Normal file
156
src/govoplan_access/backend/semantic.py
Normal file
@@ -0,0 +1,156 @@
|
||||
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,
|
||||
Function,
|
||||
FunctionAssignment,
|
||||
FunctionDelegation,
|
||||
FunctionRoleAssignment,
|
||||
Identity,
|
||||
IdentityAccountLink,
|
||||
Role,
|
||||
User,
|
||||
)
|
||||
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()
|
||||
)
|
||||
Reference in New Issue
Block a user