--- # Devis d'identite — RELEVE seul. Ne modifie rien (D-23 : un devis se lit d'abord). # # Le playbook collecte deux dictionnaires — ce que le depot DECLARE, ce que la machine # PORTE — et les depose en JSON sur le controleur. La comparaison est faite par # `scripts/devis_identite.py` : elle n'a rien a faire en Jinja, et le depot a deja # cette forme (devis_opnsense, devis_sdn : Python raisonne, Ansible releve). # # Pourquoi ce devis existe. Les 30 preuves de `prouver.py` sont STATIQUES : elles # montrent que le depot est coherent avec lui-meme. Aucune ne demande au systeme # deploye s'il ressemble a ce que le depot annonce. Les quatre defauts du 2026-08-08 # etaient tous de ce second type — chacun trouve en relisant apres avoir ecrit, # aucun signale par un test. # # make identite-plan - name: Devis d'identité — relever le déclaré et le réel hosts: serveur_keycloak become: true gather_facts: false vars: kc: "http://localhost:8080" devis_identite_sortie: "{{ playbook_dir }}/../../instance/devis-identite.json" tasks: # Les valeurs DECLAREES vivent dans les defauts du role. On les charge plutot que # de les recopier ici : un devis qui redeclare ce qu'il verifie ne verifie rien. - name: Charger les valeurs déclarées par le rôle Keycloak ansible.builtin.include_vars: dir: "{{ playbook_dir }}/../../roles/serveur_keycloak/defaults" - name: Résoudre la politique de mot de passe (déclaration unique) ansible.builtin.include_role: name: resoudre_politique_mdp # Meme raison : les DN de l'annuaire ne se recopient pas, ils se resolvent. - name: Résoudre la connexion à l'annuaire ansible.builtin.include_role: name: resoudre_annuaire - name: Charger le registre des applications ansible.builtin.include_vars: file: "{{ setops_plan_dir }}/applications.yml" - name: Obtenir un jeton d'administration ansible.builtin.uri: url: "{{ kc }}/realms/master/protocol/openid-connect/token" method: POST body_format: form-urlencoded body: grant_type: password client_id: admin-cli username: "{{ serveur_keycloak_admin_user }}" password: "{{ serveur_keycloak_admin_password }}" register: devis_jeton no_log: true - name: Lire le realm ansible.builtin.uri: url: "{{ kc }}/admin/realms/{{ serveur_keycloak_realm }}" headers: Authorization: "Bearer {{ devis_jeton.json.access_token }}" register: devis_realm no_log: true - name: Lire la fédération LDAP ansible.builtin.uri: url: "{{ kc }}/admin/realms/{{ serveur_keycloak_realm }}/components?type=org.keycloak.storage.UserStorageProvider" headers: Authorization: "Bearer {{ devis_jeton.json.access_token }}" register: devis_fed no_log: true - name: Lire les mappeurs de la fédération ansible.builtin.uri: url: >- {{ kc }}/admin/realms/{{ serveur_keycloak_realm }}/components?parent={{ devis_fed.json[0].id }}&type=org.keycloak.storage.ldap.mappers.LDAPStorageMapper headers: Authorization: "Bearer {{ devis_jeton.json.access_token }}" register: devis_mappeurs no_log: true - name: Lire la politique de mot de passe de l'annuaire community.general.ldap_search: dn: "ou=policies,{{ resoudre_annuaire_base_dn }}" scope: onelevel filter: "(objectClass=pwdPolicy)" server_uri: "ldapi:///" register: devis_ppolicy changed_when: false # Un compte sans adresse ne peut pas recevoir de lien de reinitialisation : pour # lui, « mot de passe oublie » est un ecran qui ne mene nulle part, et le seul # recours redevient l'exploitant manipulant le mot de passe d'autrui. - name: Lire les comptes de l'annuaire community.general.ldap_search: dn: "{{ resoudre_annuaire_users_dn }}" scope: onelevel filter: "(objectClass=inetOrgPerson)" attrs: ["uid", "mail", "givenName"] server_uri: "ldapi:///" register: devis_comptes changed_when: false - name: Déposer le relevé sur le contrôleur ansible.builtin.copy: dest: "{{ devis_identite_sortie }}" mode: "0600" content: "{{ { 'hote': inventory_hostname, 'realm': serveur_keycloak_realm, 'declare': { 'passwordPolicy': resoudre_politique_mdp_keycloak, 'brute': resoudre_politique_mdp_brute, 'ppolicy': resoudre_politique_mdp_ldap, 'durcissement': serveur_keycloak_ldap_durcissement, 'mappeurs': serveur_keycloak_ldap_attributs, 'editMode': serveur_keycloak_ldap_edit_mode, 'resetPasswordAllowed': serveur_keycloak_reset_mot_de_passe | bool, 'smtpHost': applications[serveur_keycloak_smtp_app].hote ~ '.' ~ domaine_interne, }, 'reel': { 'realm': devis_realm.json, 'federation': devis_fed.json[0].config, 'mappeurs': dict(devis_mappeurs.json | map(attribute='name') | list | zip(devis_mappeurs.json | map(attribute='config') | list)), 'ppolicy': (devis_ppolicy.results | first) | default({}), 'comptes': devis_comptes.results, }, } | to_nice_json }}" delegate_to: localhost become: false - name: Indiquer le relevé ansible.builtin.debug: msg: "Relevé déposé : {{ devis_identite_sortie }} — comparaison par scripts/devis_identite.py"