"""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)