#!/usr/bin/env pwsh <# .SYNOPSIS Script pour configurer Keycloak en production pour Lions User Manager .DESCRIPTION Ce script configure Keycloak production avec : - Création des clients OIDC (frontend + backend) - Création des rôles realm - Configuration des protocol mappers - Assignation des rôles au service account .PARAMETER KeycloakUrl URL de Keycloak (défaut: https://security.lions.dev) .PARAMETER Realm Realm Keycloak (défaut: master) .PARAMETER AdminUsername Nom d'utilisateur admin Keycloak .PARAMETER AdminPassword Mot de passe admin Keycloak .EXAMPLE .\setup-keycloak-production.ps1 -AdminUsername admin -AdminPassword "your-password" #> param( [Parameter(Mandatory=$false)] [string]$KeycloakUrl = "https://security.lions.dev", [Parameter(Mandatory=$false)] [string]$Realm = "master", [Parameter(Mandatory=$true)] [string]$AdminUsername, [Parameter(Mandatory=$true)] [string]$AdminPassword ) $ErrorActionPreference = "Stop" # Couleurs function Write-Success { Write-Host "✅ $args" -ForegroundColor Green } function Write-Info { Write-Host "ℹ️ $args" -ForegroundColor Cyan } function Write-Warning { Write-Host "⚠️ $args" -ForegroundColor Yellow } function Write-Error { Write-Host "❌ $args" -ForegroundColor Red } function Write-Step { Write-Host "`n🚀 $args" -ForegroundColor Magenta } Write-Host @" ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ 🔐 CONFIGURATION KEYCLOAK PRODUCTION - LIONS USER MANAGER 🔐 ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ "@ -ForegroundColor Cyan Write-Info "Keycloak URL: $KeycloakUrl" Write-Info "Realm: $Realm" Write-Info "Admin Username: $AdminUsername" Write-Info "" # 1. Obtenir le token admin Write-Step "1. Obtention du token admin Keycloak..." $tokenUrl = "$KeycloakUrl/realms/$Realm/protocol/openid-connect/token" $tokenBody = @{ username = $AdminUsername password = $AdminPassword grant_type = "password" client_id = "admin-cli" } | ConvertTo-Json try { $tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $tokenBody -ContentType "application/json" $accessToken = $tokenResponse.access_token if ([string]::IsNullOrEmpty($accessToken)) { Write-Error "Impossible d'obtenir le token admin" exit 1 } Write-Success "Token admin obtenu" } catch { Write-Error "Erreur lors de l'obtention du token: $($_.Exception.Message)" exit 1 } $headers = @{ "Authorization" = "Bearer $accessToken" "Content-Type" = "application/json" } # 2. Créer les rôles realm Write-Step "2. Création des rôles realm..." $roles = @( @{name="admin"; description="System administrator with full access"}, @{name="user_manager"; description="User manager - Can create, update, delete users"}, @{name="user_viewer"; description="User viewer - Read-only access to users"}, @{name="auditor"; description="Auditor - Can view audit logs"}, @{name="sync_manager"; description="Sync manager - Can manage synchronization"}, @{name="role_manager"; description="Role manager - Can create, update, delete roles"}, @{name="role_viewer"; description="Role viewer - Read-only access to roles"} ) foreach ($role in $roles) { $roleUrl = "$KeycloakUrl/admin/realms/$Realm/roles" $roleBody = @{ name = $role.name description = $role.description composite = $false clientRole = $false } | ConvertTo-Json -Compress try { $existingRole = Invoke-RestMethod -Uri "$roleUrl/$($role.name)" -Method Get -Headers $headers -ErrorAction SilentlyContinue Write-Warning "Rôle $($role.name) existe déjà, ignoré" } catch { try { Invoke-RestMethod -Uri $roleUrl -Method Post -Headers $headers -Body $roleBody | Out-Null Write-Success "Rôle $($role.name) créé" } catch { Write-Warning "Erreur création rôle $($role.name): $($_.Exception.Message)" } } } # 3. Créer le client frontend Write-Step "3. Création du client frontend (lions-user-manager-client)..." $frontendClientUrl = "$KeycloakUrl/admin/realms/$Realm/clients" $frontendClientBody = @{ clientId = "lions-user-manager-client" name = "Lions User Manager Client (Frontend)" description = "Frontend application for Lions User Manager" enabled = $true publicClient = $true standardFlowEnabled = $true directAccessGrantsEnabled = $false serviceAccountsEnabled = $false redirectUris = @( "https://user-manager.lions.dev/*", "https://admin.lions.dev/*", "https://user-manager.lions.dev/auth/callback" ) webOrigins = @( "https://user-manager.lions.dev", "https://admin.lions.dev" ) protocol = "openid-connect" attributes = @{ "pkce.code.challenge.method" = "S256" "use.refresh.tokens" = "true" "frontchannel.logout.session.required" = "true" } } | ConvertTo-Json -Depth 10 -Compress try { # Vérifier si le client existe $clients = Invoke-RestMethod -Uri $frontendClientUrl -Method Get -Headers $headers $existingClient = $clients | Where-Object { $_.clientId -eq "lions-user-manager-client" } if ($existingClient) { Write-Warning "Client frontend existe déjà (ID: $($existingClient.id))" $frontendClientId = $existingClient.id } else { $response = Invoke-RestMethod -Uri $frontendClientUrl -Method Post -Headers $headers -Body $frontendClientBody Write-Success "Client frontend créé" # Récupérer l'ID du client créé $clients = Invoke-RestMethod -Uri $frontendClientUrl -Method Get -Headers $headers $frontendClient = $clients | Where-Object { $_.clientId -eq "lions-user-manager-client" } $frontendClientId = $frontendClient.id } } catch { Write-Error "Erreur création client frontend: $($_.Exception.Message)" exit 1 } # 4. Créer le client backend (service account) Write-Step "4. Création du client backend (lions-user-manager) avec service account..." $backendClientUrl = "$KeycloakUrl/admin/realms/$Realm/clients" $backendClientBody = @{ clientId = "lions-user-manager" name = "Lions User Manager Server (Backend)" description = "Backend API for Lions User Manager with service account" enabled = $true publicClient = $false standardFlowEnabled = $false directAccessGrantsEnabled = $true serviceAccountsEnabled = $true authorizationServicesEnabled = $true protocol = "openid-connect" attributes = @{ "access.token.lifespan" = "300" "client.session.idle.timeout" = "1800" "client.session.max.lifespan" = "36000" } } | ConvertTo-Json -Depth 10 -Compress try { # Vérifier si le client existe $clients = Invoke-RestMethod -Uri $backendClientUrl -Method Get -Headers $headers $existingClient = $clients | Where-Object { $_.clientId -eq "lions-user-manager" } if ($existingClient) { Write-Warning "Client backend existe déjà (ID: $($existingClient.id))" $backendClientId = $existingClient.id } else { $response = Invoke-RestMethod -Uri $backendClientUrl -Method Post -Headers $headers -Body $backendClientBody Write-Success "Client backend créé" # Récupérer l'ID du client créé $clients = Invoke-RestMethod -Uri $backendClientUrl -Method Get -Headers $headers $backendClient = $clients | Where-Object { $_.clientId -eq "lions-user-manager" } $backendClientId = $backendClient.id } # Récupérer le secret du client backend $secretUrl = "$KeycloakUrl/admin/realms/$Realm/clients/$backendClientId/client-secret" $secretResponse = Invoke-RestMethod -Uri $secretUrl -Method Get -Headers $headers $backendSecret = $secretResponse.value Write-Success "Secret backend récupéré: $backendSecret" Write-Info "⚠️ IMPORTANT: Sauvegardez ce secret pour la configuration Kubernetes !" } catch { Write-Error "Erreur création client backend: $($_.Exception.Message)" exit 1 } # 5. Assigner les rôles au service account Write-Step "5. Assignation des rôles au service account backend..." try { # Récupérer l'ID du service account $serviceAccountUrl = "$KeycloakUrl/admin/realms/$Realm/clients/$backendClientId/service-account-user" $serviceAccount = Invoke-RestMethod -Uri $serviceAccountUrl -Method Get -Headers $headers $serviceAccountId = $serviceAccount.id # Récupérer les rôles à assigner $rolesToAssign = @("admin", "user_manager", "auditor", "sync_manager", "role_manager") $realmRoles = Invoke-RestMethod -Uri "$KeycloakUrl/admin/realms/$Realm/roles" -Method Get -Headers $headers $rolesToAssignObjects = $realmRoles | Where-Object { $rolesToAssign -contains $_.name } # Assigner les rôles $assignRolesUrl = "$KeycloakUrl/admin/realms/$Realm/users/$serviceAccountId/role-mappings/realm" $rolesBody = $rolesToAssignObjects | ConvertTo-Json -Depth 10 Invoke-RestMethod -Uri $assignRolesUrl -Method Post -Headers $headers -Body $rolesBody | Out-Null Write-Success "Rôles assignés au service account: $($rolesToAssign -join ', ')" } catch { Write-Warning "Erreur assignation rôles au service account: $($_.Exception.Message)" Write-Info "Vous devrez assigner les rôles manuellement dans Keycloak Admin Console" } # 6. Vérifier le protocol mapper roles Write-Step "6. Vérification du protocol mapper 'roles'..." try { $clientScopesUrl = "$KeycloakUrl/admin/realms/$Realm/client-scopes" $clientScopes = Invoke-RestMethod -Uri $clientScopesUrl -Method Get -Headers $headers $rolesScope = $clientScopes | Where-Object { $_.name -eq "roles" } if ($rolesScope) { $mappersUrl = "$KeycloakUrl/admin/realms/$Realm/client-scopes/$($rolesScope.id)/protocol-mappers/models" $mappers = Invoke-RestMethod -Uri $mappersUrl -Method Get -Headers $headers $realmRolesMapper = $mappers | Where-Object { $_.name -eq "realm roles" } if ($realmRolesMapper) { Write-Success "Protocol mapper 'realm roles' trouvé" Write-Info "Vérifiez qu'il est configuré pour l'access token (claim: realm_access.roles)" } else { Write-Warning "Protocol mapper 'realm roles' non trouvé - à créer manuellement" } } else { Write-Warning "Client scope 'roles' non trouvé - à créer manuellement" } } catch { Write-Warning "Erreur vérification protocol mapper: $($_.Exception.Message)" } # 7. Résumé Write-Step "7. Résumé de la configuration..." Write-Host @" ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ ✅ CONFIGURATION KEYCLOAK TERMINÉE ✅ ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ "@ -ForegroundColor Green Write-Host "📋 INFORMATIONS IMPORTANTES:" -ForegroundColor Yellow Write-Host "" Write-Host "🔑 SECRET BACKEND (Service Account):" -ForegroundColor Cyan Write-Host " $backendSecret" -ForegroundColor White Write-Host "" Write-Host "⚠️ ACTIONS REQUISES:" -ForegroundColor Yellow Write-Host " 1. Sauvegardez le secret backend ci-dessus" Write-Host " 2. Vérifiez le protocol mapper 'roles' dans Keycloak Admin Console" Write-Host " 3. Vérifiez que les rôles sont assignés au service account" Write-Host " 4. Créez les secrets Kubernetes avec ces informations" Write-Host "" Write-Host "🔗 URLS:" -ForegroundColor Cyan Write-Host " Keycloak Admin: $KeycloakUrl/admin" Write-Host " Realm: $Realm" Write-Host ""