#!/usr/bin/env bash # Sync Keycloak users into Gitea local accounts (for CLI + tokens). # Requires: curl, jq, kubectl. Expects a Keycloak client with realm-management # permissions (manage-users) and a Gitea admin token stored in a secret. set -euo pipefail require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } require curl; require jq; require kubectl : "${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}" : "${GITEA_BASE_URL:=https://scm.bstein.dev}" : "${GITEA_NAMESPACE:=gitea}" : "${GITEA_TOKEN_SECRET_NAME:=gitea-admin-token}" : "${GITEA_TOKEN_SECRET_KEY:=token}" : "${DEFAULT_PASSWORD:=TempSsoPass!2025}" 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' } pull_users() { local token="$1" curl -fsS -H "Authorization: Bearer ${token}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.username, (.email // ""), (.firstName // ""), (.lastName // "")] | @tsv' } get_gitea_token() { if [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then echo "${GITEA_ADMIN_TOKEN}" return fi kubectl -n "${GITEA_NAMESPACE}" get secret "${GITEA_TOKEN_SECRET_NAME}" -o "jsonpath={.data.${GITEA_TOKEN_SECRET_KEY}}" \ | base64 -d } user_exists() { local token="$1" username="$2" local code code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: token ${token}" \ "${GITEA_BASE_URL}/api/v1/admin/users/${username}") [[ "${code}" == "200" ]] } create_user() { local token="$1" username="$2" email="$3" fname="$4" lname="$5" local body status fullname fullname="$(echo "${fname} ${lname}" | xargs)" if [[ -z "${email}" ]]; then email="${username}@example.local" fi body=$(jq -n --arg u "${username}" --arg e "${email}" --arg p "${DEFAULT_PASSWORD}" \ --arg fn "${fullname}" '{username:$u, email:$e, password:$p, must_change_password:false, full_name:$fn}') status=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: token ${token}" \ -H "Content-Type: application/json" \ -X POST \ -d "${body}" \ "${GITEA_BASE_URL}/api/v1/admin/users") if [[ "${status}" == "201" ]]; then echo "created gitea user ${username}" elif [[ "${status}" == "409" ]]; then echo "gitea user ${username} already exists (409)" >&2 else echo "failed to create gitea user ${username} (status ${status})" >&2 fi } main() { local kc_token gitea_token kc_token="$(fetch_token)" gitea_token="$(get_gitea_token)" while IFS=$'\t' read -r username email fname lname; do if user_exists "${gitea_token}" "${username}"; then continue fi create_user "${gitea_token}" "${username}" "${email}" "${fname}" "${lname}" done < <(pull_users "${kc_token}") } main "$@"