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.
43 lines
1.6 KiB
Python
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)
|