265 lines
7.0 KiB
TypeScript
265 lines
7.0 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getServerSession } from "next-auth/next";
|
|
import { authOptions } from "../../auth/[...nextauth]/route";
|
|
|
|
/**
|
|
* Get an admin token
|
|
*/
|
|
async function getAdminToken() {
|
|
try {
|
|
const tokenResponse = await fetch(
|
|
`${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/token`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
},
|
|
body: new URLSearchParams({
|
|
grant_type: "client_credentials",
|
|
client_id: process.env.KEYCLOAK_CLIENT_ID!,
|
|
client_secret: process.env.KEYCLOAK_CLIENT_SECRET!,
|
|
}),
|
|
}
|
|
);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get users from Keycloak
|
|
*/
|
|
export async function GET(request: NextRequest) {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
// Check authentication
|
|
if (!session || !session.user) {
|
|
return NextResponse.json(
|
|
{ error: "Unauthorized" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Only administrators can list users
|
|
const isAdmin = session.user.role?.includes('admin');
|
|
if (!isAdmin) {
|
|
return NextResponse.json(
|
|
{ error: "Forbidden: Admin role required" },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Get admin token
|
|
const token = await getAdminToken();
|
|
if (!token) {
|
|
return NextResponse.json(
|
|
{ error: "Failed to get admin token" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Parse search params
|
|
const { searchParams } = new URL(request.url);
|
|
const query = searchParams.get('query') || '';
|
|
const max = searchParams.get('max') || '100';
|
|
|
|
// Fetch users from Keycloak
|
|
const keycloakUrl = `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`;
|
|
const queryParams = new URLSearchParams();
|
|
|
|
if (query) {
|
|
queryParams.append('search', query);
|
|
}
|
|
|
|
queryParams.append('max', max);
|
|
|
|
const usersResponse = await fetch(
|
|
`${keycloakUrl}?${queryParams.toString()}`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!usersResponse.ok) {
|
|
const errorData = await usersResponse.json();
|
|
return NextResponse.json(
|
|
{ error: "Failed to fetch users", details: errorData },
|
|
{ status: usersResponse.status }
|
|
);
|
|
}
|
|
|
|
const users = await usersResponse.json();
|
|
|
|
// Return user data
|
|
return NextResponse.json(users);
|
|
} catch (error) {
|
|
console.error("Error fetching users:", error);
|
|
return NextResponse.json(
|
|
{ error: "Server error fetching users" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new user in Keycloak
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
// Check authentication
|
|
if (!session || !session.user) {
|
|
return NextResponse.json(
|
|
{ error: "Unauthorized" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Only administrators can create users
|
|
const isAdmin = session.user.role?.includes('admin');
|
|
if (!isAdmin) {
|
|
return NextResponse.json(
|
|
{ error: "Forbidden: Admin role required" },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Get admin token
|
|
const token = await getAdminToken();
|
|
if (!token) {
|
|
return NextResponse.json(
|
|
{ error: "Failed to get admin token" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Get user data from request
|
|
const userData = await request.json();
|
|
|
|
// Validate user data
|
|
if (!userData.username || !userData.email) {
|
|
return NextResponse.json(
|
|
{ error: "Username and email are required" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Create user in Keycloak
|
|
const keycloakUrl = `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`;
|
|
const createResponse = await fetch(keycloakUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
username: userData.username,
|
|
email: userData.email,
|
|
enabled: true,
|
|
emailVerified: true,
|
|
firstName: userData.firstName || '',
|
|
lastName: userData.lastName || '',
|
|
credentials: userData.password ? [
|
|
{
|
|
type: "password",
|
|
value: userData.password,
|
|
temporary: false
|
|
}
|
|
] : undefined
|
|
}),
|
|
});
|
|
|
|
if (!createResponse.ok) {
|
|
const errorData = await createResponse.json().catch(() => ({}));
|
|
return NextResponse.json(
|
|
{ error: "Failed to create user", details: errorData },
|
|
{ status: createResponse.status }
|
|
);
|
|
}
|
|
|
|
// If user was created successfully, get the user ID
|
|
const userResponse = await fetch(
|
|
`${keycloakUrl}?username=${encodeURIComponent(userData.username)}`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!userResponse.ok) {
|
|
return NextResponse.json(
|
|
{ error: "User created but failed to retrieve user details" },
|
|
{ status: 207 } // Partial success
|
|
);
|
|
}
|
|
|
|
const users = await userResponse.json();
|
|
const createdUser = users[0];
|
|
|
|
// If roles are specified, assign them
|
|
if (userData.roles && Array.isArray(userData.roles) && userData.roles.length > 0) {
|
|
// Get available roles
|
|
const rolesResponse = await fetch(
|
|
`${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/roles`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
|
|
if (rolesResponse.ok) {
|
|
const availableRoles = await rolesResponse.json();
|
|
|
|
// Filter valid roles
|
|
const validRoles = userData.roles.map((roleName: string) =>
|
|
availableRoles.find((r: any) => r.name === roleName)
|
|
).filter(Boolean);
|
|
|
|
if (validRoles.length > 0) {
|
|
// Assign roles to user
|
|
await fetch(
|
|
`${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${createdUser.id}/role-mappings/realm`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(validRoles),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
user: createdUser
|
|
});
|
|
} catch (error) {
|
|
console.error("Error creating user:", error);
|
|
return NextResponse.json(
|
|
{ error: "Server error creating user" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|