coulomb-social/coulomb_social/apps/identity/services.py
tegwick 422cd613f6 Add app home Spaces shell and session diagnostics profile menu
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).
2026-08-11 02:31:55 +02:00

81 lines
2.7 KiB
Python

"""Session establishment after verified identity claims."""
from __future__ import annotations
from django.conf import settings
from django.contrib.auth import login
from django.http import HttpRequest
from django.utils import timezone
from coulomb_social.apps.members.models import Member, User
from .user_engine import IdentityClaims, get_user_engine_client
def establish_session(request: HttpRequest, claims: IdentityClaims) -> Member:
"""Link platform identity → Member, log Django user in. No passwords."""
tenant_id = claims.tenant or settings.DEFAULT_TENANT_ID
application_id = settings.USER_ENGINE_APPLICATION_ID
link = get_user_engine_client().link_or_create(
claims, tenant_id=tenant_id, application_id=application_id
)
member = Member.objects.filter(issuer=claims.issuer, subject=claims.subject).first()
display = (
link.display_name
or claims.name
or claims.preferred_username
or claims.email
or claims.subject
)
email = link.email or claims.email or ""
username = f"{claims.subject}@{_issuer_slug(claims.issuer)}"[:255]
if member is None:
user = User.objects.create_user(
username=username,
email=email,
full_name=display,
)
member = Member.objects.create(
tenant_id=tenant_id,
user=user,
user_engine_user_id=link.user_id,
issuer=claims.issuer,
subject=claims.subject,
display_name=display,
email=email,
)
else:
user = member.user
user.email = email or user.email
user.full_name = display or user.full_name
user.save(update_fields=["email", "full_name"])
member.display_name = display
member.email = email or member.email
member.user_engine_user_id = link.user_id
member.tenant_id = tenant_id
member.last_login_at = timezone.now()
member.save()
# Stash non-secret claim provenance for account diagnostics (T07)
request.session["user_engine_source"] = link.source
request.session["user_engine_user_id"] = link.user_id
request.session["identity_groups"] = list(claims.groups)
request.session["identity_roles"] = list(claims.roles)
request.session["identity_principal_type"] = claims.principal_type or "human"
request.session["identity_assurance"] = dict(claims.assurance or {})
login(request, user, backend="django.contrib.auth.backends.ModelBackend")
return member
def _issuer_slug(issuer: str) -> str:
return (
issuer.replace("https://", "")
.replace("http://", "")
.replace("/", "_")
.replace(":", "_")[:64]
or "issuer"
)