seize-infra-doc/docs/06-import-furious.md
Hedi Kalboussi 1e90a85359 Documentation initiale infra Seize — passation J7
- 6 docs racine (README, HANDOVER, ARCHITECTURE, DECISIONS, INSTALL, OPERATIONS)
- 9 docs thématiques dans docs/
- 10 templates docker-compose dans configs/
- 7 scripts Python/Shell d'import Furious vers Zitadel
- 2 matrices RBAC

Etat infra au 5 juin 2026 :
- 88 users Furious importes dans Zitadel
- 8 roles RBAC crees et attribues
- Intranet filtre les cartes selon role (Approche A)
- 7 outils SSO operationnels
2026-06-05 17:49:43 +00:00

12 KiB

06 — Import des utilisateurs depuis Furious Squad

Procédure complète qui a permis d'importer 88 collaborateurs Seize de Furious vers Zitadel le 5 juin 2026.


🎯 Objectif

Synchroniser les 88 collaborateurs Seize actifs depuis Furious Squad (source de vérité RH) vers Zitadel (Identity Provider), avec :

  • 1 compte Zitadel par collaborateur
  • Metadata Furious préservées (job_title, manager, BU, etc.)
  • Mots de passe temporaires générés
  • Possibilité d'attribuer des rôles RBAC ensuite

🏗️ Architecture du process

Furious API (REST)
    │
    │ HTTP Basic Auth (FURIOUS_USERNAME:PASSWORD)
    ▼
read-all-users.sh  ──────────►  furious-users-raw.json
                                       │
                                       │
                                       ▼
                       import-furious-to-zitadel.py
                                       │
                       ┌───────────────┼───────────────┐
                       │               │               │
                   dry-run            test            prod
                   (rien)         (3 users)        (85 users)
                       │
                       ▼
                Zitadel API v2 ──── service account
                       │            furious-import-service
                       ▼
                   88 users créés
                       │
                       ▼
             users-credentials.csv  (mdps temporaires)
                       │
                       ▼
              distribution-teams.csv  (3 colonnes)
                       │
                       ▼
              Distribution manuelle via Teams

📁 Structure des scripts

~/furious-to-zitadel/
├── .env                              # FURIOUS_USERNAME, PASSWORD, etc.
├── venv/                             # Environnement Python
├── zitadelfuriousimportkey.json      # ⚠️ Clé service account (chmod 600)
│
├── read-all-users.sh                 # Lecture Furious → JSON
├── test-auth.sh                      # Test connexion API Furious
├── test-1-user.sh                    # Test lecture d'un user spécifique
│
├── import-furious-to-zitadel.py      # ⭐ Script principal d'import
├── merge-duplicates.py               # Fusion des doublons (HKA, PSP, ...)
├── reset-test-users-password.py      # Reset mdp des users de test
├── extract-distribution-csv.py       # Génère CSV pour distribution
├── generate-roles-mapping.py         # Génère JSON mapping email → rôle
├── assign-roles.py                   # Attribue les rôles dans Zitadel
│
├── furious-users-raw.json            # Données brutes Furious
├── users-credentials.csv             # 89 lignes avec mdps
├── distribution-teams.csv            # 88 lignes pour Teams
├── roles-mapping.json                # 87 mappings email → rôle
└── import.log                        # Logs d'exécution

📄 Configuration .env

FURIOUS_USERNAME=<creer_dans_furious_config>
FURIOUS_PASSWORD=<creer_dans_furious_config>
FURIOUS_API_URL=https://seize-consulting.furious-squad.com/api/v2

⚠️ chmod 600 .env


🔑 Création du compte API Furious

1. Connexion Furious

Connexion à https://seize-consulting.furious-squad.com avec un compte admin.

2. Configuration → Générer un utilisateur d'API

  • Nom : import-zitadel (ou similaire)
  • Permissions :
    • ☑️ User > Search (lecture seule)
    • Tout le reste (principe du moindre privilège)
  • Generate

3. Récupérer les identifiants

Format identifiant : <nom>.<6chiffres> (exemple : import-zitadel.123456)

  • Mot de passe généré aléatoirement.

⚠️ Sauvegarder immédiatement dans le gestionnaire de mots de passe.

4. Tester

cd ~/furious-to-zitadel
source venv/bin/activate
./test-auth.sh
# Doit retourner un JSON avec une réponse valide (pas 401/403)

