import { NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { authOptions } from "@/app/api/auth/options"; import { getKeycloakAdminClient } from "@/lib/keycloak"; // Fix for Next.js "params should be awaited" error export const dynamic = 'force-dynamic'; export async function GET(request: Request, props: { params: Promise<{ userId?: string }> }) { const params = await props.params; try { const session = await getServerSession(authOptions); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Safely extract userId from params const rawUserId = params?.userId; const userId = typeof rawUserId === 'string' ? rawUserId : ''; if (!userId) { return NextResponse.json({ error: "User ID is required" }, { status: 400 }); } try { // Check for required environment variables before attempting to connect const missingVars = []; if (!process.env.KEYCLOAK_BASE_URL && !process.env.KEYCLOAK_ISSUER && !process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER) { missingVars.push('KEYCLOAK_BASE_URL or KEYCLOAK_ISSUER'); } if (!process.env.KEYCLOAK_CLIENT_ID) missingVars.push('KEYCLOAK_CLIENT_ID'); if (!process.env.KEYCLOAK_ADMIN_USERNAME) missingVars.push('KEYCLOAK_ADMIN_USERNAME'); if (!process.env.KEYCLOAK_ADMIN_PASSWORD) missingVars.push('KEYCLOAK_ADMIN_PASSWORD'); if (!process.env.KEYCLOAK_REALM) missingVars.push('KEYCLOAK_REALM'); // Note: Client secret might be required depending on client configuration console.log('Keycloak client config:', { clientId: process.env.KEYCLOAK_CLIENT_ID, hasClientSecret: !!process.env.KEYCLOAK_CLIENT_SECRET, username: process.env.KEYCLOAK_ADMIN_USERNAME, realm: process.env.KEYCLOAK_REALM, }); if (missingVars.length > 0) { console.error(`Missing Keycloak environment variables: ${missingVars.join(', ')}`); return NextResponse.json( { error: "Keycloak configuration incomplete", message: "Role management is currently unavailable due to missing configuration.", details: `Missing: ${missingVars.join(', ')}` }, { status: 503 } ); } const kcAdminClient = await getKeycloakAdminClient(); // Get all available roles const availableRoles = await kcAdminClient.roles.find(); // Get user's current roles const userRoles = await kcAdminClient.users.listRoleMappings({ id: userId, }); return NextResponse.json({ availableRoles, userRoles, }); } catch (keycloakError) { console.error("Error connecting to Keycloak:", keycloakError); return NextResponse.json( { error: "Failed to connect to Keycloak service", details: String(keycloakError) }, { status: 503 } ); } } catch (error) { console.error("Error fetching roles:", error); return NextResponse.json( { error: "Failed to fetch roles" }, { status: 500 } ); } } export async function PUT(request: Request, props: { params: Promise<{ userId?: string }> }) { const params = await props.params; try { const session = await getServerSession(authOptions); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Safely extract userId from params const rawUserId = params?.userId; const userId = typeof rawUserId === 'string' ? rawUserId : ''; if (!userId) { return NextResponse.json({ error: "User ID is required" }, { status: 400 }); } try { // Check for required environment variables before attempting to connect const missingVars = []; if (!process.env.KEYCLOAK_BASE_URL && !process.env.KEYCLOAK_ISSUER && !process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER) { missingVars.push('KEYCLOAK_BASE_URL or KEYCLOAK_ISSUER'); } if (!process.env.KEYCLOAK_CLIENT_ID) missingVars.push('KEYCLOAK_CLIENT_ID'); if (!process.env.KEYCLOAK_ADMIN_USERNAME) missingVars.push('KEYCLOAK_ADMIN_USERNAME'); if (!process.env.KEYCLOAK_ADMIN_PASSWORD) missingVars.push('KEYCLOAK_ADMIN_PASSWORD'); if (!process.env.KEYCLOAK_REALM) missingVars.push('KEYCLOAK_REALM'); // Note: Client secret might be required depending on client configuration console.log('Keycloak client config:', { clientId: process.env.KEYCLOAK_CLIENT_ID, hasClientSecret: !!process.env.KEYCLOAK_CLIENT_SECRET, username: process.env.KEYCLOAK_ADMIN_USERNAME, realm: process.env.KEYCLOAK_REALM, }); if (missingVars.length > 0) { console.error(`Missing Keycloak environment variables: ${missingVars.join(', ')}`); return NextResponse.json( { error: "Keycloak configuration incomplete", message: "Role management is currently unavailable due to missing configuration.", details: `Missing: ${missingVars.join(', ')}` }, { status: 503 } ); } const { roles } = await request.json(); const kcAdminClient = await getKeycloakAdminClient(); // Get all available roles const availableRoles = await kcAdminClient.roles.find(); // Get current user roles const currentRoles = await kcAdminClient.users.listRoleMappings({ id: userId, }); // Find roles to add and remove const rolesToAdd = roles.filter( (role: string) => !currentRoles.realmMappings?.some((r: any) => r.name === role) ); const rolesToRemove = currentRoles.realmMappings?.filter( (role: any) => !roles.includes(role.name) ); // Add new roles for (const roleName of rolesToAdd) { const role = availableRoles.find((r: any) => r.name === roleName); if (role) { await kcAdminClient.users.addRealmRoleMappings({ id: userId, roles: [role as any], }); } } // Remove old roles if (rolesToRemove && rolesToRemove.length > 0) { await kcAdminClient.users.delRealmRoleMappings({ id: userId, roles: rolesToRemove as any, }); } return NextResponse.json({ success: true }); } catch (keycloakError) { console.error("Error connecting to Keycloak:", keycloakError); return NextResponse.json( { error: "Failed to connect to Keycloak service", details: String(keycloakError) }, { status: 503 } ); } } catch (error) { console.error("Error updating roles:", error); return NextResponse.json( { error: "Failed to update roles" }, { status: 500 } ); } }