From 4d6d0b89b24db382aef65343981deea127cdd341 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 12 Jan 2026 23:24:09 -0300 Subject: [PATCH] planka: default users to project owners --- services/keycloak/realm-settings-job.yaml | 103 +++++++++++++++++++--- services/planka/deployment.yaml | 4 +- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/services/keycloak/realm-settings-job.yaml b/services/keycloak/realm-settings-job.yaml index 108f141..bdc816d 100644 --- a/services/keycloak/realm-settings-job.yaml +++ b/services/keycloak/realm-settings-job.yaml @@ -2,7 +2,7 @@ apiVersion: batch/v1 kind: Job metadata: - name: keycloak-realm-settings-15 + name: keycloak-realm-settings-16 namespace: sso spec: backoffLimit: 0 @@ -227,21 +227,23 @@ spec: if status not in (200, 204): raise SystemExit(f"Unexpected user-profile update response: {status}") - # Ensure basic realm groups exist for provisioning. - for group_name in ("dev", "admin"): + def find_group(group_name: str): status, groups = http_json( "GET", f"{base_url}/admin/realms/{realm}/groups?search={urllib.parse.quote(group_name)}", access_token, ) - exists = False - if status == 200 and isinstance(groups, list): - for item in groups: - if isinstance(item, dict) and item.get("name") == group_name: - exists = True - break - if exists: - continue + if status != 200 or not isinstance(groups, list): + return None + for item in groups: + if isinstance(item, dict) and item.get("name") == group_name: + return item + return None + + def ensure_group(group_name: str): + group = find_group(group_name) + if group: + return group status, _ = http_json( "POST", f"{base_url}/admin/realms/{realm}/groups", @@ -250,6 +252,85 @@ spec: ) if status not in (201, 204): raise SystemExit(f"Unexpected group create response for {group_name}: {status}") + return find_group(group_name) + + # Ensure basic realm groups exist for provisioning. + ensure_group("dev") + ensure_group("admin") + planka_group = ensure_group("planka-users") + + if planka_group and planka_group.get("id"): + group_id = planka_group["id"] + status, default_groups = http_json( + "GET", + f"{base_url}/admin/realms/{realm}/default-groups", + access_token, + ) + default_ids = set() + if status == 200 and isinstance(default_groups, list): + for item in default_groups: + if isinstance(item, dict) and item.get("id"): + default_ids.add(item["id"]) + + if group_id not in default_ids: + status, _ = http_json( + "PUT", + f"{base_url}/admin/realms/{realm}/default-groups/{group_id}", + access_token, + ) + if status not in (200, 201, 204): + status, _ = http_json( + "POST", + f"{base_url}/admin/realms/{realm}/default-groups/{group_id}", + access_token, + ) + if status not in (200, 201, 204): + raise SystemExit( + f"Unexpected default-group update response for planka-users: {status}" + ) + + # Ensure all existing users are in the planka-users group. + first = 0 + page_size = 100 + while True: + status, users = http_json( + "GET", + f"{base_url}/admin/realms/{realm}/users?first={first}&max={page_size}", + access_token, + ) + if status != 200 or not isinstance(users, list) or not users: + break + for user in users: + user_id = user.get("id") if isinstance(user, dict) else None + if not user_id: + continue + status, groups = http_json( + "GET", + f"{base_url}/admin/realms/{realm}/users/{user_id}/groups", + access_token, + ) + if status == 200 and isinstance(groups, list): + already = any(isinstance(g, dict) and g.get("id") == group_id for g in groups) + if already: + continue + status, _ = http_json( + "PUT", + f"{base_url}/admin/realms/{realm}/users/{user_id}/groups/{group_id}", + access_token, + ) + if status not in (200, 201, 204): + status, _ = http_json( + "POST", + f"{base_url}/admin/realms/{realm}/users/{user_id}/groups/{group_id}", + access_token, + ) + if status not in (200, 201, 204): + raise SystemExit( + f"Unexpected group membership update for user {user_id}: {status}" + ) + if len(users) < page_size: + break + first += page_size # Ensure Planka client exposes groups in userinfo for role mapping. status, clients = http_json( diff --git a/services/planka/deployment.yaml b/services/planka/deployment.yaml index 36fe4fd..9524245 100644 --- a/services/planka/deployment.yaml +++ b/services/planka/deployment.yaml @@ -68,8 +68,10 @@ spec: value: "true" - name: OIDC_IGNORE_ROLES value: "false" + - name: OIDC_ADMIN_ROLES + value: admin - name: OIDC_PROJECT_OWNER_ROLES - value: "*" + value: planka-users - name: OIDC_ROLES_ATTRIBUTE value: groups envFrom: