ixg_platform/apps/users/auth.py
ixgadmin 19d67555da Add public buyer self-registration
A UK buyer can now create their own account at /portal/signup/. The screen
creates a Keycloak user through a least-privilege service account
(ixg-provisioning: manage-users, view-users) and the matching Django row as
a buyer, then sends them through the normal sign-in flow to the portal.

Staff status is derived from the role in the OIDC backend rather than
asserted, so a self-registered buyer is not staff and does not become staff
on a later sign-in. The platform root and the RBAC middleware now route
each role to its own portal, which also fixes a redirect loop that the
staff fix exposed for non-staff buyers.
2026-09-21 11:30:29 +00:00

43 lines
1.6 KiB
Python

from mozilla_django_oidc.auth import OIDCAuthenticationBackend
# Roles that carry platform privileges. A buyer is not staff: staff status is
# derived from the role, never asserted, so a self-registered buyer cannot come
# out as staff (or flip to staff on a later sign-in).
STAFF_ROLES = {"ops", "admin"}
def is_staff_for_role(role, *, is_superuser=False):
return bool(is_superuser) or role in STAFF_ROLES
class KeycloakOIDCBackend(OIDCAuthenticationBackend):
def create_user(self, claims):
email = claims.get("email", "")
role = claims.get("role", "buyer")
if role not in dict(self.UserModel.Roles.choices):
role = self.UserModel.Roles.BUYER
user = self.UserModel.objects.create_user(
email=email,
full_name=claims.get("name", ""),
keycloak_id=claims.get("sub", ""),
role=role,
is_staff=is_staff_for_role(role),
)
return user
def update_user(self, user, claims):
user.email = claims.get("email", user.email)
user.full_name = claims.get("name", user.full_name)
role = claims.get("role", user.role)
if role in dict(self.UserModel.Roles.choices):
user.role = role
user.is_staff = is_staff_for_role(user.role, is_superuser=user.is_superuser)
user.save()
return user
def filter_users_by_claims(self, claims):
email = claims.get("email")
if not email:
return self.UserModel.objects.none()
return self.UserModel.objects.filter(email__iexact=email)