Add identity trust administration surfaces

This commit is contained in:
2026-08-04 01:04:39 +02:00
parent 0ffc8b6b0f
commit 9640ec29dd
16 changed files with 1143 additions and 10 deletions
+112 -5
View File
@@ -31,6 +31,10 @@ class IdentityTrustError(ValueError):
pass
class IdentityTrustAccessDenied(IdentityTrustError):
pass
class SqlIdentityTrustService:
def register_device_key(
self,
@@ -148,10 +152,8 @@ class SqlIdentityTrustService:
) -> tuple[DeviceKeyRef, ...]:
db = _session(session)
_require_tenant(principal, tenant_id)
if account_id != _account_id(principal) and not _has_scope(
principal, "identity_trust:device:read_all"
):
raise IdentityTrustError(
if not _may_read_account(principal, account_id):
raise IdentityTrustAccessDenied(
"The acting account cannot inspect these device keys."
)
statement = select(DevicePublicKey).where(
@@ -170,6 +172,100 @@ class SqlIdentityTrustService:
)
)
def list_assurance_evidence(
self,
session: object,
principal: object,
*,
tenant_id: str,
account_id: str,
active_only: bool = False,
limit: int = 200,
) -> tuple[AssuranceEvidence, ...]:
db = _session(session)
_require_tenant(principal, tenant_id)
if not _may_read_account(principal, account_id):
raise IdentityTrustAccessDenied(
"The acting account cannot inspect this assurance evidence."
)
statement = select(AssuranceEvidence).where(
AssuranceEvidence.tenant_id == tenant_id,
AssuranceEvidence.account_id == account_id,
)
if active_only:
statement = statement.where(
AssuranceEvidence.expires_at >= _as_utc(utcnow())
)
return tuple(
db.scalars(
statement.order_by(
AssuranceEvidence.verified_at.desc(),
AssuranceEvidence.id,
).limit(max(1, min(int(limit), 500)))
)
)
def list_epochs(
self,
session: object,
principal: object,
*,
tenant_id: str,
subject_kind: str,
subject_id: str,
limit: int = 200,
) -> tuple[KeyEpochRef, ...]:
db = _session(session)
_require_tenant(principal, tenant_id)
if not _has_scope(principal, "identity_trust:device:admin"):
raise IdentityTrustAccessDenied(
"Identity Trust administration is required to inspect key epochs."
)
values = db.scalars(
select(TrustKeyEpoch)
.where(
TrustKeyEpoch.tenant_id == tenant_id,
TrustKeyEpoch.subject_kind == subject_kind,
TrustKeyEpoch.subject_id == subject_id,
)
.order_by(TrustKeyEpoch.epoch.desc())
.limit(max(1, min(int(limit), 500)))
)
return tuple(_epoch_ref(value) for value in values)
def list_key_access_decisions(
self,
session: object,
principal: object,
*,
tenant_id: str,
account_id: str,
limit: int = 200,
) -> tuple[KeyAccessDecisionRecord, ...]:
db = _session(session)
_require_tenant(principal, tenant_id)
if not (
_has_scope(principal, "identity_trust:key_access:approve")
or _has_scope(principal, "identity_trust:device:admin")
):
raise IdentityTrustAccessDenied(
"Key-access decision authority is required to inspect decisions."
)
return tuple(
db.scalars(
select(KeyAccessDecisionRecord)
.where(
KeyAccessDecisionRecord.tenant_id == tenant_id,
KeyAccessDecisionRecord.account_id == account_id,
)
.order_by(
KeyAccessDecisionRecord.created_at.desc(),
KeyAccessDecisionRecord.id,
)
.limit(max(1, min(int(limit), 500)))
)
)
def rotate_epoch(
self,
session: object,
@@ -592,7 +688,9 @@ def _session(value: object) -> Session:
def _require_tenant(principal: object, tenant_id: str) -> None:
if str(getattr(principal, "tenant_id", "")) != tenant_id:
raise IdentityTrustError("Cross-tenant identity-trust access is denied.")
raise IdentityTrustAccessDenied(
"Cross-tenant identity-trust access is denied."
)
def _account_id(principal: object) -> str:
@@ -605,6 +703,14 @@ def _has_scope(principal: object, scope: str) -> bool:
return scope in set(getattr(principal, "scopes", ()))
def _may_read_account(principal: object, account_id: str) -> bool:
return (
account_id == _account_id(principal)
or _has_scope(principal, "identity_trust:device:read_all")
or _has_scope(principal, "identity_trust:device:admin")
)
def _required(value: str, label: str) -> str:
cleaned = value.strip()
if not cleaned:
@@ -630,6 +736,7 @@ def _as_utc(value: datetime) -> datetime:
__all__ = [
"IdentityTrustAccessDenied",
"IdentityTrustError",
"SqlIdentityTrustService",
"record_assurance_evidence",