diff --git a/docker-compose.yml b/docker-compose.yml index adf4084..ec3c195 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -69,10 +69,12 @@ services: SMTP_PASSWORD: BBdgb6KxRQ8mNcpWFJsZCJxbSGNdgLhKFiITMErfBlQP LOGIN_THEME: ecosplay ECOSPLAY_GROUPS: "gp_asso gp_contest gp_mail gp_mailling gp_member gp_ndd gp_sign gp_ticket super_admin_asso superadmin" - ADMIN_USER_USERNAME: jovann@siteconseil.fr + ADMIN_USER_USERNAME: jovann@e-cosplay.fr ADMIN_USER_PASSWORD: Shoko1997@ ADMIN_USER_FIRSTNAME: Jovann ADMIN_USER_LASTNAME: Serreau + SYNC_BOT_CLIENT: sync-bot + SYNC_BOT_SECRET: dev-sync-bot-9f3b2a7c1e8d4f6a0b5c2e1d7f8a4b3c volumes: - ./init/sync.sh:/opt/init/sync.sh:ro entrypoint: ["/bin/bash", "/opt/init/sync.sh"] diff --git a/init/sync.sh b/init/sync.sh index ee6e4d9..c276fd1 100755 --- a/init/sync.sh +++ b/init/sync.sh @@ -16,6 +16,7 @@ # ECOSPLAY_GROUPS (space-separated list) # ADMIN_USER_USERNAME, ADMIN_USER_PASSWORD, # ADMIN_USER_FIRSTNAME, ADMIN_USER_LASTNAME +# SYNC_BOT_CLIENT, SYNC_BOT_SECRET # ============================================================= set -euo pipefail @@ -29,16 +30,44 @@ 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 \ +# Two authentication paths: +# 1. sync-bot service account (preferred, used after bootstrap). +# 2. Default bootstrap admin (used on first run, before sync-bot +# has been created and granted the admin role). +# ------------------------------------------------------------- +KC_LOGIN_MODE=none + +login_as_sync_bot() { + [ -n "${SYNC_BOT_SECRET:-}" ] || return 1 + $KC config credentials \ + --server "$KC_SERVER" \ + --realm master \ + --client "$SYNC_BOT_CLIENT" \ + --secret "$SYNC_BOT_SECRET" >/dev/null 2>&1 +} + +login_as_admin() { + $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..." + --password "$KC_ADMIN_PASSWORD" >/dev/null 2>&1 +} + +log "Authenticating to Keycloak at ${KC_SERVER}" +while true; do + if login_as_sync_bot; then + KC_LOGIN_MODE=sync-bot + break + fi + if login_as_admin; then + KC_LOGIN_MODE=bootstrap + break + fi + info "Keycloak not ready / no valid credentials yet, retrying in 5s..." sleep 5 done -info "Keycloak ready." +info "Authenticated (mode=$KC_LOGIN_MODE)" # ------------------------------------------------------------- # Helpers @@ -171,6 +200,72 @@ ensure_client() { info "+ client $2 ($1)" } +ensure_sync_bot() { + # Create (or keep in sync) the automation service client in master. + # Grants its service account the master 'admin' realm role so that + # sync.sh can authenticate with client_credentials after the default + # admin user is disabled. + local cid + cid=$(client_internal_id master "$SYNC_BOT_CLIENT") + if [ -z "$cid" ]; then + $KC create clients -r master \ + -s "clientId=$SYNC_BOT_CLIENT" \ + -s "name=Sync Bot" \ + -s "description=Service account used by init/sync.sh for unattended config reconciliation" \ + -s 'protocol=openid-connect' \ + -s 'enabled=true' \ + -s 'publicClient=false' \ + -s "secret=$SYNC_BOT_SECRET" \ + -s 'standardFlowEnabled=false' \ + -s 'implicitFlowEnabled=false' \ + -s 'directAccessGrantsEnabled=false' \ + -s 'serviceAccountsEnabled=true' >/dev/null + cid=$(client_internal_id master "$SYNC_BOT_CLIENT") + info "+ service client $SYNC_BOT_CLIENT (master)" + else + $KC update "clients/$cid" -r master \ + -s 'enabled=true' \ + -s 'serviceAccountsEnabled=true' \ + -s 'standardFlowEnabled=false' \ + -s 'directAccessGrantsEnabled=false' \ + -s "secret=$SYNC_BOT_SECRET" >/dev/null + info "= service client $SYNC_BOT_CLIENT (master)" + fi + # Grant 'admin' realm role to the auto-generated service account user + $KC add-roles -r master \ + --uusername "service-account-${SYNC_BOT_CLIENT}" \ + --rolename admin >/dev/null 2>&1 || true + info " service-account-${SYNC_BOT_CLIENT} -> realm role admin" +} + +disable_default_admin() { + # Only disable once we're sure sync-bot can authenticate (otherwise + # next run would be locked out). + if ! login_as_sync_bot; then + warn "sync-bot auth not working, refusing to disable default admin" + login_as_admin >/dev/null 2>&1 || true + return + fi + # Re-login as admin to perform the disable action (sync-bot's token + # was used only for verification) + login_as_admin >/dev/null 2>&1 || true + + local admin_id current + admin_id=$(user_id master "$KC_ADMIN") + if [ -z "$admin_id" ]; then + info "no '$KC_ADMIN' user in master, nothing to disable" + return + fi + current=$($KC get "users/$admin_id" -r master --fields enabled 2>/dev/null \ + | sed -n 's/.*"enabled"[ ]*:[ ]*\(true\|false\).*/\1/p' | head -n1) + if [ "$current" = "false" ]; then + info "= default admin user '$KC_ADMIN' already disabled" + return + fi + $KC update "users/$admin_id" -r master -s enabled=false >/dev/null + info "+ default admin user '$KC_ADMIN' disabled" +} + # ============================================================= # Master realm: SMTP, theme, locale # ============================================================= @@ -191,6 +286,12 @@ $KC update realms/master \ -s "defaultLocale=fr" info "master realm updated" +# ============================================================= +# Master realm: automation service account (sync-bot) +# ============================================================= +log "Ensuring sync-bot service client in master realm" +ensure_sync_bot + # ============================================================= # Master realm: global Keycloak admin user # ============================================================= @@ -236,4 +337,10 @@ else warn "ecosplay realm not found — will be imported on next boot" fi +# ============================================================= +# Disable default bootstrap admin (only after sync-bot is proven) +# ============================================================= +log "Disabling default bootstrap admin user" +disable_default_admin + log "Sync complete" diff --git a/realms/ecosplay-realm.json b/realms/ecosplay-realm.json index e324601..548d20b 100644 --- a/realms/ecosplay-realm.json +++ b/realms/ecosplay-realm.json @@ -69,8 +69,8 @@ "users": [ { - "username": "jovann@siteconseil.fr", - "email": "jovann@siteconseil.fr", + "username": "jovann@e-cosplay.fr", + "email": "jovann@e-cosplay.fr", "firstName": "Jovann", "lastName": "Serreau", "enabled": true,