Wire user-engine HTTP /me for member provisioning (CSOC-WP-0002-T04)

HttpUserEngineClient uses trusted-proxy claims against live user-engine.
Offline stub when URL/secret unset. Align default tenant with KeyCape
tenant:coulomb; map OIDC tenant/principal_type/groups into the envelope.
This commit is contained in:
tegwick 2026-08-09 01:56:44 +02:00
parent a6a380b19f
commit d88767f05b
13 changed files with 325 additions and 48 deletions

View file

@ -77,13 +77,7 @@ def oidc_callback(request: HttpRequest) -> HttpResponse:
return HttpResponseBadRequest("Token missing subject")
issuer = raw.get("iss") or settings.OIDC_ISSUER
claims = IdentityClaims(
issuer=str(issuer),
subject=str(sub),
email=str(raw.get("email") or ""),
name=str(raw.get("name") or ""),
preferred_username=str(raw.get("preferred_username") or ""),
)
claims = _claims_from_oidc_payload(raw, issuer=str(issuer), subject=str(sub))
establish_session(request, claims)
return redirect(settings.LOGIN_REDIRECT_URL)
@ -105,11 +99,52 @@ def dev_login(request: HttpRequest) -> HttpResponse:
email=(request.POST.get("email") or "").strip(),
name=(request.POST.get("name") or "").strip(),
preferred_username=(request.POST.get("preferred_username") or "").strip(),
tenant=(request.POST.get("tenant") or settings.DEFAULT_TENANT_ID).strip(),
principal_type="human",
roles=("user",),
authorized_party=settings.USER_ENGINE_APPLICATION_ID,
)
establish_session(request, claims)
return redirect(settings.LOGIN_REDIRECT_URL)
return render(request, "identity/dev_login.html")
return render(
request,
"identity/dev_login.html",
{"default_tenant": settings.DEFAULT_TENANT_ID},
)
def _claims_from_oidc_payload(
raw: dict, *, issuer: str, subject: str
) -> IdentityClaims:
aud = raw.get("aud", ())
if isinstance(aud, str):
audience = (aud,)
else:
audience = tuple(str(a) for a in (aud or ()))
groups = raw.get("groups") or ()
if isinstance(groups, str):
groups = (groups,)
roles = raw.get("roles") or raw.get("tenant_roles") or ()
if isinstance(roles, str):
roles = (roles,)
assurance = raw.get("assurance") if isinstance(raw.get("assurance"), dict) else {}
return IdentityClaims(
issuer=issuer,
subject=subject,
email=str(raw.get("email") or ""),
name=str(raw.get("name") or ""),
preferred_username=str(raw.get("preferred_username") or ""),
tenant=str(raw.get("tenant") or settings.DEFAULT_TENANT_ID),
principal_type=str(raw.get("principal_type") or "human"),
groups=tuple(str(g) for g in groups),
roles=tuple(str(r) for r in roles) or ("user",),
assurance=dict(assurance),
audience=audience,
authorized_party=str(
raw.get("azp") or raw.get("client_id") or settings.OIDC_CLIENT_ID or ""
),
)
@require_http_methods(["GET", "POST"])