Source code for loom.core.repository.sqlalchemy.rls.elevate
"""Elevation wired to the product's declaration and to the active SQLAlchemy session."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Final
from sqlalchemy import text
from loom.core.authz import Decision, RoleCatalog, Scope
from loom.core.authz.elevation import elevate_scope, in_execution_frame
from loom.core.authz.product import AuthzProduct, load_authz_product
from loom.core.config import ConfigError
from loom.core.model.scoped import ScopedTable
from loom.core.repository.sqlalchemy.rls.provider import elevation_setting
from loom.core.repository.sqlalchemy.session_settings import SET_SETTINGS, settings_parameters
from loom.core.repository.sqlalchemy.transactional import get_active_session
_SET_SETTINGS: Final = text(SET_SETTINGS)
[docs]
class SQLAlchemyElevationSink:
"""Sets and clears elevation flags on the transaction of the active session."""
def in_transaction(self) -> bool:
session = get_active_session()
return session is not None and session.in_transaction()
async def set_flag(self, scope: str) -> None:
await self._apply({elevation_setting(scope): "on"})
async def clear_flags(self, scopes: frozenset[str]) -> None:
await self._apply({elevation_setting(scope): "" for scope in sorted(scopes)})
async def invalidate(self) -> None:
session = get_active_session()
if session is not None:
await session.invalidate()
@staticmethod
async def _apply(values: Mapping[str, str]) -> None:
session = get_active_session()
parameters = settings_parameters(values)
if session is None or parameters is None:
return
await session.execute(_SET_SETTINGS, parameters)
[docs]
async def elevate(
scope: str, decision: Decision, *, at: Scope, catalog: RoleCatalog | None = None
) -> None:
"""Elevate ``scope`` for the current execution after checking ``decision``.
Raises:
RuntimeError: Outside an execution frame.
ConfigError: Without a product, or when the product did not map ``scope``.
PermissionError: When the decision does not justify the elevation.
"""
if not in_execution_frame():
raise RuntimeError("elevate requires an open execution frame")
product = load_authz_product()
if product is None:
raise ConfigError("no authorization product is registered under the loom.authz entry point")
permission = product.elevations.get(scope)
if permission is None:
raise ConfigError(f"scope {scope!r} is not declared in AuthzProduct.elevations")
await elevate_scope(
scope, decision, at=at, permission=permission, catalog=catalog or product.catalog
)
[docs]
def validate_elevations(
product: AuthzProduct, scoped: Mapping[tuple[str | None, str], ScopedTable]
) -> None:
"""Fail at startup when ``elevations`` names a scope that is unknown or not elevable."""
elevable = {c.scope for table in scoped.values() for c in table.scopes if c.elevable}
declared = {c.scope for table in scoped.values() for c in table.scopes}
for scope in product.elevations:
if scope not in declared:
raise ConfigError(f"AuthzProduct.elevations names unknown scope {scope!r}")
if scope not in elevable:
raise ConfigError(
f"AuthzProduct.elevations names scope {scope!r}, which is not elevable"
)