258 lines
9.7 KiB
Python
258 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Script pour corriger la configuration du client Keycloak
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
import time
|
|
|
|
class ClientFixer:
|
|
def __init__(self, base_url: str = "http://localhost:8180"):
|
|
self.base_url = base_url
|
|
self.session = requests.Session()
|
|
self.admin_token = None
|
|
|
|
def get_admin_token(self) -> bool:
|
|
"""Obtient le token admin"""
|
|
try:
|
|
data = {
|
|
"username": "admin",
|
|
"password": "admin",
|
|
"grant_type": "password",
|
|
"client_id": "admin-cli"
|
|
}
|
|
|
|
response = self.session.post(
|
|
f"{self.base_url}/realms/master/protocol/openid-connect/token",
|
|
data=data,
|
|
headers={"Content-Type": "application/x-www-form-urlencoded"}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
token_data = response.json()
|
|
self.admin_token = token_data.get("access_token")
|
|
return self.admin_token is not None
|
|
|
|
except Exception as e:
|
|
print(f"Erreur obtention token: {e}")
|
|
|
|
return False
|
|
|
|
def get_client_id(self, realm_name: str, client_id: str) -> str:
|
|
"""Obtient l'ID interne du client"""
|
|
try:
|
|
response = self.session.get(
|
|
f"{self.base_url}/admin/realms/{realm_name}/clients",
|
|
headers={"Authorization": f"Bearer {self.admin_token}"}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
clients = response.json()
|
|
for client in clients:
|
|
if client.get("clientId") == client_id:
|
|
return client.get("id")
|
|
|
|
except Exception as e:
|
|
print(f"Erreur récupération client ID: {e}")
|
|
|
|
return None
|
|
|
|
def delete_client_if_exists(self, realm_name: str, client_id: str) -> bool:
|
|
"""Supprime le client s'il existe"""
|
|
internal_id = self.get_client_id(realm_name, client_id)
|
|
if internal_id:
|
|
try:
|
|
response = self.session.delete(
|
|
f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}",
|
|
headers={"Authorization": f"Bearer {self.admin_token}"}
|
|
)
|
|
if response.status_code == 204:
|
|
print(f" ✓ Client {client_id} supprimé")
|
|
return True
|
|
except Exception as e:
|
|
print(f" ⚠ Erreur suppression client: {e}")
|
|
|
|
return False
|
|
|
|
def create_client_complete(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool:
|
|
"""Crée le client avec la configuration complète"""
|
|
print(f"🔧 Création complète du client {client_id}...")
|
|
|
|
# 1. Supprimer s'il existe
|
|
self.delete_client_if_exists(realm_name, client_id)
|
|
|
|
# 2. Créer le client avec une configuration complète
|
|
client_data = {
|
|
"clientId": client_id,
|
|
"name": "UnionFlow Mobile App",
|
|
"description": "Client pour l'application mobile UnionFlow",
|
|
"enabled": True,
|
|
"clientAuthenticatorType": "client-secret",
|
|
"secret": "",
|
|
"redirectUris": ["*"],
|
|
"webOrigins": ["*"],
|
|
"notBefore": 0,
|
|
"bearerOnly": False,
|
|
"consentRequired": False,
|
|
"standardFlowEnabled": True,
|
|
"implicitFlowEnabled": False,
|
|
"directAccessGrantsEnabled": True,
|
|
"serviceAccountsEnabled": False,
|
|
"publicClient": True,
|
|
"frontchannelLogout": False,
|
|
"protocol": "openid-connect",
|
|
"attributes": {
|
|
"saml.assertion.signature": "false",
|
|
"saml.force.post.binding": "false",
|
|
"saml.multivalued.roles": "false",
|
|
"saml.encrypt": "false",
|
|
"saml.server.signature": "false",
|
|
"saml.server.signature.keyinfo.ext": "false",
|
|
"exclude.session.state.from.auth.response": "false",
|
|
"saml_force_name_id_format": "false",
|
|
"saml.client.signature": "false",
|
|
"tls.client.certificate.bound.access.tokens": "false",
|
|
"saml.authnstatement": "false",
|
|
"display.on.consent.screen": "false",
|
|
"saml.onetimeuse.condition": "false",
|
|
"access.token.lifespan": "300",
|
|
"client_credentials.use_refresh_token": "false"
|
|
},
|
|
"authenticationFlowBindingOverrides": {},
|
|
"fullScopeAllowed": True,
|
|
"nodeReRegistrationTimeout": -1,
|
|
"defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"],
|
|
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
|
}
|
|
|
|
try:
|
|
response = self.session.post(
|
|
f"{self.base_url}/admin/realms/{realm_name}/clients",
|
|
json=client_data,
|
|
headers={
|
|
"Authorization": f"Bearer {self.admin_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
)
|
|
|
|
if response.status_code == 201:
|
|
print(f" ✓ Client {client_id} créé avec succès")
|
|
|
|
# Attendre un peu pour que la configuration soit prise en compte
|
|
time.sleep(2)
|
|
|
|
# Vérifier la configuration
|
|
internal_id = self.get_client_id(realm_name, client_id)
|
|
if internal_id:
|
|
# Récupérer la configuration du client
|
|
get_response = self.session.get(
|
|
f"{self.base_url}/admin/realms/{realm_name}/clients/{internal_id}",
|
|
headers={"Authorization": f"Bearer {self.admin_token}"}
|
|
)
|
|
|
|
if get_response.status_code == 200:
|
|
client_config = get_response.json()
|
|
print(f" ✓ Configuration vérifiée:")
|
|
print(f" - Public Client: {client_config.get('publicClient')}")
|
|
print(f" - Direct Access Grants: {client_config.get('directAccessGrantsEnabled')}")
|
|
print(f" - Standard Flow: {client_config.get('standardFlowEnabled')}")
|
|
print(f" - Enabled: {client_config.get('enabled')}")
|
|
|
|
return True
|
|
|
|
else:
|
|
print(f" ✗ Erreur création client: {response.status_code}")
|
|
print(f" Réponse: {response.text}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f" ✗ Exception création client: {e}")
|
|
return False
|
|
|
|
def test_client_auth(self, realm_name: str = "unionflow", client_id: str = "unionflow-mobile") -> bool:
|
|
"""Teste l'authentification avec le client"""
|
|
print(f"🧪 Test d'authentification avec le client...")
|
|
|
|
# Attendre un peu pour que tout soit synchronisé
|
|
time.sleep(3)
|
|
|
|
test_data = {
|
|
"username": "marie.active",
|
|
"password": "Marie123!",
|
|
"grant_type": "password",
|
|
"client_id": client_id
|
|
}
|
|
|
|
try:
|
|
response = self.session.post(
|
|
f"{self.base_url}/realms/{realm_name}/protocol/openid-connect/token",
|
|
data=test_data,
|
|
headers={"Content-Type": "application/x-www-form-urlencoded"}
|
|
)
|
|
|
|
print(f" Status: {response.status_code}")
|
|
|
|
if response.status_code == 200:
|
|
token_data = response.json()
|
|
if "access_token" in token_data:
|
|
print(f" ✅ AUTHENTIFICATION RÉUSSIE !")
|
|
print(f" ✓ Token reçu (longueur: {len(token_data['access_token'])})")
|
|
return True
|
|
else:
|
|
print(f" ❌ Token manquant dans la réponse")
|
|
else:
|
|
print(f" ❌ Authentification échouée")
|
|
print(f" Réponse: {response.text}")
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Exception test auth: {e}")
|
|
|
|
return False
|
|
|
|
def fix_complete(self):
|
|
"""Correction complète du client"""
|
|
print("=" * 80)
|
|
print("🔧 CORRECTION DU CLIENT KEYCLOAK")
|
|
print("=" * 80)
|
|
print()
|
|
|
|
if not self.get_admin_token():
|
|
print("❌ Impossible d'obtenir le token admin")
|
|
return False
|
|
|
|
print("✅ Token admin obtenu")
|
|
print()
|
|
|
|
# Créer le client
|
|
if not self.create_client_complete():
|
|
print("❌ Échec de la création du client")
|
|
return False
|
|
|
|
print()
|
|
|
|
# Tester l'authentification
|
|
if self.test_client_auth():
|
|
print()
|
|
print("=" * 80)
|
|
print("🎉 CLIENT CORRIGÉ AVEC SUCCÈS !")
|
|
print("=" * 80)
|
|
print()
|
|
print("🚀 Maintenant testez tous les comptes avec: python test_auth.py")
|
|
return True
|
|
else:
|
|
print()
|
|
print("=" * 80)
|
|
print("⚠️ CLIENT CRÉÉ MAIS PROBLÈME D'AUTHENTIFICATION")
|
|
print("=" * 80)
|
|
return False
|
|
|
|
|
|
def main():
|
|
fixer = ClientFixer()
|
|
fixer.fix_complete()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|