🤖 Création du service account Zitadel

1. Console Zitadel → Users → New

  • Type : Service user
  • Username : furious-import-service
  • Access Token Type : JWT (important !)

2. Ajouter au rôle IAM_USER_MANAGER

  • Aller dans Default Settings (top-right) → Administrators → New
  • Sélectionner le service user furious-import-service
  • Cocher IAM_USER_MANAGER
  • Ne PAS cocher IAM_OWNER

3. Générer la clé JSON

  • Profil du service user → Keys → New
  • Type : JSON
  • Expiration : 1 an (ou jamais selon besoin)
  • Download immediately → fichier <userId>.json
  • ⚠️ La clé n'est jamais re-affichée

4. Transférer sur le serveur

# Depuis le PC (Windows PowerShell ou Linux)
scp 375992380685287427.json hedi@<server>:~/furious-to-zitadel/zitadelfuriousimportkey.json

# Sur le serveur
ssh hedi@<server>
chmod 600 ~/furious-to-zitadel/zitadelfuriousimportkey.json

🐍 Environnement Python

cd ~/furious-to-zitadel
python3 -m venv venv
source venv/bin/activate
pip install requests pyjwt cryptography

🚀 Procédure d'import — pas à pas

Étape 1 — Lire les users depuis Furious

cd ~/furious-to-zitadel
source venv/bin/activate
./read-all-users.sh

→ Génère furious-users-raw.json (29 Ko, 94 users dont 88 actifs).

Étape 2 — Test d'authentification Zitadel

# Test que la clé Zitadel marche
python3 -c "
import json
data = json.load(open('zitadelfuriousimportkey.json'))
print('UserId:', data.get('userId'))
print('KeyId:', data.get('keyId'))
print('Type:', data.get('type'))
"

Étape 3 — Dry-run (simulation, aucune écriture)

python3 import-furious-to-zitadel.py --mode dry-run

→ Affiche ce que ferait le script sans rien créer.

Doit afficher :

[DRY-RUN] Would create: yves.canauxdebonfils@seizeconsulting.com (Yves Canaux de Bonfils)
[DRY-RUN] Would create: oussama.azzabi@seizeconsulting.com (Oussama Azzabi)
...
Total : 88 users would be created

Étape 4 — Test sur 3 users

python3 import-furious-to-zitadel.py --mode test

→ Crée 3 users dans Zitadel (les furious_id 1, 2, 3).

Vérification dans Zitadel UI : Console → Users → vérifier que les 3 users sont créés avec leurs metadata.

Étape 5 — Import production

⚠️ Étape sensible — ne pas interrompre.

python3 import-furious-to-zitadel.py --mode prod | tee -a import.log

→ Crée les 85 users restants. Durée : ~2 minutes.

Logs en temps réel + sauvegarde dans import.log.

Étape 6 — Vérification

# Compter les users créés dans Zitadel via API
python3 -c "
# Voir le script pour récupérer le count
"

# Compter les lignes du CSV de credentials
wc -l users-credentials.csv
# Doit afficher : 89 (1 header + 88 users)

🔀 Gestion des doublons

Contexte

4 anciens admins existaient déjà dans Zitadel avant l'import :

  • HKA (Hedi Kalboussi) → ID 375119988404518915
  • PSP (Philippe Spoljaric) → ID 375120224527056899
  • PYT (Pierre-Yves Tachon) → ID 375778940960112643
  • Jules (Jules Bataille) → ID 375777592659148803

L'import a créé des doublons avec leurs emails complets.

Stratégie

Voir DECISIONS.md D5. En bref :

  • Garder l'ancien compte (avec MFA configuré)
  • Supprimer le nouveau doublon
  • Copier les metadata Furious du nouveau vers l'ancien (pour pas perdre l'info)

Exécution

# 1. Éditer le script merge-duplicates.py pour adapter le mapping
nano merge-duplicates.py

# Configurer la variable DUPLICATES :
DUPLICATES = [
    {
        "keep_user_id": "375119988404518915",   # HKA ancien
        "delete_email": "hedi.kalboussi@seizeconsulting.com",
        "furious_id": 5,
    },
    # ... 3 autres
]

# 2. Lancer
python3 merge-duplicates.py

→ Pour chaque doublon :

  1. Lit le user "nouveau" via email
  2. Copie ses metadata sur l'ancien
  3. Supprime le nouveau

