#!/usr/bin/env python3 """ Script de configuration Keycloak pour UnionFlow Crée les rôles et les comptes de test pour MUKEFI et MESKA """ import requests import json import sys from typing import Dict, List, Optional # Configuration KEYCLOAK_URL = "http://localhost:8180" REALM_NAME = "unionflow" ADMIN_USERNAME = "admin" ADMIN_PASSWORD = "admin" # Modifier si différent TEST_PASSWORD = "Test@123" class KeycloakManager: def __init__(self): self.base_url = KEYCLOAK_URL self.realm = REALM_NAME self.token = None def get_admin_token(self) -> bool: """Obtient un token admin pour l'API Keycloak""" url = f"{self.base_url}/realms/master/protocol/openid-connect/token" data = { "client_id": "admin-cli", "username": ADMIN_USERNAME, "password": ADMIN_PASSWORD, "grant_type": "password" } try: response = requests.post(url, data=data) response.raise_for_status() self.token = response.json()["access_token"] print("✅ Connecté à Keycloak Admin API") return True except Exception as e: print(f"❌ Erreur connexion Keycloak: {e}") return False def _headers(self) -> Dict: """Headers pour les requêtes API""" return { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" } def list_users(self) -> List[Dict]: """Liste tous les utilisateurs du realm""" url = f"{self.base_url}/admin/realms/{self.realm}/users" response = requests.get(url, headers=self._headers()) response.raise_for_status() return response.json() def delete_user(self, user_id: str) -> bool: """Supprime un utilisateur""" url = f"{self.base_url}/admin/realms/{self.realm}/users/{user_id}" response = requests.delete(url, headers=self._headers()) return response.status_code == 204 def list_roles(self) -> List[Dict]: """Liste tous les rôles du realm""" url = f"{self.base_url}/admin/realms/{self.realm}/roles" response = requests.get(url, headers=self._headers()) response.raise_for_status() return response.json() def create_role(self, name: str, description: str) -> bool: """Crée un rôle realm""" url = f"{self.base_url}/admin/realms/{self.realm}/roles" data = { "name": name, "description": description, "composite": False, "clientRole": False } try: response = requests.post(url, headers=self._headers(), json=data) if response.status_code == 201: print(f" ✅ Rôle créé: {name}") return True elif response.status_code == 409: print(f" ⚠️ Rôle existe déjà: {name}") return True else: print(f" ❌ Erreur création rôle {name}: {response.status_code}") return False except Exception as e: print(f" ❌ Exception création rôle {name}: {e}") return False def create_user(self, username: str, email: str, first_name: str, last_name: str, password: str, roles: List[str], enabled: bool = True) -> Optional[str]: """Crée un utilisateur avec ses rôles""" # 1. Créer l'utilisateur url = f"{self.base_url}/admin/realms/{self.realm}/users" data = { "username": username, "email": email, "firstName": first_name, "lastName": last_name, "enabled": enabled, "emailVerified": True, "credentials": [{ "type": "password", "value": password, "temporary": False }] } try: response = requests.post(url, headers=self._headers(), json=data) if response.status_code == 201: # Récupérer l'ID de l'utilisateur créé location = response.headers.get("Location") user_id = location.split("/")[-1] if location else None if not user_id: # Chercher l'utilisateur par username users = self.list_users() user = next((u for u in users if u["username"] == username), None) if user: user_id = user["id"] if user_id: # 2. Assigner les rôles self.assign_roles_to_user(user_id, roles) print(f" ✅ Utilisateur créé: {username} ({email})") return user_id else: print(f" ⚠️ Utilisateur créé mais ID non trouvé: {username}") return None elif response.status_code == 409: print(f" ⚠️ Utilisateur existe déjà: {username}") # Récupérer l'ID et mettre à jour les rôles users = self.list_users() user = next((u for u in users if u["username"] == username), None) if user: self.assign_roles_to_user(user["id"], roles) return None else: print(f" ❌ Erreur création utilisateur {username}: {response.status_code} - {response.text}") return None except Exception as e: print(f" ❌ Exception création utilisateur {username}: {e}") return None def assign_roles_to_user(self, user_id: str, role_names: List[str]): """Assigne des rôles à un utilisateur""" # Récupérer les objets de rôle all_roles = self.list_roles() roles_to_assign = [r for r in all_roles if r["name"] in role_names] if not roles_to_assign: return url = f"{self.base_url}/admin/realms/{self.realm}/users/{user_id}/role-mappings/realm" response = requests.post(url, headers=self._headers(), json=roles_to_assign) if response.status_code in [204, 200]: print(f" → Rôles assignés: {', '.join(role_names)}") else: print(f" ⚠️ Erreur assignation rôles: {response.status_code}") def main(): print("=" * 70) print("🔧 Configuration Keycloak pour UnionFlow") print("=" * 70) kc = KeycloakManager() # 1. Connexion print("\n📡 Connexion à Keycloak...") if not kc.get_admin_token(): print("\n❌ Impossible de se connecter à Keycloak.") print(" Vérifiez que Keycloak est démarré sur http://localhost:8180") print(" Credentials par défaut: admin / admin") sys.exit(1) # 2. Audit de l'existant print("\n📋 Audit de l'état actuel...") existing_users = kc.list_users() existing_roles = kc.list_roles() print(f" • Utilisateurs existants: {len(existing_users)}") for user in existing_users: print(f" - {user['username']} ({user.get('email', 'no email')})") print(f" • Rôles existants: {len(existing_roles)}") for role in existing_roles: print(f" - {role['name']}") # 3. Suppression des utilisateurs existants print("\n🗑️ Suppression des utilisateurs existants...") for user in existing_users: if kc.delete_user(user["id"]): print(f" ✅ Supprimé: {user['username']}") else: print(f" ❌ Erreur suppression: {user['username']}") # 4. Création de la structure de rôles print("\n👥 Création de la structure de rôles...") roles_to_create = [ ("SUPER_ADMIN", "Super administrateur - Accès total plateforme multi-organisations"), ("ADMIN_ORGANISATION", "Administrateur d'une organisation - Accès total à son organisation"), ("TRESORIER", "Trésorier - Gestion financière, comptabilité, épargne/crédit"), ("SECRETAIRE", "Secrétaire - Gestion administrative, membres, adhésions, documents"), ("RESPONSABLE_SOCIAL", "Responsable social - Gestion aide sociale et solidarité"), ("RESPONSABLE_EVENEMENTS", "Responsable événements - Gestion événements et logistique"), ("RESPONSABLE_CREDIT", "Responsable crédit - Gestion épargne/crédit (mutuelles)"), ("MEMBRE_BUREAU", "Membre du bureau - Accès étendu consultation et actions"), ("MEMBRE_ACTIF", "Membre actif - Consultation et actions de base"), ("MEMBRE_SIMPLE", "Membre simple - Consultation uniquement"), ("MEMBRE", "Rôle technique - Membre base"), ("ADMIN", "Rôle technique - Admin base"), ("USER", "Rôle technique - Utilisateur base") ] for role_name, description in roles_to_create: kc.create_role(role_name, description) # 5. Création des comptes de test print("\n👤 Création des comptes de test...") users_to_create = [ # Super-Admin { "username": "superadmin", "email": "superadmin@unionflow.test", "first_name": "Super", "last_name": "Admin", "roles": ["SUPER_ADMIN", "ADMIN", "USER"] }, # MUKEFI (Mutuelle d'épargne et de crédit) { "username": "admin.mukefi", "email": "admin.mukefi@unionflow.test", "first_name": "Administrateur", "last_name": "MUKEFI", "roles": ["ADMIN_ORGANISATION", "ADMIN", "USER"] }, { "username": "tresorier.mukefi", "email": "tresorier.mukefi@unionflow.test", "first_name": "Trésorier", "last_name": "MUKEFI", "roles": ["TRESORIER", "MEMBRE", "USER"] }, { "username": "secretaire.mukefi", "email": "secretaire.mukefi@unionflow.test", "first_name": "Secrétaire", "last_name": "MUKEFI", "roles": ["SECRETAIRE", "MEMBRE", "USER"] }, { "username": "credit.mukefi", "email": "credit.mukefi@unionflow.test", "first_name": "Responsable Crédit", "last_name": "MUKEFI", "roles": ["RESPONSABLE_CREDIT", "MEMBRE", "USER"] }, { "username": "membre.mukefi", "email": "membre.mukefi@unionflow.test", "first_name": "Membre", "last_name": "MUKEFI", "roles": ["MEMBRE_ACTIF", "MEMBRE", "USER"] }, # MESKA (Association) { "username": "admin.meska", "email": "admin.meska@unionflow.test", "first_name": "Administrateur", "last_name": "MESKA", "roles": ["ADMIN_ORGANISATION", "ADMIN", "USER"] }, { "username": "secretaire.meska", "email": "secretaire.meska@unionflow.test", "first_name": "Secrétaire", "last_name": "MESKA", "roles": ["SECRETAIRE", "MEMBRE", "USER"] }, { "username": "social.meska", "email": "social.meska@unionflow.test", "first_name": "Responsable Social", "last_name": "MESKA", "roles": ["RESPONSABLE_SOCIAL", "MEMBRE", "USER"] }, { "username": "evenements.meska", "email": "evenements.meska@unionflow.test", "first_name": "Responsable Événements", "last_name": "MESKA", "roles": ["RESPONSABLE_EVENEMENTS", "MEMBRE", "USER"] }, { "username": "membre.meska", "email": "membre.meska@unionflow.test", "first_name": "Membre", "last_name": "MESKA", "roles": ["MEMBRE_ACTIF", "MEMBRE", "USER"] } ] print(f"\n📝 Création de {len(users_to_create)} comptes utilisateurs...") print(f" Mot de passe pour tous: {TEST_PASSWORD}\n") for user_data in users_to_create: kc.create_user( username=user_data["username"], email=user_data["email"], first_name=user_data["first_name"], last_name=user_data["last_name"], password=TEST_PASSWORD, roles=user_data["roles"] ) # 6. Résumé final print("\n" + "=" * 70) print("✅ Configuration Keycloak terminée avec succès !") print("=" * 70) print("\n📊 Résumé:") print(f" • {len(roles_to_create)} rôles créés") print(f" • {len(users_to_create)} utilisateurs créés") print(f" • Mot de passe: {TEST_PASSWORD}") print("\n👥 Comptes créés:") print("\n 🔧 Super-Admin:") print(" → superadmin@unionflow.test") print("\n 🏦 MUKEFI (Mutuelle):") print(" → admin.mukefi@unionflow.test (Admin)") print(" → tresorier.mukefi@unionflow.test (Trésorier)") print(" → secretaire.mukefi@unionflow.test (Secrétaire)") print(" → credit.mukefi@unionflow.test (Responsable Crédit)") print(" → membre.mukefi@unionflow.test (Membre Actif)") print("\n 🤝 MESKA (Association):") print(" → admin.meska@unionflow.test (Admin)") print(" → secretaire.meska@unionflow.test (Secrétaire)") print(" → social.meska@unionflow.test (Responsable Social)") print(" → evenements.meska@unionflow.test (Responsable Événements)") print(" → membre.meska@unionflow.test (Membre Actif)") print("\n🌐 Accès Keycloak:") print(f" • Console Admin: {KEYCLOAK_URL}/admin") print(f" • Realm: {REALM_NAME}") print("\n") if __name__ == "__main__": main()