88 lines
3.1 KiB
Bash
Executable File
88 lines
3.1 KiB
Bash
Executable File
#!/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 "$@"
|