diff --git a/Keycloak-installation.md b/Keycloak-installation.md index 7a44509..d47277f 100644 --- a/Keycloak-installation.md +++ b/Keycloak-installation.md @@ -47,7 +47,7 @@ Go to **Clients → labhelper → Client scopes** tab → click the dedicated sc |---|---|---| | Name | `groups` | Label for this mapper | | Token Claim Name | `groups` | The claim name the app reads from the token | -| Full group path | Off | Sends `Lab Administrators` instead of `/Lab Administrators`. The app strips leading slashes anyway, but this is cleaner | +| Full group path | Off | Sends `LabHelper Administrators` instead of `/LabHelper Administrators`. The app strips leading slashes anyway, but this is cleaner | | Add to ID token | On | | | Add to access token | On | | | Add to userinfo | On | The app fetches userinfo after the token exchange | @@ -56,9 +56,9 @@ Go to **Clients → labhelper → Client scopes** tab → click the dedicated sc Go to **Groups** (left sidebar) and create these three groups with exactly these names — they map to the existing Django groups: -- `Lab Administrators` — gets `is_staff=True` in Django (admin access) -- `Lab Staff` -- `Lab Viewers` +- `LabHelper Administrators` — gets `is_staff=True` in Django (admin access) +- `LabHelper Staff` +- `LabHelper Viewers` ### 6. Ensure users have an email address @@ -120,7 +120,7 @@ Overrides `OIDCAuthenticationBackend` to: - Use `preferred_username` from Keycloak as the Django username - Set `first_name` and `last_name` from `given_name` / `family_name` claims - Sync group memberships on every login — if a user is added to or removed from a Keycloak group, it takes effect at their next login -- Set `is_staff=True` for members of `Lab Administrators` (grants Django admin access) +- Set `is_staff=True` for members of `LabHelper Administrators` (grants Django admin access) `django.contrib.auth.backends.ModelBackend` is kept as a fallback so the Django admin login form still works with a local username/password (useful for emergency superuser access without Keycloak). diff --git a/argocd/configmap.yaml b/argocd/configmap.yaml index 1f8ebc2..7f2eff0 100644 --- a/argocd/configmap.yaml +++ b/argocd/configmap.yaml @@ -18,6 +18,7 @@ data: OIDC_OP_BASE_URL: "https://sso.baumann.gr/realms/homelab" OIDC_RP_CLIENT_ID: "labhelper" LOGIN_REDIRECT_URL: "index" - LOGOUT_REDIRECT_URL: "login" + LOGOUT_REDIRECT_URL: "/login/" + OIDC_AUTHENTICATION_FAILURE_REDIRECT_URL: "/login/" GUNICORN_OPTS: "--access-logfile -" - IMAGE_TAG: "0.077" + IMAGE_TAG: "0.078" diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index f9643eb..01be0f2 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -27,7 +27,7 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/labhelper:0.077 + image: git.baumann.gr/adebaumann/labhelper:0.078 imagePullPolicy: Always ports: - containerPort: 8000 @@ -117,6 +117,11 @@ spec: configMapKeyRef: name: django-config key: LOGOUT_REDIRECT_URL + - name: OIDC_AUTHENTICATION_FAILURE_REDIRECT_URL + valueFrom: + configMapKeyRef: + name: django-config + key: OIDC_AUTHENTICATION_FAILURE_REDIRECT_URL - name: GUNICORN_OPTS valueFrom: configMapKeyRef: diff --git a/labhelper/auth_backend.py b/labhelper/auth_backend.py index f72c422..14376a7 100644 --- a/labhelper/auth_backend.py +++ b/labhelper/auth_backend.py @@ -2,16 +2,16 @@ from mozilla_django_oidc.auth import OIDCAuthenticationBackend from django.contrib.auth.models import Group # Keycloak group name → Django group name mapping. -# Keycloak may send group paths with a leading slash (e.g. "/Lab Administrators"); +# Keycloak may send group paths with a leading slash (e.g. "/LabHelper Administrators"); # these are stripped before comparison. KEYCLOAK_GROUP_MAP = { - 'Lab Administrators': 'Lab Administrators', - 'Lab Staff': 'Lab Staff', - 'Lab Viewers': 'Lab Viewers', + 'LabHelper Administrators': 'LabHelper Administrators', + 'LabHelper Staff': 'LabHelper Staff', + 'LabHelper Viewers': 'LabHelper Viewers', } # Members of these groups receive is_staff=True (Django admin access) -STAFF_GROUPS = {'Lab Administrators'} +STAFF_GROUPS = {'LabHelper Administrators'} class KeycloakOIDCBackend(OIDCAuthenticationBackend): @@ -35,7 +35,7 @@ class KeycloakOIDCBackend(OIDCAuthenticationBackend): user.first_name = claims.get('given_name', user.first_name) user.last_name = claims.get('family_name', user.last_name) - # Keycloak sends group paths like "/Lab Administrators"; normalise them. + # Keycloak sends group paths like "/LabHelper Administrators"; normalise them. raw_groups = claims.get('groups', []) keycloak_groups = {g.lstrip('/') for g in raw_groups} diff --git a/labhelper/management/commands/create_default_users.py b/labhelper/management/commands/create_default_users.py index eedd780..d68e2d5 100644 --- a/labhelper/management/commands/create_default_users.py +++ b/labhelper/management/commands/create_default_users.py @@ -9,9 +9,9 @@ class Command(BaseCommand): self.stdout.write('Creating default users and groups...') groups = { - 'Lab Administrators': 'Full access to all lab functions', - 'Lab Staff': 'Can view and search items, add things to boxes', - 'Lab Viewers': 'Read-only access to view and search', + 'LabHelper Administrators': 'Full access to all lab functions', + 'LabHelper Staff': 'Can view and search items, add things to boxes', + 'LabHelper Viewers': 'Read-only access to view and search', } for group_name, description in groups.items(): @@ -22,9 +22,9 @@ class Command(BaseCommand): self.stdout.write(f'Group already exists: {group_name}') users = { - 'admin': ('Lab Administrators', True), - 'staff': ('Lab Staff', False), - 'viewer': ('Lab Viewers', False), + 'admin': ('LabHelper Administrators', True), + 'staff': ('LabHelper Staff', False), + 'viewer': ('LabHelper Viewers', False), } for username, (group_name, is_superuser) in users.items(): diff --git a/labhelper/settings.py b/labhelper/settings.py index 4e4c46c..2260525 100644 --- a/labhelper/settings.py +++ b/labhelper/settings.py @@ -178,5 +178,8 @@ OIDC_OP_LOGOUT_ENDPOINT = os.environ.get('OIDC_OP_LOGOUT_ENDPOINT', f'{_oidc_con # Store the ID token in the session so Keycloak logout can use id_token_hint OIDC_STORE_ID_TOKEN = True +# Redirect to the static login page on auth failure instead of looping back into OIDC +OIDC_AUTHENTICATION_FAILURE_REDIRECT_URL = os.environ.get('OIDC_AUTHENTICATION_FAILURE_REDIRECT_URL', '/login/') + # Exempt AJAX endpoints from the session-refresh middleware redirect OIDC_EXEMPT_URLS = ['search_api']