Post-login lands on Spaces empty state instead of a principal dump. Profile menu exposes Session details with identity, tenant, roles/groups, and authz diagnostics for operator refinement (CSOC-WP-0004 T01/T07).
75 lines
2.9 KiB
Python
75 lines
2.9 KiB
Python
"""Build principal/session diagnostic dicts for templates (no secrets)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from django.http import HttpRequest
|
|
|
|
from coulomb_social.apps.identity import flex_auth
|
|
|
|
|
|
def display_name_for(request: HttpRequest) -> str:
|
|
member = getattr(request.user, "member", None)
|
|
if member and member.display_name:
|
|
return member.display_name
|
|
return request.user.get_full_name() or request.user.get_username()
|
|
|
|
|
|
def build_principal(request: HttpRequest, *, authz_resource_id: str = "app_home") -> dict[str, Any]:
|
|
"""Principal card fields + optional session claim diagnostics."""
|
|
member = getattr(request.user, "member", None)
|
|
subject = member.subject if member else request.user.get_username()
|
|
tenant = member.tenant_id if member else ""
|
|
decision = flex_auth.check(
|
|
"shell:view",
|
|
resource="shell",
|
|
resource_id=authz_resource_id,
|
|
subject=subject,
|
|
tenant=tenant,
|
|
attributes={
|
|
"issuer": member.issuer if member else "",
|
|
"user_engine_user_id": str(member.user_engine_user_id) if member else "",
|
|
},
|
|
)
|
|
groups = request.session.get("identity_groups") or []
|
|
roles = request.session.get("identity_roles") or []
|
|
assurance = request.session.get("identity_assurance") or {}
|
|
return {
|
|
"username": request.user.get_username(),
|
|
"display_name": display_name_for(request),
|
|
"email": (member.email if member else "") or request.user.email or "",
|
|
"issuer": member.issuer if member else "",
|
|
"subject": member.subject if member else "",
|
|
"user_engine_user_id": str(member.user_engine_user_id) if member else "",
|
|
"tenant_id": member.tenant_id if member else "",
|
|
"user_engine_source": request.session.get("user_engine_source", ""),
|
|
"principal_type": request.session.get("identity_principal_type", "") or "—",
|
|
"groups": groups,
|
|
"roles": roles,
|
|
"groups_display": ", ".join(str(g) for g in groups) if groups else "—",
|
|
"roles_display": ", ".join(str(r) for r in roles) if roles else "—",
|
|
"assurance": assurance,
|
|
"assurance_display": _format_assurance(assurance),
|
|
"authz_decision_id": decision.decision_id,
|
|
"authz_reason": decision.reason,
|
|
"authz_allow": decision.allow,
|
|
"member": member,
|
|
"decision": decision,
|
|
}
|
|
|
|
|
|
def _format_assurance(assurance: dict[str, Any]) -> str:
|
|
if not assurance:
|
|
return "—"
|
|
aal = assurance.get("aal") or assurance.get("acr") or ""
|
|
methods = assurance.get("methods") or []
|
|
mfa = assurance.get("mfa")
|
|
parts: list[str] = []
|
|
if aal:
|
|
parts.append(str(aal))
|
|
if methods:
|
|
parts.append("methods=" + ",".join(str(m) for m in methods))
|
|
if mfa is not None:
|
|
parts.append(f"mfa={mfa}")
|
|
return "; ".join(parts) if parts else str(assurance)
|