From 22bab9e8459995dfbc249802d4cc2e0e70824497 Mon Sep 17 00:00:00 2001 From: Alma Date: Wed, 9 Apr 2025 20:33:38 +0200 Subject: [PATCH] update api users and groups and users 3 --- app/api/users/[userId]/roles/route.ts | 126 ++++++++++++++++------- app/api/users/route.ts | 138 ++++++++++++++++++-------- 2 files changed, 191 insertions(+), 73 deletions(-) diff --git a/app/api/users/[userId]/roles/route.ts b/app/api/users/[userId]/roles/route.ts index 7b562d2c..02119000 100644 --- a/app/api/users/[userId]/roles/route.ts +++ b/app/api/users/[userId]/roles/route.ts @@ -2,20 +2,8 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; import { NextResponse } from "next/server"; -export async function PUT( - req: Request, - { params }: { params: { userId: string } } -) { - const session = await getServerSession(authOptions); - - if (!session) { - return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); - } - +async function getAdminToken() { try { - const { roles } = await req.json(); - - // Get client credentials token const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { @@ -31,62 +19,132 @@ export async function PUT( } ); - const tokenData = await tokenResponse.json(); + const data = await tokenResponse.json(); - if (!tokenResponse.ok) { - console.error("Failed to get token:", tokenData); - return NextResponse.json({ error: "Failed to get token" }, { status: 500 }); + if (!tokenResponse.ok || !data.access_token) { + console.error('Token Error:', data); + return null; } - // Get available roles + return data.access_token; + } catch (error) { + console.error('Token Error:', error); + return null; + } +} + +export async function PUT( + req: Request, + { params }: { params: { userId: string } } +) { + const session = await getServerSession(authOptions); + + if (!session) { + return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); + } + + try { + const { roles } = await req.json(); + const token = await getAdminToken(); + + if (!token) { + return NextResponse.json({ error: "Erreur d'authentification" }, { status: 401 }); + } + + // First, get all available roles from Keycloak const rolesResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/roles`, { headers: { - 'Authorization': `Bearer ${tokenData.access_token}`, + Authorization: `Bearer ${token}`, }, } ); if (!rolesResponse.ok) { const errorData = await rolesResponse.json(); - console.error("Failed to get roles:", errorData); - return NextResponse.json({ error: "Failed to get roles" }, { status: rolesResponse.status }); + console.error("Failed to fetch roles:", errorData); + return NextResponse.json({ error: "Erreur lors de la récupération des rôles" }, { status: rolesResponse.status }); } const availableRoles = await rolesResponse.json(); + console.log("Available roles:", availableRoles); // Map role names to role objects const roleObjects = roles.map((roleName: string) => { const role = availableRoles.find((r: any) => r.name === roleName); if (!role) { - throw new Error(`Role ${roleName} not found`); + throw new Error(`Role ${roleName} not found in Keycloak`); } return role; }); - // Update user roles - const updateResponse = await fetch( + // First, get current role mappings + const currentMappingsResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${params.userId}/role-mappings/realm`, { - method: 'POST', headers: { - 'Authorization': `Bearer ${tokenData.access_token}`, - 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, - body: JSON.stringify(roleObjects), } ); - if (!updateResponse.ok) { - const errorData = await updateResponse.json(); - console.error("Failed to update roles:", errorData); - return NextResponse.json({ error: "Failed to update roles" }, { status: updateResponse.status }); + if (!currentMappingsResponse.ok) { + const errorData = await currentMappingsResponse.json(); + console.error("Failed to fetch current role mappings:", errorData); + return NextResponse.json({ error: "Erreur lors de la récupération des rôles actuels" }, { status: currentMappingsResponse.status }); } - return NextResponse.json({ success: true }); + const currentMappings = await currentMappingsResponse.json(); + + // Remove all current role mappings + if (currentMappings.length > 0) { + const deleteResponse = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${params.userId}/role-mappings/realm`, + { + method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(currentMappings), + } + ); + + if (!deleteResponse.ok) { + const errorData = await deleteResponse.json(); + console.error("Failed to remove current roles:", errorData); + return NextResponse.json({ error: "Erreur lors de la suppression des rôles actuels" }, { status: deleteResponse.status }); + } + } + + // Add new role mappings + if (roleObjects.length > 0) { + const addResponse = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${params.userId}/role-mappings/realm`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(roleObjects), + } + ); + + if (!addResponse.ok) { + const errorData = await addResponse.json(); + console.error("Failed to add new roles:", errorData); + return NextResponse.json({ error: "Erreur lors de l'ajout des nouveaux rôles" }, { status: addResponse.status }); + } + } + + return NextResponse.json({ success: true, roles }); } catch (error) { console.error("Error in update roles:", error); - return NextResponse.json({ error: "Internal server error" }, { status: 500 }); + return NextResponse.json( + { error: error instanceof Error ? error.message : "Une erreur est survenue" }, + { status: 500 } + ); } } \ No newline at end of file diff --git a/app/api/users/route.ts b/app/api/users/route.ts index be628816..07afa848 100644 --- a/app/api/users/route.ts +++ b/app/api/users/route.ts @@ -144,18 +144,8 @@ function getCurrentUser(session: any) { }; } -export async function POST(req: Request) { - const session = await getServerSession(authOptions); - - if (!session) { - return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); - } - +async function getAdminToken() { try { - const data = await req.json(); - console.log("Creating user:", data); - - // First get an admin token using client credentials const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { @@ -171,37 +161,92 @@ export async function POST(req: Request) { } ); - const tokenData = await tokenResponse.json(); - - if (!tokenResponse.ok) { - console.error("Failed to get admin token:", tokenData); + const data = await tokenResponse.json(); + + if (!tokenResponse.ok || !data.access_token) { + console.error('Token Error:', data); + return null; + } + + return data.access_token; + } catch (error) { + console.error('Token Error:', error); + return null; + } +} + +export async function POST(req: Request) { + const session = await getServerSession(authOptions); + + if (!session) { + return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); + } + + try { + const data = await req.json(); + console.log("Creating user:", data); + + const token = await getAdminToken(); + if (!token) { + return NextResponse.json({ error: "Erreur d'authentification" }, { status: 401 }); + } + + // First, get all available roles from Keycloak + const rolesResponse = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/roles`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + if (!rolesResponse.ok) { + const errorData = await rolesResponse.json(); + console.error("Failed to fetch roles:", errorData); + return NextResponse.json({ error: "Erreur lors de la récupération des rôles" }, { status: rolesResponse.status }); + } + + const availableRoles = await rolesResponse.json(); + console.log("Available roles:", availableRoles); + + // Verify that the requested roles exist + const requestedRoles = data.roles || []; + const validRoles = requestedRoles.filter((roleName: string) => + availableRoles.some((r: any) => r.name === roleName) + ); + + if (validRoles.length === 0) { return NextResponse.json( - { error: "Erreur d'authentification" }, - { status: 401 } + { error: "Aucun rôle valide n'a été spécifié" }, + { status: 400 } ); } - // Format user data for Keycloak - const keycloakUser = { - username: data.username, - enabled: true, - emailVerified: true, - firstName: data.firstName, - lastName: data.lastName, - email: data.email, - groups: [data.realmRoles], // Use groups instead of roles - }; - - // Create user in Keycloak using admin token + // Create the user const createResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`, { method: "POST", headers: { - Authorization: `Bearer ${tokenData.access_token}`, + Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, - body: JSON.stringify(keycloakUser), + body: JSON.stringify({ + username: data.username, + enabled: true, + emailVerified: true, + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + credentials: [ + { + type: "password", + value: data.password, + temporary: false, + }, + ], + }), } ); @@ -212,7 +257,7 @@ export async function POST(req: Request) { if (!createResponse.ok) { const errorData = await createResponse.json(); - console.log("Keycloak error:", errorData); + console.error("Keycloak error:", errorData); if (errorData.errorMessage?.includes("User exists with same username")) { return NextResponse.json( @@ -236,7 +281,7 @@ export async function POST(req: Request) { `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users?username=${data.username}`, { headers: { - Authorization: `Bearer ${tokenData.access_token}`, + Authorization: `Bearer ${token}`, }, } ); @@ -251,22 +296,37 @@ export async function POST(req: Request) { ); } - // Add user to group - await fetch( - `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/groups/${data.realmRoles}`, + // Add roles to the user + const roleObjects = validRoles.map((roleName: string) => + availableRoles.find((r: any) => r.name === roleName) + ); + + const roleResponse = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/role-mappings/realm`, { - method: "PUT", + method: "POST", headers: { - Authorization: `Bearer ${tokenData.access_token}`, + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", }, + body: JSON.stringify(roleObjects), } ); + if (!roleResponse.ok) { + const errorData = await roleResponse.json(); + console.error("Failed to add roles:", errorData); + return NextResponse.json( + { error: "Erreur lors de l'ajout des rôles", details: errorData }, + { status: 500 } + ); + } + return NextResponse.json({ success: true, user: { ...user, - roles: [data.realmRoles], + roles: validRoles, }, });