319 lines
13 KiB
PowerShell
319 lines
13 KiB
PowerShell
#!/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 ""
|
||
|