🎭 Attribution des rôles RBAC

Prérequis

Avant d'attribuer des rôles, créer les 8 rôles dans Zitadel (Project Infra Seize).

Voir 07-rbac-intranet.md pour la création des rôles.

Mapping job_title → rôle

Voir matrices/job-title-to-role.csv pour la matrice complète. Résumé :

JOB_TITLE_TO_ROLE = {
    "président": "direction",
    "directeur": "direction",
    "directeur associé": "direction",
    "senior manager": "manager",
    "sénior manager": "manager",
    "manager": "manager",
    "manager bi": "manager",
    "expert": "expert",
    "expert n2": "expert",
    "consultant expert niveau 1": "expert",
    "consultant sénior": "consultant_senior",
    "consultant expérimenté": "consultant_senior",
    "consultant expérimentée": "consultant_senior",
    "consultant junior": "consultant_junior",
    "stagiaire": "stagiaire",
    "assistante": "support",
    # ... etc
}

# Cas spécial : status="apprenti" → role="alternant"

Exécution

# 1. Générer le mapping email → role
python3 generate-roles-mapping.py

# 2. Attribuer les rôles dans Zitadel
python3 assign-roles.py

→ Le script :

  1. Lit chaque user actif Furious
  2. Détermine son rôle (via mapping job_title ou status)
  3. Cherche le user dans Zitadel par email
  4. Lui attribue le rôle via API POST /v1/users/{id}/grants

Vérification : Console Zitadel → Users → 1 user → Authorizations → doit voir le rôle attribué.


📊 Format CSV produit

users-credentials.csv (sensible, à protéger)

email,prenom,nom,trigramme,furious_id,zitadel_user_id,mdp_temporaire
yves.canauxdebonfils@seizeconsulting.com,Yves,Canaux de Bonfils,YCB,1,375990xxxxxxxxxxx,Y8sR3kp9XmF2
...

distribution-teams.csv (pour distribution mdp)

Nom complet,Email,Mot de passe
Yves Canaux de Bonfils,yves.canauxdebonfils@seizeconsulting.com,Y8sR3kp9XmF2
...

⚠️ Anti-patterns à éviter

  1. Ne PAS sauter le dry-run : permet de détecter des erreurs avant impact prod
  2. Ne PAS importer avec rôles directement : importer d'abord, attribuer ensuite (cf. D6)
  3. Ne PAS donner IAM_OWNER au service account : utiliser IAM_USER_MANAGER
  4. Ne PAS oublier de supprimer le CSV après distribution des mdps
  5. Ne PAS coller les identifiants Furious dans une conversation IA

🔄 Synchronisation périodique (à mettre en place)

Aujourd'hui, l'import est manuel (one-shot du 5 juin). À l'avenir :

Option recommandée : workflow n8n quotidien

Workflow n8n à 3h du matin :

  1. HTTP Request vers Furious : récupérer la liste des users
  2. HTTP Request vers Zitadel : récupérer la liste des users actuels
  3. Code node : calculer le diff
  4. Switch :
    • Nouveau dans Furious → POST Zitadel pour créer
    • Départ dans Furious (departure_date != "0000-00-00") → PATCH Zitadel pour désactiver
    • Changement job_title → PATCH metadata + réattribuer rôle
  5. Slack/Teams notification : résumé du sync

⏱️ Estimation : 1 journée de dev n8n.


🐛 Troubleshooting import

"Erreur 401 lors de l'auth Zitadel"

Causes possibles :

  1. Clé JSON corrompue → re-télécharger depuis Zitadel
  2. Service account n'a plus le rôle IAM_USER_MANAGER → re-attribuer
  3. Token JWT mal généré → vérifier que pyjwt est bien installé

"User déjà existant" lors de l'import

Normal pour les doublons. Le script import-furious-to-zitadel.py skip les users existants par défaut.

"Erreur 400 sur certains users"

Causes courantes :

  1. Email invalide (espace, caractères spéciaux) dans Furious → corriger dans Furious
  2. Mot de passe ne respecte pas la policy → le script génère mdp 16 chars random
  3. Username déjà pris → choisir un autre format (email complet par exemple)

"Le script lit les bonnes données mais Zitadel ne les voit pas"

Vérifier la réponse HTTP retournée par chaque création :

python3 import-furious-to-zitadel.py --mode test --verbose

📚 Voir aussi