diff --git a/app/api/groups/[groupId]/members/route.ts b/app/api/groups/[groupId]/members/route.ts
new file mode 100644
index 00000000..64fcf5fc
--- /dev/null
+++ b/app/api/groups/[groupId]/members/route.ts
@@ -0,0 +1,121 @@
+import { getServerSession } from "next-auth/next";
+import { authOptions } from "@/app/api/auth/[...nextauth]/route";
+import { NextResponse } from "next/server";
+
+export async function GET(
+ req: Request,
+ { params }: { params: { groupId: string } }
+) {
+ const session = await getServerSession(authOptions);
+
+ if (!session) {
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
+ }
+
+ try {
+ // Get client credentials token
+ const tokenResponse = await fetch(
+ `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/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 tokenData = await tokenResponse.json();
+
+ if (!tokenResponse.ok) {
+ console.error("Failed to get token:", tokenData);
+ return NextResponse.json({ error: "Failed to get token" }, { status: 500 });
+ }
+
+ // Get group members
+ const membersResponse = await fetch(
+ `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/groups/${params.groupId}/members`,
+ {
+ headers: {
+ 'Authorization': `Bearer ${tokenData.access_token}`,
+ },
+ }
+ );
+
+ if (!membersResponse.ok) {
+ const errorData = await membersResponse.json();
+ console.error("Failed to get group members:", errorData);
+ return NextResponse.json({ error: "Failed to get group members" }, { status: membersResponse.status });
+ }
+
+ const members = await membersResponse.json();
+ return NextResponse.json(members);
+ } catch (error) {
+ console.error("Error in get group members:", error);
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
+ }
+}
+
+export async function POST(
+ req: Request,
+ { params }: { params: { groupId: string } }
+) {
+ const session = await getServerSession(authOptions);
+
+ if (!session) {
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
+ }
+
+ try {
+ const { userId } = 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`,
+ {
+ 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 tokenData = await tokenResponse.json();
+
+ if (!tokenResponse.ok) {
+ console.error("Failed to get token:", tokenData);
+ return NextResponse.json({ error: "Failed to get token" }, { status: 500 });
+ }
+
+ // Add user to group
+ const addResponse = await fetch(
+ `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${userId}/groups/${params.groupId}`,
+ {
+ method: 'PUT',
+ headers: {
+ 'Authorization': `Bearer ${tokenData.access_token}`,
+ },
+ }
+ );
+
+ if (!addResponse.ok) {
+ const errorData = await addResponse.json();
+ console.error("Failed to add user to group:", errorData);
+ return NextResponse.json({ error: "Failed to add user to group" }, { status: addResponse.status });
+ }
+
+ return NextResponse.json({ success: true });
+ } catch (error) {
+ console.error("Error in add user to group:", error);
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/users/[userId]/roles/route.ts b/app/api/users/[userId]/roles/route.ts
index 02119000..a52f1474 100644
--- a/app/api/users/[userId]/roles/route.ts
+++ b/app/api/users/[userId]/roles/route.ts
@@ -1,149 +1,97 @@
-import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { NextResponse } from "next/server";
+import { getServerSession } from "next-auth";
+import { authOptions } from "@/app/api/auth/[...nextauth]/route";
+import { getKeycloakAdminClient } from "@/lib/keycloak";
+import { RoleRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
-async function getAdminToken() {
+export async function GET(
+ request: Request,
+ { params }: { params: { userId: string } }
+) {
try {
- const tokenResponse = await fetch(
- `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/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;
+ const session = await getServerSession(authOptions);
+ if (!session) {
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
- return data.access_token;
+ const { userId } = params;
+ 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 (error) {
- console.error('Token Error:', error);
- return null;
+ console.error("Error fetching roles:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch roles" },
+ { status: 500 }
+ );
}
}
export async function PUT(
- req: Request,
+ request: 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 });
+ const session = await getServerSession(authOptions);
+ if (!session) {
+ return NextResponse.json({ error: "Unauthorized" }, { 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}`,
- },
- }
- );
+ const { userId } = params;
+ const { roles } = await request.json();
+ const kcAdminClient = await getKeycloakAdminClient();
- 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);
-
- // 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 in Keycloak`);
- }
- return role;
+ // Get all available roles
+ const availableRoles = await kcAdminClient.roles.find();
+
+ // Get current user roles
+ const currentRoles = await kcAdminClient.users.listRoleMappings({
+ id: userId,
});
- // 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`,
- {
- headers: {
- Authorization: `Bearer ${token}`,
- },
- }
+ // Find roles to add and remove
+ const rolesToAdd = roles.filter(
+ (role: string) => !currentRoles.clientMappings?.some((r: RoleRepresentation) => r.name === role)
+ );
+ const rolesToRemove = currentRoles.clientMappings?.filter(
+ (role: RoleRepresentation) => !roles.includes(role.name)
);
- 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 });
- }
-
- 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 roles
+ for (const roleName of rolesToAdd) {
+ const role = availableRoles.find((r: RoleRepresentation) => r.name === roleName);
+ if (role) {
+ await kcAdminClient.users.addRealmRoleMappings({
+ id: userId,
+ roles: [role],
+ });
}
}
- // 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 });
- }
+ // Remove old roles
+ if (rolesToRemove && rolesToRemove.length > 0) {
+ await kcAdminClient.users.delRealmRoleMappings({
+ id: userId,
+ roles: rolesToRemove,
+ });
}
- return NextResponse.json({ success: true, roles });
+ return NextResponse.json({ success: true });
} catch (error) {
- console.error("Error in update roles:", error);
+ console.error("Error updating roles:", error);
return NextResponse.json(
- { error: error instanceof Error ? error.message : "Une erreur est survenue" },
+ { error: "Failed to update roles" },
{ status: 500 }
);
}
diff --git a/components/users/users-table.tsx b/components/users/users-table.tsx
index db6aabbc..6f0393b9 100644
--- a/components/users/users-table.tsx
+++ b/components/users/users-table.tsx
@@ -551,6 +551,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
variant="ghost"
className="h-8 w-8 p-0"
onClick={(e) => {
+ e.preventDefault();
e.stopPropagation();
}}
>
@@ -562,6 +563,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
Actions
{
+ e.preventDefault();
e.stopPropagation();
handleEdit(user.id);
}}
@@ -571,6 +573,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
{
+ e.preventDefault();
e.stopPropagation();
handleManageRoles(user.id);
}}
@@ -580,6 +583,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
{
+ e.preventDefault();
e.stopPropagation();
handleChangePassword(user.id);
}}
@@ -589,6 +593,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
{
+ e.preventDefault();
e.stopPropagation();
handleToggleUserStatus(user.id, user.enabled);
}}
@@ -609,6 +614,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
{
+ e.preventDefault();
e.stopPropagation();
handleDelete(user.id);
}}