keycloak : le claim de groupes exige les clients — troisieme defaut d'ordre
Meme motif que les roles de realm, une etape plus loin. groupes-ldap.yml posait aussi un oidc-group-membership-mapper sur les CLIENTS, alors que clients-oidc.yml s'execute apres. Invisible tant que les clients existaient d'un passage precedent. Extrait dans claim-groupes.yml, place APRES clients-oidc. J'avais d'abord insere l'appel AVANT — le defaut meme que je corrigeais ; rattrape avant tout deploiement. La lecon vaut au-dela du role : un fichier de taches nomme d'apres un SUJET (« les groupes ») rassemble des etapes aux dependances differentes, et l'ordre qui en resulte n'est correct que par accident. Ce qui doit gouverner le decoupage, c'est ce dont chaque etape a BESOIN, pas ce dont elle parle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3bf99fa7be
commit
8b0eaaec0a
3 changed files with 66 additions and 48 deletions
62
roles/serveur_keycloak/tasks/claim-groupes.yml
Normal file
62
roles/serveur_keycloak/tasks/claim-groupes.yml
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
---
|
||||||
|
# Emission du claim `groups` dans le jeton des CLIENTS — extrait de `groupes-ldap.yml`
|
||||||
|
# le 2026-08-08, pour la meme raison que `roles-realm.yml` une heure plus tot.
|
||||||
|
#
|
||||||
|
# Ce mapper se pose sur des clients OIDC, donc il exige que `clients-oidc.yml` soit
|
||||||
|
# passe. Il vivait pourtant au milieu de la projection des groupes, execute AVANT.
|
||||||
|
# Invisible tant que les clients existaient d'un passage precedent ; sur un realm neuf,
|
||||||
|
# la tache cherche des clients qui n'ont pas encore ete crees.
|
||||||
|
#
|
||||||
|
# Trouve par la reconstruction from-zero, une etape apres le defaut des roles de realm.
|
||||||
|
# La lecon est la meme et vaut d'etre retenue : un fichier de taches nomme d'apres un
|
||||||
|
# SUJET (« les groupes ») rassemble des etapes aux dependances differentes, et l'ordre
|
||||||
|
# qui en resulte n'est correct que par accident. Ce qui doit gouverner le decoupage,
|
||||||
|
# c'est ce dont chaque etape a BESOIN — pas ce dont elle parle.
|
||||||
|
- name: Émettre le claim de groupes dans le jeton des clients
|
||||||
|
ansible.builtin.shell:
|
||||||
|
executable: /bin/bash
|
||||||
|
cmd: |
|
||||||
|
set -euo pipefail
|
||||||
|
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
||||||
|
"$KC" config credentials --server http://localhost:8080 --realm master \
|
||||||
|
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
||||||
|
CID=$("$KC" get clients -r {{ serveur_keycloak_realm }} -q clientId={{ item }} 2>/dev/null \
|
||||||
|
| grep -oP '"id"\s*:\s*"\K[^"]+' | head -1)
|
||||||
|
if [ -z "$CID" ]; then echo "SETOPS_OK client absent"; exit 0; fi
|
||||||
|
if "$KC" get clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} 2>/dev/null \
|
||||||
|
| grep -q '"groupes-membres"'; then
|
||||||
|
echo SETOPS_OK
|
||||||
|
else
|
||||||
|
# `full.path=false` : le claim porte `sysadmin`, pas `/sysadmin`. Les
|
||||||
|
# services comparent un nom de groupe, pas un chemin.
|
||||||
|
"$KC" create clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} \
|
||||||
|
-s name=groupes-membres -s protocol=openid-connect \
|
||||||
|
-s protocolMapper=oidc-group-membership-mapper \
|
||||||
|
-s 'config."claim.name"={{ serveur_keycloak_groupes_claim }}' \
|
||||||
|
-s 'config."full.path"=false' \
|
||||||
|
-s 'config."id.token.claim"=true' \
|
||||||
|
-s 'config."access.token.claim"=true' \
|
||||||
|
-s 'config."userinfo.token.claim"=true' >/dev/null
|
||||||
|
echo SETOPS_CHANGED
|
||||||
|
fi
|
||||||
|
environment:
|
||||||
|
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
||||||
|
loop: "{{ serveur_keycloak_groupes_mapper_clients }}"
|
||||||
|
register: serveur_keycloak_grp_mapper
|
||||||
|
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_grp_mapper.stdout"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- serveur_keycloak_groupes_mapper_clients | length > 0
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
# --- Rendre le changement de mot de passe EXPLICITE ---------------------------
|
||||||
|
# OpenLDAP marque `pwdReset` et refuse les operations au niveau du PROTOCOLE ;
|
||||||
|
# Keycloak a son propre systeme d'actions requises et NE LIT PAS `pwdReset`. Les
|
||||||
|
# deux mecanismes ne se parlent pas : l'utilisateur entre avec son jeton, atterrit
|
||||||
|
# dans la console de compte, et rien ne lui dit pourquoi ni quoi faire. Constate le
|
||||||
|
# 2026-08-07 par l'operateur : « ce n'est pas du tout explicite ».
|
||||||
|
#
|
||||||
|
# On aligne donc Keycloak sur l'annuaire : tant que `pwdReset` est vrai, l'action
|
||||||
|
# `UPDATE_PASSWORD` est posee et Keycloak affiche un ecran qui l'explique. Elle
|
||||||
|
# disparait d'elle-meme quand le mot de passe change — l'annuaire retire `pwdReset`,
|
||||||
|
# et la condition cesse d'etre vraie.
|
||||||
|
|
@ -144,54 +144,6 @@
|
||||||
# Le mapper de PROTOCOLE : sans lui, les groupes existent dans le realm mais
|
# Le mapper de PROTOCOLE : sans lui, les groupes existent dans le realm mais
|
||||||
# n'apparaissent JAMAIS dans le jeton — Forgejo et Nextcloud lisent un claim vide
|
# n'apparaissent JAMAIS dans le jeton — Forgejo et Nextcloud lisent un claim vide
|
||||||
# et n'accordent rien. C'est le dernier maillon de la chaine.
|
# et n'accordent rien. C'est le dernier maillon de la chaine.
|
||||||
- name: Émettre le claim de groupes dans le jeton des clients
|
|
||||||
ansible.builtin.shell:
|
|
||||||
executable: /bin/bash
|
|
||||||
cmd: |
|
|
||||||
set -euo pipefail
|
|
||||||
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
|
||||||
"$KC" config credentials --server http://localhost:8080 --realm master \
|
|
||||||
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
|
||||||
CID=$("$KC" get clients -r {{ serveur_keycloak_realm }} -q clientId={{ item }} 2>/dev/null \
|
|
||||||
| grep -oP '"id"\s*:\s*"\K[^"]+' | head -1)
|
|
||||||
if [ -z "$CID" ]; then echo "SETOPS_OK client absent"; exit 0; fi
|
|
||||||
if "$KC" get clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} 2>/dev/null \
|
|
||||||
| grep -q '"groupes-membres"'; then
|
|
||||||
echo SETOPS_OK
|
|
||||||
else
|
|
||||||
# `full.path=false` : le claim porte `sysadmin`, pas `/sysadmin`. Les
|
|
||||||
# services comparent un nom de groupe, pas un chemin.
|
|
||||||
"$KC" create clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} \
|
|
||||||
-s name=groupes-membres -s protocol=openid-connect \
|
|
||||||
-s protocolMapper=oidc-group-membership-mapper \
|
|
||||||
-s 'config."claim.name"={{ serveur_keycloak_groupes_claim }}' \
|
|
||||||
-s 'config."full.path"=false' \
|
|
||||||
-s 'config."id.token.claim"=true' \
|
|
||||||
-s 'config."access.token.claim"=true' \
|
|
||||||
-s 'config."userinfo.token.claim"=true' >/dev/null
|
|
||||||
echo SETOPS_CHANGED
|
|
||||||
fi
|
|
||||||
environment:
|
|
||||||
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
|
||||||
loop: "{{ serveur_keycloak_groupes_mapper_clients }}"
|
|
||||||
register: serveur_keycloak_grp_mapper
|
|
||||||
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_grp_mapper.stdout"
|
|
||||||
no_log: true
|
|
||||||
when:
|
|
||||||
- serveur_keycloak_groupes_mapper_clients | length > 0
|
|
||||||
- not ansible_check_mode
|
|
||||||
|
|
||||||
# --- Rendre le changement de mot de passe EXPLICITE ---------------------------
|
|
||||||
# OpenLDAP marque `pwdReset` et refuse les operations au niveau du PROTOCOLE ;
|
|
||||||
# Keycloak a son propre systeme d'actions requises et NE LIT PAS `pwdReset`. Les
|
|
||||||
# deux mecanismes ne se parlent pas : l'utilisateur entre avec son jeton, atterrit
|
|
||||||
# dans la console de compte, et rien ne lui dit pourquoi ni quoi faire. Constate le
|
|
||||||
# 2026-08-07 par l'operateur : « ce n'est pas du tout explicite ».
|
|
||||||
#
|
|
||||||
# On aligne donc Keycloak sur l'annuaire : tant que `pwdReset` est vrai, l'action
|
|
||||||
# `UPDATE_PASSWORD` est posee et Keycloak affiche un ecran qui l'explique. Elle
|
|
||||||
# disparait d'elle-meme quand le mot de passe change — l'annuaire retire `pwdReset`,
|
|
||||||
# et la condition cesse d'etre vraie.
|
|
||||||
- name: Lire `pwdReset` sur le compte d'amorçage (délégué à l'annuaire)
|
- name: Lire `pwdReset` sur le compte d'amorçage (délégué à l'annuaire)
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,10 @@
|
||||||
ansible.builtin.include_tasks: clients-oidc.yml
|
ansible.builtin.include_tasks: clients-oidc.yml
|
||||||
when: serveur_keycloak_clients | length > 0
|
when: serveur_keycloak_clients | length > 0
|
||||||
|
|
||||||
|
- name: Émettre le claim de groupes dans le jeton des clients
|
||||||
|
ansible.builtin.include_tasks: claim-groupes.yml
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Déclarer les URI de retour après déconnexion
|
- name: Déclarer les URI de retour après déconnexion
|
||||||
ansible.builtin.include_tasks: deconnexion-oidc.yml
|
ansible.builtin.include_tasks: deconnexion-oidc.yml
|
||||||
when: not ansible_check_mode
|
when: not ansible_check_mode
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue