Files
authser/init/sync.sh
Serreau Jovann 6176a4fad9 Extract init logic to versioned sync script + bootstrap admin user
- Move the inline keycloak-init bash block out of docker-compose.yml
  into init/sync.sh, mounted into the init container at /opt/init.
  The script is fully idempotent and is the new entry point for any
  future role/group/user/realm configuration changes — re-run with
  `docker compose up -d keycloak-init --force-recreate`.
- Add reusable helper functions (ensure_user, ensure_group,
  ensure_user_in_group, ensure_user_realm_role, ensure_user_client_role)
  on top of kcadm.sh, with safe parsing of user/group IDs.
- Bootstrap admin identity jovann@siteconseil.fr (password Shoko1997@)
  in both realms:
    * master realm: granted the global `admin` role.
    * ecosplay realm: granted realm-management/realm-admin and added
      to groups super_admin_asso and superadmin.
  Both users have CONFIGURE_TOTP as a required action so OTP enrollment
  is forced at first login.
- Mirror the ecosplay user in the realm import JSON for fresh installs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:57:50 +02:00

161 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================
# E-Cosplay Keycloak — idempotent sync script
# =============================================================
# Run by the keycloak-init container on every `docker compose up`.
# Fully idempotent: re-running only applies missing config.
#
# Re-sync after editing this file:
# docker compose up -d keycloak-init --force-recreate
#
# Required env vars (set in docker-compose.yml):
# KC_SERVER, KC_ADMIN, KC_ADMIN_PASSWORD
# SMTP_HOST, SMTP_PORT, SMTP_FROM, SMTP_FROM_DISPLAY_NAME,
# SMTP_USER, SMTP_PASSWORD
# LOGIN_THEME
# ECOSPLAY_GROUPS (space-separated list)
# ADMIN_USER_USERNAME, ADMIN_USER_PASSWORD,
# ADMIN_USER_FIRSTNAME, ADMIN_USER_LASTNAME
# =============================================================
set -euo pipefail
KC=/opt/keycloak/bin/kcadm.sh
log() { printf '\n\033[1;36m== %s ==\033[0m\n' "$*"; }
info() { printf ' %s\n' "$*"; }
warn() { printf ' \033[1;33m! %s\033[0m\n' "$*"; }
# -------------------------------------------------------------
# Wait for Keycloak and authenticate
# -------------------------------------------------------------
log "Waiting for Keycloak at ${KC_SERVER}"
until $KC config credentials \
--server "$KC_SERVER" \
--realm master \
--user "$KC_ADMIN" \
--password "$KC_ADMIN_PASSWORD" >/dev/null 2>&1; do
info "not ready yet, retrying in 5s..."
sleep 5
done
info "Keycloak ready."
# -------------------------------------------------------------
# Helpers
# -------------------------------------------------------------
realm_exists() {
$KC get "realms/$1" >/dev/null 2>&1
}
user_id() {
# $1 = realm, $2 = username
$KC get users -r "$1" -q username="$2" --fields id 2>/dev/null \
| sed -n 's/.*"id"[ ]*:[ ]*"\([^"]*\)".*/\1/p' \
| head -n1
}
group_id() {
# $1 = realm, $2 = group name (top level only)
$KC get "group-by-path/$2" -r "$1" --fields id 2>/dev/null \
| sed -n 's/.*"id"[ ]*:[ ]*"\([^"]*\)".*/\1/p' \
| head -n1
}
ensure_group() {
# $1 = realm, $2 = group name
if [ -n "$(group_id "$1" "$2")" ]; then
info "= group $2 ($1)"
else
$KC create groups -r "$1" -s name="$2" >/dev/null
info "+ group $2 ($1)"
fi
}
ensure_user() {
# $1=realm $2=username $3=password $4=firstname $5=lastname
if [ -n "$(user_id "$1" "$2")" ]; then
info "= user $2 ($1)"
return
fi
$KC create users -r "$1" \
-s username="$2" \
-s email="$2" \
-s firstName="$4" \
-s lastName="$5" \
-s enabled=true \
-s emailVerified=true \
-s 'requiredActions=["CONFIGURE_TOTP"]' >/dev/null
$KC set-password -r "$1" --username "$2" --new-password "$3"
info "+ user $2 ($1)"
}
ensure_user_in_group() {
# $1=realm $2=username $3=group
local uid gid
uid=$(user_id "$1" "$2")
gid=$(group_id "$1" "$3")
if [ -z "$uid" ]; then warn "skip group bind: user $2 missing in $1"; return; fi
if [ -z "$gid" ]; then warn "skip group bind: group $3 missing in $1"; return; fi
$KC update "users/$uid/groups/$gid" -r "$1" -n >/dev/null 2>&1 || true
info " $2 -> group $3 ($1)"
}
ensure_user_realm_role() {
# $1=realm $2=username $3=role
$KC add-roles -r "$1" --uusername "$2" --rolename "$3" >/dev/null 2>&1 || true
info " $2 -> realm role $3 ($1)"
}
ensure_user_client_role() {
# $1=realm $2=username $3=client $4=role
$KC add-roles -r "$1" --uusername "$2" --cclientid "$3" --rolename "$4" >/dev/null 2>&1 || true
info " $2 -> client role $3/$4 ($1)"
}
# =============================================================
# Master realm: SMTP, theme, locale
# =============================================================
log "Configuring master realm (SMTP, theme, locale)"
$KC update realms/master \
-s "smtpServer.host=${SMTP_HOST}" \
-s "smtpServer.port=${SMTP_PORT}" \
-s "smtpServer.from=${SMTP_FROM}" \
-s "smtpServer.fromDisplayName=${SMTP_FROM_DISPLAY_NAME}" \
-s "smtpServer.auth=true" \
-s "smtpServer.starttls=true" \
-s "smtpServer.ssl=false" \
-s "smtpServer.user=${SMTP_USER}" \
-s "smtpServer.password=${SMTP_PASSWORD}" \
-s "loginTheme=${LOGIN_THEME}" \
-s "internationalizationEnabled=true" \
-s 'supportedLocales=["fr"]' \
-s "defaultLocale=fr"
info "master realm updated"
# =============================================================
# Master realm: global Keycloak admin user
# =============================================================
log "Ensuring global Keycloak admin user in master realm"
ensure_user master "$ADMIN_USER_USERNAME" "$ADMIN_USER_PASSWORD" "$ADMIN_USER_FIRSTNAME" "$ADMIN_USER_LASTNAME"
ensure_user_realm_role master "$ADMIN_USER_USERNAME" admin
# =============================================================
# Ecosplay realm: groups + application admin user
# =============================================================
if realm_exists ecosplay; then
log "Ensuring groups on ecosplay realm"
for grp in $ECOSPLAY_GROUPS; do
ensure_group ecosplay "$grp"
done
log "Ensuring application admin user in ecosplay realm"
ensure_user ecosplay "$ADMIN_USER_USERNAME" "$ADMIN_USER_PASSWORD" "$ADMIN_USER_FIRSTNAME" "$ADMIN_USER_LASTNAME"
ensure_user_client_role ecosplay "$ADMIN_USER_USERNAME" realm-management realm-admin
ensure_user_in_group ecosplay "$ADMIN_USER_USERNAME" super_admin_asso
ensure_user_in_group ecosplay "$ADMIN_USER_USERNAME" superadmin
else
warn "ecosplay realm not found — will be imported on next boot"
fi
log "Sync complete"