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

436 lines
12 KiB
Markdown

# 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`
```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
```bash
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
```bash
# 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
```bash
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
```bash
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
```bash
# 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)
```bash
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
```bash
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.**
```bash
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
```bash
# 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](../DECISIONS.md). 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
```bash
# 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](./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é :
```python
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
```bash
# 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)
```csv
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)
```csv
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 :
```bash
python3 import-furious-to-zitadel.py --mode test --verbose
```
---
## 📚 Voir aussi
- [07-rbac-intranet.md](./07-rbac-intranet.md) — Suite de l'import : attribution des rôles RBAC
- [03-zitadel-setup.md](./03-zitadel-setup.md) — Création du service account Zitadel
- [../matrices/job-title-to-role.csv](../matrices/job-title-to-role.csv) — Matrice de mapping