Set-OPS-Public/roles/serveur_keycloak/tasks/groupes-ldap.yml
Daniel Allaire 74a2456f09 keycloak : group-ldap-mapper — le role s'attache au GROUPE
La chaine est complete : LDAP cn=sysadmin -> groupe de realm -> role
grafana-admin -> Admin dans Grafana. Ajouter quelqu'un au groupe dans l'annuaire
lui ouvre Grafana sans deploiement et sans que personne ne soit nomme (D-66).
Rejoue : changed=0.

Defaut de fond trouve en chemin : la federation LDAP n'etait JAMAIS reconciliee.
`federation-ldap.yml` creait le provider s'il manquait puis ne le corrigeait
plus — il pointait encore `ldaps://id-ldap-01...`, le nom errone corrige le matin
meme dans `resoudre_annuaire`. La synchronisation echouait sur `UnknownHost`.

C'est le revers de D-67 au mauvais endroit : l'INFRASTRUCTURE se reconcilie,
seules les appartenances ne le sont pas. URL, usersDn et bindDn sont desormais
corriges a chaque passage.

Et j'avais masque l'echec avec `|| true` : le mapper existait, le realm restait
vide, rien ne disait pourquoi. Retire — l'erreur remonte avec son message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:32:47 -04:00

119 lines
5.8 KiB
YAML

---
# PROJECTION DES GROUPES LDAP (D-65). LDAP porte les habilitations ; Keycloak les
# traduit en groupes de realm, puis en roles que les services lisent dans le jeton.
#
# Sans ce mapper, la chaine s'arrete avant le premier service : les groupes LDAP
# n'atteignent jamais Grafana, Forgejo ni Nextcloud, et l'assignation se fait
# utilisateur par utilisateur — ce que D-66 interdit. Constate le 2026-08-07.
- name: Projeter les groupes LDAP dans le realm (group-ldap-mapper)
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
CHANGED=0
# Le provider de federation, retrouve par son NOM : son identifiant est
# genere a la creation, on ne peut pas le supposer.
FID=$("$KC" get components -r {{ serveur_keycloak_realm }} \
-q type=org.keycloak.storage.UserStorageProvider \
--fields id,name --format csv --noquotes 2>/dev/null \
| awk -F, '$2 == "{{ serveur_keycloak_ldap_nom }}" {print $1}' | head -1)
if [ -z "$FID" ]; then
echo "SETOPS_ERREUR: provider LDAP '{{ serveur_keycloak_ldap_nom }}' introuvable" >&2
exit 3
fi
EXISTE=$("$KC" get components -r {{ serveur_keycloak_realm }} \
-q parent="$FID" -q type=org.keycloak.storage.ldap.mappers.LDAPStorageMapper \
--fields name --format csv --noquotes 2>/dev/null \
| grep -c '^{{ serveur_keycloak_groupes_mapper_nom }}$' || true)
if [ "$EXISTE" = 0 ]; then
"$KC" create components -r {{ serveur_keycloak_realm }} \
-s name={{ serveur_keycloak_groupes_mapper_nom }} \
-s providerId=group-ldap-mapper \
-s providerType=org.keycloak.storage.ldap.mappers.LDAPStorageMapper \
-s parentId="$FID" \
-s 'config."groups.dn"=["{{ serveur_keycloak_groupes_dn }}"]' \
-s 'config."group.name.ldap.attribute"=["cn"]' \
-s 'config."group.object.classes"=["{{ serveur_keycloak_groupes_object_class }}"]' \
-s 'config."membership.ldap.attribute"=["{{ serveur_keycloak_groupes_membership_attr }}"]' \
-s 'config."membership.attribute.type"=["DN"]' \
-s 'config."membership.user.ldap.attribute"=["{{ serveur_keycloak_ldap_username_attr }}"]' \
-s 'config."mode"=["READ_ONLY"]' \
-s 'config."user.roles.retrieve.strategy"=["LOAD_GROUPS_BY_MEMBER_ATTRIBUTE"]' \
-s 'config."preserve.group.inheritance"=["false"]' \
-s 'config."drop.non.existing.groups.during.sync"=["false"]' >/dev/null
CHANGED=1
fi
# La synchronisation IMPORTE les groupes : sans elle, le mapper existe mais
# le realm reste vide jusqu'a la premiere connexion d'un membre.
MID=$("$KC" get components -r {{ serveur_keycloak_realm }} -q parent="$FID" \
--fields id,name --format csv --noquotes 2>/dev/null \
| awk -F, '$2 == "{{ serveur_keycloak_groupes_mapper_nom }}" {print $1}' | head -1)
# PAS de `|| true` ici : un echec de synchronisation doit se voir. Masque, il
# m'a coute la moitie du diagnostic — le mapper existait, le realm restait
# vide, et rien ne disait pourquoi (2026-08-07).
if [ -n "$MID" ]; then
if ! "$KC" create "user-storage/$FID/mappers/$MID/sync?direction=fedToKeycloak" \
-r {{ serveur_keycloak_realm }} 2>/tmp/setops-sync.err >/dev/null; then
echo "SETOPS_ERREUR: synchronisation des groupes echouee : $(cat /tmp/setops-sync.err)" >&2
rm -f /tmp/setops-sync.err
exit 5
fi
rm -f /tmp/setops-sync.err
fi
[ "$CHANGED" = 1 ] && echo SETOPS_CHANGED || echo SETOPS_OK
environment:
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
register: serveur_keycloak_grp
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_grp.stdout"
no_log: true
retries: 5
delay: 6
until: serveur_keycloak_grp.rc == 0
when: not ansible_check_mode
# LE GROUPE PORTE LE ROLE, et c'est ce qui remplace l'assignation nominative.
# Un membre du groupe LDAP herite du role de realm sans que personne ne le nomme :
# ajouter quelqu'un a `cn=sysadmin` dans LDAP suffit (D-66).
- name: Attacher les rôles de realm aux groupes projetés
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
CHANGED=0
{% for lien in serveur_keycloak_groupes_roles %}
GID=$("$KC" get groups -r {{ serveur_keycloak_realm }} \
--fields id,name --format csv --noquotes 2>/dev/null \
| awk -F, '$2 == "{{ lien.groupe }}" {print $1}' | head -1)
if [ -z "$GID" ]; then
echo "SETOPS_ERREUR: groupe '{{ lien.groupe }}' absent du realm (synchronise ?)" >&2
exit 4
fi
{% for role in lien.roles %}
if ! "$KC" get "groups/$GID/role-mappings/realm" -r {{ serveur_keycloak_realm }} \
--fields name --format csv --noquotes 2>/dev/null | grep -q '^{{ role }}$'; then
"$KC" add-roles -r {{ serveur_keycloak_realm }} --gid "$GID" --rolename {{ role }} >/dev/null
CHANGED=1
fi
{% endfor %}
{% endfor %}
[ "$CHANGED" = 1 ] && echo SETOPS_CHANGED || echo SETOPS_OK
environment:
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
register: serveur_keycloak_grp_roles
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_grp_roles.stdout"
no_log: true
when:
- serveur_keycloak_groupes_roles | length > 0
- not ansible_check_mode