keycloak-model-context-protocol 0.0.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +188 -101
- package/dist/config.js +178 -0
- package/dist/config.js.map +1 -0
- package/dist/guards.js +47 -0
- package/dist/guards.js.map +1 -0
- package/dist/index.js +36 -174
- package/dist/index.js.map +1 -0
- package/dist/keycloak/errors.js +86 -0
- package/dist/keycloak/errors.js.map +1 -0
- package/dist/keycloak/gateway.js +131 -0
- package/dist/keycloak/gateway.js.map +1 -0
- package/dist/logging.js +49 -0
- package/dist/logging.js.map +1 -0
- package/dist/prompts/audit-realm-security.js +26 -0
- package/dist/prompts/audit-realm-security.js.map +1 -0
- package/dist/prompts/index.js +12 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/investigate-failed-logins.js +34 -0
- package/dist/prompts/investigate-failed-logins.js.map +1 -0
- package/dist/prompts/offboard-employee.js +31 -0
- package/dist/prompts/offboard-employee.js.map +1 -0
- package/dist/prompts/onboard-employee.js +50 -0
- package/dist/prompts/onboard-employee.js.map +1 -0
- package/dist/prompts/shared.js +46 -0
- package/dist/prompts/shared.js.map +1 -0
- package/dist/resources/index.js +8 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/realms.js +96 -0
- package/dist/resources/realms.js.map +1 -0
- package/dist/resources/shared.js +44 -0
- package/dist/resources/shared.js.map +1 -0
- package/dist/resources/users.js +26 -0
- package/dist/resources/users.js.map +1 -0
- package/dist/server.js +16 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/clients.js +137 -0
- package/dist/tools/clients.js.map +1 -0
- package/dist/tools/events.js +161 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/groups.js +135 -0
- package/dist/tools/groups.js.map +1 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/realms.js +99 -0
- package/dist/tools/realms.js.map +1 -0
- package/dist/tools/roles.js +270 -0
- package/dist/tools/roles.js.map +1 -0
- package/dist/tools/security.js +58 -0
- package/dist/tools/security.js.map +1 -0
- package/dist/tools/sessions.js +68 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/shared.js +144 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/users.js +272 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -12
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertPathSegment, requireConfirmation } from "../guards.js";
|
|
3
|
+
import { ToolError } from "../keycloak/errors.js";
|
|
4
|
+
import { defineTool, pageOf, paginationShape, realmField, toPage } from "./shared.js";
|
|
5
|
+
const roleSummary = z.object({
|
|
6
|
+
name: z.string().describe("Role name."),
|
|
7
|
+
description: z.string().optional().describe("Role description, if set."),
|
|
8
|
+
composite: z.boolean().optional().describe("Whether the role is composite."),
|
|
9
|
+
clientRole: z.boolean().optional().describe("Whether this is a client role."),
|
|
10
|
+
});
|
|
11
|
+
function toRoleSummary(role) {
|
|
12
|
+
return {
|
|
13
|
+
name: role.name ?? "",
|
|
14
|
+
...(role.description !== undefined && { description: role.description }),
|
|
15
|
+
...(role.composite !== undefined && { composite: role.composite }),
|
|
16
|
+
...(role.clientRole !== undefined && { clientRole: role.clientRole }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const clientIdField = z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1)
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Client ID (the readable `clientId`, not the internal UUID). Omit for realm roles.");
|
|
24
|
+
/** Resolves a readable clientId to the client's internal id. */
|
|
25
|
+
async function lookupClientUuid(kc, realm, clientId) {
|
|
26
|
+
const clients = await kc.clients.find({ realm, clientId });
|
|
27
|
+
const match = clients.find((c) => c.clientId === clientId);
|
|
28
|
+
if (!match?.id)
|
|
29
|
+
throw new ToolError("not_found", `Client '${clientId}' does not exist in realm '${realm}'.`);
|
|
30
|
+
return match.id;
|
|
31
|
+
}
|
|
32
|
+
const roleList = defineTool({
|
|
33
|
+
name: "role_list",
|
|
34
|
+
title: "List roles",
|
|
35
|
+
description: "List realm roles, or the roles of one client when `clientId` is given, optionally filtered by a search string, with pagination.",
|
|
36
|
+
kind: "read",
|
|
37
|
+
input: z.object({
|
|
38
|
+
realm: realmField,
|
|
39
|
+
clientId: clientIdField,
|
|
40
|
+
search: z.string().optional().describe("Only roles whose name contains this text."),
|
|
41
|
+
...paginationShape,
|
|
42
|
+
}),
|
|
43
|
+
output: pageOf(roleSummary).extend({
|
|
44
|
+
realm: z.string().describe("Realm that was queried."),
|
|
45
|
+
clientId: z.string().optional().describe("Client whose roles were listed, if any."),
|
|
46
|
+
}),
|
|
47
|
+
async handler({ realm, clientId, search, first, max }, { gateway }) {
|
|
48
|
+
const rows = await gateway.run(async (kc) => {
|
|
49
|
+
if (clientId === undefined) {
|
|
50
|
+
return kc.roles.find({
|
|
51
|
+
realm,
|
|
52
|
+
first,
|
|
53
|
+
max: max + 1,
|
|
54
|
+
briefRepresentation: true,
|
|
55
|
+
...(search !== undefined && { search }),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const id = await lookupClientUuid(kc, realm, clientId);
|
|
59
|
+
// The client-roles endpoint has no paging in the admin client; slice locally.
|
|
60
|
+
const all = await kc.clients.listRoles({ realm, id });
|
|
61
|
+
const needle = search?.toLowerCase();
|
|
62
|
+
return all
|
|
63
|
+
.filter((r) => needle === undefined || (r.name ?? "").toLowerCase().includes(needle))
|
|
64
|
+
.slice(first, first + max + 1);
|
|
65
|
+
});
|
|
66
|
+
const page = toPage(rows.map(toRoleSummary), first, max);
|
|
67
|
+
return {
|
|
68
|
+
data: { ...page, realm, ...(clientId !== undefined && { clientId }) },
|
|
69
|
+
summary: `Found ${page.items.length} role(s) in realm '${realm}' (first=${first}, max=${max}${page.hasMore ? ", more available" : ""}).`,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const userRolesList = defineTool({
|
|
74
|
+
name: "user_roles_list",
|
|
75
|
+
title: "List user role mappings",
|
|
76
|
+
description: "List the realm and client roles assigned to a user. With effective=true, composite roles are expanded; client roles are then resolved for `clientId` if given, otherwise for the clients the user has direct client-role mappings in. Optional `clientId` limits client roles to that client.",
|
|
77
|
+
kind: "read",
|
|
78
|
+
input: z.object({
|
|
79
|
+
realm: realmField,
|
|
80
|
+
userId: z.string().min(1).describe("ID (UUID) of the user."),
|
|
81
|
+
clientId: clientIdField,
|
|
82
|
+
effective: z
|
|
83
|
+
.boolean()
|
|
84
|
+
.default(false)
|
|
85
|
+
.describe("If true, include roles inherited via composite roles (default false)."),
|
|
86
|
+
}),
|
|
87
|
+
output: z.object({
|
|
88
|
+
realm: z.string().describe("Realm of the user."),
|
|
89
|
+
userId: z.string().describe("ID of the user."),
|
|
90
|
+
effective: z.boolean().describe("Whether composite roles were expanded."),
|
|
91
|
+
realmRoles: z.array(roleSummary).describe("Realm roles of the user."),
|
|
92
|
+
clientRoles: z
|
|
93
|
+
.array(z.object({
|
|
94
|
+
clientId: z.string().describe("Client the roles belong to."),
|
|
95
|
+
roles: z.array(roleSummary).describe("Roles of the user in that client."),
|
|
96
|
+
}))
|
|
97
|
+
.describe("Client roles of the user, grouped by client."),
|
|
98
|
+
}),
|
|
99
|
+
async handler({ realm, userId, clientId, effective }, { gateway }) {
|
|
100
|
+
const data = await gateway.run(async (kc) => {
|
|
101
|
+
const mappings = await kc.users.listRoleMappings({ realm, id: userId });
|
|
102
|
+
const realmRoles = effective
|
|
103
|
+
? await kc.users.listCompositeRealmRoleMappings({ realm, id: userId })
|
|
104
|
+
: (mappings.realmMappings ?? []);
|
|
105
|
+
const direct = Object.entries((mappings.clientMappings ?? {}));
|
|
106
|
+
let clientRoles;
|
|
107
|
+
if (!effective) {
|
|
108
|
+
clientRoles = direct
|
|
109
|
+
.filter(([key, v]) => clientId === undefined || (v.client ?? key) === clientId)
|
|
110
|
+
.map(([key, v]) => ({
|
|
111
|
+
clientId: v.client ?? key,
|
|
112
|
+
roles: (v.mappings ?? []).map(toRoleSummary),
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const targets = clientId === undefined
|
|
117
|
+
? direct.map(([key, v]) => ({ clientId: v.client ?? key, uuid: v.id }))
|
|
118
|
+
: [{ clientId, uuid: undefined }];
|
|
119
|
+
clientRoles = [];
|
|
120
|
+
for (const t of targets) {
|
|
121
|
+
const uuid = t.uuid ?? (await lookupClientUuid(kc, realm, t.clientId));
|
|
122
|
+
const roles = await kc.users.listCompositeClientRoleMappings({
|
|
123
|
+
realm,
|
|
124
|
+
id: userId,
|
|
125
|
+
clientUniqueId: uuid,
|
|
126
|
+
});
|
|
127
|
+
clientRoles.push({ clientId: t.clientId, roles: roles.map(toRoleSummary) });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { realm, userId, effective, realmRoles: realmRoles.map(toRoleSummary), clientRoles };
|
|
131
|
+
});
|
|
132
|
+
const clientCount = data.clientRoles.reduce((n, c) => n + c.roles.length, 0);
|
|
133
|
+
return {
|
|
134
|
+
data,
|
|
135
|
+
summary: `User ${userId} has ${data.realmRoles.length} realm role(s) and ${clientCount} client role(s) in realm '${realm}'${effective ? " (effective)" : ""}.`,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
async function resolveRoles(kc, realm, names, clientId) {
|
|
140
|
+
for (const name of names)
|
|
141
|
+
assertPathSegment(name, "role name");
|
|
142
|
+
const clientUuid = clientId === undefined ? undefined : await lookupClientUuid(kc, realm, clientId);
|
|
143
|
+
const roles = [];
|
|
144
|
+
const missing = [];
|
|
145
|
+
for (const name of new Set(names)) {
|
|
146
|
+
const rep = clientUuid === undefined
|
|
147
|
+
? await kc.roles.findOneByName({ realm, name })
|
|
148
|
+
: await kc.clients.findRole({ realm, id: clientUuid, roleName: name });
|
|
149
|
+
if (rep?.id)
|
|
150
|
+
roles.push({ id: rep.id, name });
|
|
151
|
+
else
|
|
152
|
+
missing.push(name);
|
|
153
|
+
}
|
|
154
|
+
if (missing.length > 0) {
|
|
155
|
+
const where = clientId === undefined ? `realm '${realm}'` : `client '${clientId}' in realm '${realm}'`;
|
|
156
|
+
throw new ToolError("not_found", `Unknown role(s) in ${where}: ${missing.join(", ")}. Nothing was changed.`);
|
|
157
|
+
}
|
|
158
|
+
return clientUuid === undefined ? { roles } : { roles, clientUuid };
|
|
159
|
+
}
|
|
160
|
+
/** Roles that grant administrative power over the realm. */
|
|
161
|
+
export function isPrivilegedRole(name, clientId) {
|
|
162
|
+
if (clientId === undefined)
|
|
163
|
+
return name === "admin" || name === "create-realm";
|
|
164
|
+
return clientId === "realm-management" || name === "realm-admin" || name.startsWith("manage-");
|
|
165
|
+
}
|
|
166
|
+
const rolesField = z
|
|
167
|
+
.array(z.string().min(1))
|
|
168
|
+
.min(1)
|
|
169
|
+
.max(50)
|
|
170
|
+
.describe("Role names (not IDs), e.g. ['editor']. Use role_list to see available roles.");
|
|
171
|
+
const roleMutationOutput = z.object({
|
|
172
|
+
realm: z.string().describe("Realm of the user."),
|
|
173
|
+
userId: z.string().describe("ID of the user."),
|
|
174
|
+
clientId: z.string().optional().describe("Client of the roles; absent for realm roles."),
|
|
175
|
+
roles: z.array(z.string()).describe("Role names that were processed."),
|
|
176
|
+
});
|
|
177
|
+
const userRoleAssign = defineTool({
|
|
178
|
+
name: "user_role_assign",
|
|
179
|
+
title: "Assign roles to user",
|
|
180
|
+
description: "Assign realm roles (or roles of one client when `clientId` is given) to a user. Unknown roles abort the call without changes. Assigning administrative roles (realm role admin/create-realm, any role of client realm-management, realm-admin or manage-*) additionally requires confirm: true after the user approved.",
|
|
181
|
+
kind: "write",
|
|
182
|
+
idempotent: true,
|
|
183
|
+
input: z.object({
|
|
184
|
+
realm: realmField,
|
|
185
|
+
userId: z.string().min(1).describe("ID (UUID) of the user."),
|
|
186
|
+
roles: rolesField,
|
|
187
|
+
clientId: clientIdField,
|
|
188
|
+
confirm: z
|
|
189
|
+
.boolean()
|
|
190
|
+
.optional()
|
|
191
|
+
.describe("Must be true to assign administrative roles. Only set it after the user approved."),
|
|
192
|
+
}),
|
|
193
|
+
output: roleMutationOutput.extend({
|
|
194
|
+
assigned: z.boolean().describe("True once the roles are assigned."),
|
|
195
|
+
}),
|
|
196
|
+
async handler({ realm, userId, roles: names, clientId, confirm }, { gateway, config }) {
|
|
197
|
+
const resolved = await gateway.run(async (kc) => {
|
|
198
|
+
const found = await resolveRoles(kc, realm, names, clientId);
|
|
199
|
+
const privileged = found.roles.filter((r) => isPrivilegedRole(r.name, clientId));
|
|
200
|
+
if (privileged.length > 0) {
|
|
201
|
+
const list = privileged.map((r) => r.name).join(", ");
|
|
202
|
+
requireConfirmation(config, confirm, `grant the administrative role(s) ${list} to user ${userId} in realm '${realm}'.`);
|
|
203
|
+
}
|
|
204
|
+
if (found.clientUuid === undefined) {
|
|
205
|
+
await kc.users.addRealmRoleMappings({ realm, id: userId, roles: found.roles });
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
await kc.users.addClientRoleMappings({
|
|
209
|
+
realm,
|
|
210
|
+
id: userId,
|
|
211
|
+
clientUniqueId: found.clientUuid,
|
|
212
|
+
roles: found.roles,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return found.roles;
|
|
216
|
+
});
|
|
217
|
+
const roles = resolved.map((r) => r.name);
|
|
218
|
+
return {
|
|
219
|
+
data: { realm, userId, ...(clientId !== undefined && { clientId }), roles, assigned: true },
|
|
220
|
+
summary: `Assigned ${roles.length} role(s) to user ${userId} in realm '${realm}'.`,
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const userRoleRevoke = defineTool({
|
|
225
|
+
name: "user_role_revoke",
|
|
226
|
+
title: "Revoke roles from user",
|
|
227
|
+
description: "Remove realm roles (or roles of one client when `clientId` is given) from a user. Unknown roles abort the call without changes. Requires confirm: true (ask the user first).",
|
|
228
|
+
kind: "destructive",
|
|
229
|
+
idempotent: true,
|
|
230
|
+
input: z.object({
|
|
231
|
+
realm: realmField,
|
|
232
|
+
userId: z.string().min(1).describe("ID (UUID) of the user."),
|
|
233
|
+
roles: rolesField,
|
|
234
|
+
clientId: clientIdField,
|
|
235
|
+
}),
|
|
236
|
+
output: roleMutationOutput.extend({
|
|
237
|
+
revoked: z.boolean().describe("True once the roles are removed."),
|
|
238
|
+
}),
|
|
239
|
+
async preview({ realm, userId, roles, clientId }, { gateway }) {
|
|
240
|
+
const user = await gateway.run((kc) => kc.users.findOne({ realm, id: userId }));
|
|
241
|
+
if (!user)
|
|
242
|
+
throw new ToolError("not_found", `User ${userId} does not exist in realm '${realm}'.`);
|
|
243
|
+
const scope = clientId === undefined ? "realm" : `client '${clientId}'`;
|
|
244
|
+
return `remove ${scope} role(s) ${roles.join(", ")} from user '${user.username ?? userId}' in realm '${realm}'.`;
|
|
245
|
+
},
|
|
246
|
+
async handler({ realm, userId, roles: names, clientId }, { gateway }) {
|
|
247
|
+
const resolved = await gateway.run(async (kc) => {
|
|
248
|
+
const found = await resolveRoles(kc, realm, names, clientId);
|
|
249
|
+
if (found.clientUuid === undefined) {
|
|
250
|
+
await kc.users.delRealmRoleMappings({ realm, id: userId, roles: found.roles });
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
await kc.users.delClientRoleMappings({
|
|
254
|
+
realm,
|
|
255
|
+
id: userId,
|
|
256
|
+
clientUniqueId: found.clientUuid,
|
|
257
|
+
roles: found.roles,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return found.roles;
|
|
261
|
+
});
|
|
262
|
+
const roles = resolved.map((r) => r.name);
|
|
263
|
+
return {
|
|
264
|
+
data: { realm, userId, ...(clientId !== undefined && { clientId }), roles, revoked: true },
|
|
265
|
+
summary: `Revoked ${roles.length} role(s) from user ${userId} in realm '${realm}'.`,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
export const roleTools = [roleList, userRolesList, userRoleAssign, userRoleRevoke];
|
|
270
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.js","sourceRoot":"","sources":["../../src/tools/roles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool, realmField } from "./shared.js";
|
|
3
|
+
const userIdField = z.string().min(1).describe("ID (UUID) of the user, e.g. from user_search.");
|
|
4
|
+
const userBruteForceStatus = defineTool({
|
|
5
|
+
name: "user_brute_force_status",
|
|
6
|
+
title: "Get brute-force status",
|
|
7
|
+
description: "Show the brute-force detection state of a user: whether the account is temporarily locked and recent failure counters.",
|
|
8
|
+
kind: "read",
|
|
9
|
+
input: z.object({ realm: realmField, userId: userIdField }),
|
|
10
|
+
output: z.object({
|
|
11
|
+
realm: z.string().describe("Realm that was queried."),
|
|
12
|
+
userId: z.string().describe("User ID that was queried."),
|
|
13
|
+
disabled: z
|
|
14
|
+
.boolean()
|
|
15
|
+
.describe("True if the user is currently locked by brute-force detection."),
|
|
16
|
+
numFailures: z.number().describe("Number of consecutive failed logins."),
|
|
17
|
+
lastFailure: z.number().optional().describe("Time of the last failure (epoch milliseconds)."),
|
|
18
|
+
lastIPFailure: z.string().optional().describe("IP address of the last failure."),
|
|
19
|
+
}),
|
|
20
|
+
async handler({ realm, userId }, { gateway }) {
|
|
21
|
+
const st = await gateway.run((kc) => kc.attackDetection.findOne({ realm, id: userId }));
|
|
22
|
+
const disabled = st?.disabled === true;
|
|
23
|
+
const numFailures = typeof st?.numFailures === "number" ? st.numFailures : 0;
|
|
24
|
+
return {
|
|
25
|
+
data: {
|
|
26
|
+
realm,
|
|
27
|
+
userId,
|
|
28
|
+
disabled,
|
|
29
|
+
numFailures,
|
|
30
|
+
...(typeof st?.lastFailure === "number" && { lastFailure: st.lastFailure }),
|
|
31
|
+
...(typeof st?.lastIPFailure === "string" && { lastIPFailure: st.lastIPFailure }),
|
|
32
|
+
},
|
|
33
|
+
summary: `User ${userId} in realm '${realm}': ${disabled ? "locked" : "not locked"}, ${numFailures} failed login(s).`,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
const userBruteForceUnlock = defineTool({
|
|
38
|
+
name: "user_brute_force_unlock",
|
|
39
|
+
title: "Unlock user (brute-force)",
|
|
40
|
+
description: "Clear the brute-force detection state of a user so a temporarily locked account can log in again. Does not change the user's enabled flag.",
|
|
41
|
+
kind: "write",
|
|
42
|
+
idempotent: true,
|
|
43
|
+
input: z.object({ realm: realmField, userId: userIdField }),
|
|
44
|
+
output: z.object({
|
|
45
|
+
unlocked: z.boolean().describe("True if the brute-force state was cleared."),
|
|
46
|
+
userId: z.string().describe("User that was unlocked."),
|
|
47
|
+
realm: z.string().describe("Realm of the user."),
|
|
48
|
+
}),
|
|
49
|
+
async handler({ realm, userId }, { gateway }) {
|
|
50
|
+
await gateway.run((kc) => kc.attackDetection.del({ realm, id: userId }));
|
|
51
|
+
return {
|
|
52
|
+
data: { unlocked: true, userId, realm },
|
|
53
|
+
summary: `Cleared brute-force state of user ${userId} in realm '${realm}'.`,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
export const securityTools = [userBruteForceStatus, userBruteForceUnlock];
|
|
58
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/tools/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ToolError } from "../keycloak/errors.js";
|
|
3
|
+
import { defineTool, realmField } from "./shared.js";
|
|
4
|
+
const userIdField = z.string().min(1).describe("ID (UUID) of the user, e.g. from user_search.");
|
|
5
|
+
const sessionItem = z.object({
|
|
6
|
+
id: z.string().describe("Session ID."),
|
|
7
|
+
ipAddress: z.string().optional().describe("IP address the session was started from."),
|
|
8
|
+
start: z.number().optional().describe("Session start (epoch milliseconds)."),
|
|
9
|
+
lastAccess: z.number().optional().describe("Last access (epoch milliseconds)."),
|
|
10
|
+
clients: z.array(z.string()).describe("Client IDs (clientId) taking part in the session."),
|
|
11
|
+
});
|
|
12
|
+
const userSessionsList = defineTool({
|
|
13
|
+
name: "user_sessions_list",
|
|
14
|
+
title: "List user sessions",
|
|
15
|
+
description: "List the active sessions of a user (IP address, start, last access, clients).",
|
|
16
|
+
kind: "read",
|
|
17
|
+
input: z.object({ realm: realmField, userId: userIdField }),
|
|
18
|
+
output: z.object({
|
|
19
|
+
realm: z.string().describe("Realm that was queried."),
|
|
20
|
+
userId: z.string().describe("User ID that was queried."),
|
|
21
|
+
sessions: z.array(sessionItem).describe("Active sessions of the user."),
|
|
22
|
+
}),
|
|
23
|
+
async handler({ realm, userId }, { gateway }) {
|
|
24
|
+
const rows = await gateway.run((kc) => kc.users.listSessions({ realm, id: userId }));
|
|
25
|
+
const sessions = rows.map((s) => ({
|
|
26
|
+
id: s.id ?? "",
|
|
27
|
+
...(s.ipAddress !== undefined && { ipAddress: s.ipAddress }),
|
|
28
|
+
...(s.start !== undefined && { start: s.start }),
|
|
29
|
+
...(s.lastAccess !== undefined && { lastAccess: s.lastAccess }),
|
|
30
|
+
clients: Object.values(s.clients ?? {}),
|
|
31
|
+
}));
|
|
32
|
+
return {
|
|
33
|
+
data: { realm, userId, sessions },
|
|
34
|
+
summary: `User ${userId} has ${sessions.length} active session(s) in realm '${realm}'.`,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const userLogout = defineTool({
|
|
39
|
+
name: "user_logout",
|
|
40
|
+
title: "Log out user",
|
|
41
|
+
description: "End all active sessions of a user (forced logout). Offline tokens are not affected. Requires confirm: true (ask the user first).",
|
|
42
|
+
kind: "destructive",
|
|
43
|
+
idempotent: true,
|
|
44
|
+
input: z.object({ realm: realmField, userId: userIdField }),
|
|
45
|
+
output: z.object({
|
|
46
|
+
loggedOut: z.boolean().describe("True if the logout request was accepted."),
|
|
47
|
+
userId: z.string().describe("User whose sessions were ended."),
|
|
48
|
+
realm: z.string().describe("Realm of the user."),
|
|
49
|
+
}),
|
|
50
|
+
async preview({ realm, userId }, { gateway }) {
|
|
51
|
+
const { user, sessions } = await gateway.run(async (kc) => ({
|
|
52
|
+
user: await kc.users.findOne({ realm, id: userId }),
|
|
53
|
+
sessions: await kc.users.listSessions({ realm, id: userId }),
|
|
54
|
+
}));
|
|
55
|
+
if (!user)
|
|
56
|
+
throw new ToolError("not_found", `User ${userId} does not exist in realm '${realm}'.`);
|
|
57
|
+
return `end all sessions (${sessions.length} active) of user '${user.username ?? userId}' (id ${userId}) in realm '${realm}'.`;
|
|
58
|
+
},
|
|
59
|
+
async handler({ realm, userId }, { gateway }) {
|
|
60
|
+
await gateway.run((kc) => kc.users.logout({ realm, id: userId }));
|
|
61
|
+
return {
|
|
62
|
+
data: { loggedOut: true, userId, realm },
|
|
63
|
+
summary: `Ended all sessions of user ${userId} in realm '${realm}'.`,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
export const sessionTools = [userSessionsList, userLogout];
|
|
68
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/tools/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertPathSegment, assertRealmAllowed, requireConfirmation } from "../guards.js";
|
|
3
|
+
import { mapKeycloakError } from "../keycloak/errors.js";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Shared schema building blocks
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export const DEFAULT_PAGE_SIZE = 50;
|
|
8
|
+
export const MAX_PAGE_SIZE = 200;
|
|
9
|
+
export const realmField = z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.describe("Name of the target realm (the realm to operate on, not the login realm).");
|
|
13
|
+
export const confirmField = z
|
|
14
|
+
.boolean()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Must be true to execute this destructive operation. Only set it after the user approved.");
|
|
17
|
+
export const paginationShape = {
|
|
18
|
+
first: z
|
|
19
|
+
.number()
|
|
20
|
+
.int()
|
|
21
|
+
.min(0)
|
|
22
|
+
.default(0)
|
|
23
|
+
.describe("Index of the first result to return (offset, default 0)."),
|
|
24
|
+
max: z
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(MAX_PAGE_SIZE)
|
|
29
|
+
.default(DEFAULT_PAGE_SIZE)
|
|
30
|
+
.describe(`Maximum number of results (default ${DEFAULT_PAGE_SIZE}, at most ${MAX_PAGE_SIZE}).`),
|
|
31
|
+
};
|
|
32
|
+
/** Output schema wrapper for list tools: { items, first, max, hasMore }. */
|
|
33
|
+
export function pageOf(item) {
|
|
34
|
+
return z.object({
|
|
35
|
+
items: z.array(item),
|
|
36
|
+
first: z.number().int().describe("Offset used for this page."),
|
|
37
|
+
max: z.number().int().describe("Page size used for this page."),
|
|
38
|
+
hasMore: z.boolean().describe("True if more results exist beyond this page."),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Turns a result list fetched with `max + 1` rows into a page.
|
|
43
|
+
* Query Keycloak with `max: max + 1` so hasMore can be detected without a count call.
|
|
44
|
+
*/
|
|
45
|
+
export function toPage(rows, first, max) {
|
|
46
|
+
return { items: rows.slice(0, max), first, max, hasMore: rows.length > max };
|
|
47
|
+
}
|
|
48
|
+
export function toResult(output) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: "text", text: output.summary }],
|
|
51
|
+
structuredContent: output.data,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function errorResult(message) {
|
|
55
|
+
return { isError: true, content: [{ type: "text", text: message }] };
|
|
56
|
+
}
|
|
57
|
+
export function toToolError(error) {
|
|
58
|
+
return mapKeycloakError(error);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Input fields that end up as URL path segments. Query-only fields such as the
|
|
62
|
+
* public `clientId` (which may be a URL for SAML clients) are not listed.
|
|
63
|
+
*/
|
|
64
|
+
const PATH_SEGMENT_FIELDS = new Set(["realm", "id", "userId", "groupId"]);
|
|
65
|
+
/**
|
|
66
|
+
* Builds a tool with all cross-cutting behaviour applied uniformly:
|
|
67
|
+
* annotations, realm guards (allowlist, master write protection), path segment
|
|
68
|
+
* validation, confirmation for destructive tools, error mapping to isError,
|
|
69
|
+
* and one audit log line per call (no arguments or PII logged).
|
|
70
|
+
*/
|
|
71
|
+
export function defineTool(spec) {
|
|
72
|
+
const destructive = spec.kind === "destructive";
|
|
73
|
+
if (destructive && !spec.preview) {
|
|
74
|
+
throw new Error(`Tool ${spec.name}: destructive tools must define preview()`);
|
|
75
|
+
}
|
|
76
|
+
if (destructive && "confirm" in spec.input.shape) {
|
|
77
|
+
throw new Error(`Tool ${spec.name}: 'confirm' is added automatically for destructive tools`);
|
|
78
|
+
}
|
|
79
|
+
const inputSchema = destructive ? spec.input.extend({ confirm: confirmField }) : spec.input;
|
|
80
|
+
const pathKeys = Object.keys(spec.input.shape).filter((key) => PATH_SEGMENT_FIELDS.has(key));
|
|
81
|
+
const write = spec.kind !== "read";
|
|
82
|
+
return {
|
|
83
|
+
name: spec.name,
|
|
84
|
+
kind: spec.kind,
|
|
85
|
+
register(server, ctx) {
|
|
86
|
+
server.registerTool(spec.name, {
|
|
87
|
+
title: spec.title,
|
|
88
|
+
description: spec.description,
|
|
89
|
+
inputSchema,
|
|
90
|
+
outputSchema: spec.output,
|
|
91
|
+
annotations: {
|
|
92
|
+
title: spec.title,
|
|
93
|
+
readOnlyHint: spec.kind === "read",
|
|
94
|
+
destructiveHint: destructive,
|
|
95
|
+
idempotentHint: spec.idempotent ?? spec.kind !== "write",
|
|
96
|
+
openWorldHint: false,
|
|
97
|
+
},
|
|
98
|
+
}, async (rawArgs) => {
|
|
99
|
+
const started = Date.now();
|
|
100
|
+
const args = rawArgs;
|
|
101
|
+
const realm = typeof args.realm === "string" ? args.realm : undefined;
|
|
102
|
+
let outcome = "ok";
|
|
103
|
+
try {
|
|
104
|
+
for (const key of pathKeys) {
|
|
105
|
+
const value = args[key];
|
|
106
|
+
if (typeof value === "string")
|
|
107
|
+
assertPathSegment(value, key);
|
|
108
|
+
}
|
|
109
|
+
if (realm !== undefined)
|
|
110
|
+
assertRealmAllowed(ctx.config, realm, { write });
|
|
111
|
+
if (destructive && ctx.config.requireConfirmation && args.confirm !== true) {
|
|
112
|
+
const preview = await spec.preview(args, ctx);
|
|
113
|
+
requireConfirmation(ctx.config, args.confirm === true, preview);
|
|
114
|
+
}
|
|
115
|
+
return toResult(await spec.handler(args, ctx));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const toolError = mapKeycloakError(error, {
|
|
119
|
+
baseUrl: ctx.config.url,
|
|
120
|
+
authRealm: ctx.config.authRealm,
|
|
121
|
+
timeoutMs: ctx.config.timeoutMs,
|
|
122
|
+
});
|
|
123
|
+
outcome = toolError.kind;
|
|
124
|
+
if (toolError.kind === "unexpected") {
|
|
125
|
+
ctx.logger.error("Unexpected tool failure", {
|
|
126
|
+
tool: spec.name,
|
|
127
|
+
error: error instanceof Error ? error.message : String(error),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return errorResult(toolError.message);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
ctx.logger.info("tool_call", {
|
|
134
|
+
tool: spec.name,
|
|
135
|
+
realm,
|
|
136
|
+
outcome,
|
|
137
|
+
durationMs: Date.now() - started,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"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"}
|