#!/usr/bin/env bash # Ensure Keycloak users are in the GitOps admin group used by weave-gitops (cd.bstein.dev). # Weave GitOps relies on OIDC; membership in the "admin" group maps to cluster-admin via RBAC. # Requires: curl, jq. Needs a Keycloak client with realm-management (manage-users/groups). set -euo pipefail require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } require curl; require jq : "${KEYCLOAK_URL:=https://sso.bstein.dev}" : "${KEYCLOAK_REALM:=atlas}" : "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" : "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" : "${GITOPS_GROUP:=admin}" # Comma-separated usernames to sync; set SYNC_ALL_USERS=true to include all Keycloak users. : "${TARGET_USERNAMES:=bstein}" : "${SYNC_ALL_USERS:=false}" fetch_token() { curl -fsS -X POST \ -d "grant_type=client_credentials" \ -d "client_id=${KEYCLOAK_CLIENT_ID}" \ -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ | jq -r '.access_token' } ensure_group() { local token="$1" group="$2" id id=$(curl -fsS -H "Authorization: Bearer ${token}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \ | jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1) if [[ -n "${id}" ]]; then echo "${id}" return fi curl -fsS -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json" \ -d "{\"name\":\"${group}\"}" \ -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups" # Fetch again to get id curl -fsS -H "Authorization: Bearer ${token}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \ | jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1 } user_id_by_name() { local token="$1" username="$2" curl -fsS -H "Authorization: Bearer ${token}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=${username}" \ | jq -r '.[0].id' } add_user_to_group() { local token="$1" user_id="$2" group_id="$3" username="$4" if [[ -z "${user_id}" ]]; then echo "user ${username} not found in Keycloak; skip" >&2 return fi curl -fsS -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer ${token}" \ -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${user_id}/groups/${group_id}" \ | grep -qE '^(204|409)$' || echo "failed adding ${username} to group" >&2 } main() { local token group_id users=() token="$(fetch_token)" group_id="$(ensure_group "${token}" "${GITOPS_GROUP}")" if [[ "${SYNC_ALL_USERS}" == "true" ]]; then readarray -t users < <(curl -fsS -H "Authorization: Bearer ${token}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ | jq -r '.[] | select(.enabled==true) | .username') else IFS=',' read -ra users <<< "${TARGET_USERNAMES}" fi for user in "${users[@]}"; do user="$(echo "${user}" | xargs)" [[ -z "${user}" ]] && continue add_user_to_group "${token}" "$(user_id_by_name "${token}" "${user}")" "${group_id}" "${user}" done } main "$@"