Partners (e.g. Maple Surgery) have their own Keycloak realm for authentication. Each partner org admin (ORG_ADMIN role) can manage users within their own org via both:
maple-surgery.gp.veripath.co.uk → TenantMiddleware → PartnerOrg lookup (slug=maple-surgery)request.partner_org is set on the requestrequest.tenant defaults to the GP tenant (fallback for sector-based routing)get_current_partner_org() thread-local available for the OIDC backend and routerdefault database alongside registry dataorg_id field for scopingOrgScopedQuerySetMixin available for filtering querysets by user.org_id{partner-slug}.gp.veripath.co.uk → not authenticated → redirect to OIDCTenantAwareOIDCRequestView detects request.partner_org → builds realm-specific auth URLauth.veripath.co.uk/realms/{realm}/...)role=ORG_ADMIN, is_superuser=False, org_id from JWT/partner/users/)| Item | Details |
|---|---|
| PartnerOrg model | slug, name, keycloak_realm, is_active |
| TenantMiddleware | Falls back to PartnerOrg lookup → sets request.partner_org |
| CustomUser model | Added ORG_ADMIN role, org_id field |
| Dynamic OIDC backend | get_settings() override returns realm-specific endpoints |
| Realm-specific auth view | TenantAwareOIDCRequestView constructs realm auth URLs |
| org_id on models | Added to Appointment, ClinicalNote, Prescription, PatientHealthProfile |
| CSRF fix | csrf_exempt on OIDC callback for cross-domain redirect |
| Nginx wildcard | *.gp.veripath.co.uk server block with per-subdomain SSL |
| Keycloak realm | maple-surgery created with gp-booking-app client |
| Test user | admin.maple / Password1 with org_id=maple-surgery in JWT |
| Protocol mapper | org_id user attribute → JWT claim |
| Basic user list | GET /partner/users/ shows users, form to create new users |
| Login redirect | ORG_ADMIN → /partner/users/ after login |
| RBAC | /partner/ paths allowed for ORG_ADMIN |
| AI agents disabled | Removed from base template, partner org check added |
manage-users + view-users roles from realm-management client to admin.maplehttps://auth.veripath.co.uk/admin/{realm}/console/| View | Route | Description |
|---|---|---|
| Partner Dashboard | GET /partner/ |
Org info card, user count, quick links |
| User List (enhanced) | GET /partner/users/ |
Add search by username/email, role badge, action buttons (View, Disable, Reset Password) |
| User Detail | GET /partner/users/<id>/ |
Show username, email, status, created date; role dropdown, enable/disable toggle, reset password button |
| Enable/Disable | POST /partner/users/<id>/toggle/ |
Toggle user enabled status in Keycloak |
| Password Reset | POST /partner/users/<id>/reset-password/ |
Set new temporary password via Keycloak admin API |
| Role Change | POST /partner/users/<id>/role/ |
Update user role (for future when more roles exist) |
| File | Action | Purpose |
|---|---|---|
partner/views.py |
Modify | Add PartnerDashboardView, PartnerUserDetailView; enhance PartnerUserListView |
partner/urls.py |
Modify | Add routes for dashboard, user detail, toggle, password reset |
partner/utils.py |
Modify | Add get_user(), toggle_user(), reset_password(), update_role() to KeycloakAdminClient |
partner/templates/partner/dashboard.html |
Create | Partner landing page |
partner/templates/partner/user_list.html |
Modify | Add search, filter, role badge, action buttons |
partner/templates/partner/user_detail.html |
Create | User detail/edit view |
templates/base.html |
Modify | Update nav — add "Dashboard" link for partner orgs |
ORG_ADMIN on /partner/CustomUser.Roles already has ORG_ADMINAfter granting roles, partner admins can access:
https://auth.veripath.co.uk/admin/{realm}/console/
Login with the partner user's credentials. They will see:
gp-booking-app client (read-only by default)They will not see:
org_id attribute set in Keycloak (the Django user management page sets this automatically)org_id in the JWT is mapped by a protocol mapper on the gp-booking-app client*.gp.veripath.co.uk) via Let's Encrypt DNS-01 challenge is recommended