keycloak-api-manager 3.2.1 → 4.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/.env.example +27 -0
- package/Handlers/clientsHandler.js +240 -30
- package/Handlers/groupsHandler.js +16 -1
- package/Handlers/httpApiHelper.js +87 -0
- package/Handlers/realmsHandler.js +26 -4
- package/Handlers/usersHandler.js +43 -10
- package/README.md +341 -10
- package/index.js +159 -29
- package/package.json +3 -14
- package/test/.mocharc.json +4 -0
- package/test/TESTING.md +327 -0
- package/test/config/CONFIGURATION.md +170 -0
- package/test/config/default.json +36 -0
- package/test/config/local.json.example +7 -0
- package/test/config/secrets.json.example +7 -0
- package/test/diagnostic-protocol-mappers.js +189 -0
- package/test/docker-keycloak/DEPLOYMENT_GUIDE.md +262 -0
- package/test/docker-keycloak/certs/.gitkeep +7 -0
- package/test/docker-keycloak/docker-compose-https.yml +50 -0
- package/test/docker-keycloak/docker-compose.yml +59 -0
- package/test/docker-keycloak/setup-keycloak.js +501 -0
- package/test/enableServerFeatures.js +315 -0
- package/test/helpers/config.js +218 -0
- package/test/helpers/docker-helpers.js +513 -0
- package/test/helpers/setup.js +186 -0
- package/test/package.json +18 -0
- package/test/setup.js +194 -0
- package/test/specs/authenticationManagement.test.js +224 -0
- package/test/specs/clientCredentials.test.js +76 -0
- package/test/specs/clientScopes.test.js +388 -0
- package/test/specs/clients.test.js +791 -0
- package/test/specs/components.test.js +151 -0
- package/test/specs/debugClientLibrary.test.js +88 -0
- package/test/specs/groups.test.js +362 -0
- package/test/specs/identityProviders.test.js +292 -0
- package/test/specs/realms.test.js +390 -0
- package/test/specs/roles.test.js +322 -0
- package/test/specs/users.test.js +445 -0
- package/test/testConfig.js +69 -0
- package/.mocharc.json +0 -7
- package/docker-compose.yml +0 -27
- package/test/authenticationManagement.test.js +0 -329
- package/test/clientScopes.test.js +0 -256
- package/test/clients.test.js +0 -284
- package/test/components.test.js +0 -122
- package/test/config.js +0 -137
- package/test/docker-helpers.js +0 -111
- package/test/groups.test.js +0 -284
- package/test/identityProviders.test.js +0 -197
- package/test/mocha.env.js +0 -55
- package/test/realms.test.js +0 -349
- package/test/roles.test.js +0 -215
- package/test/users.test.js +0 -405
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const { expect } = require('chai');
|
|
5
|
+
|
|
6
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'test';
|
|
7
|
+
process.env.PROPERTIES_PATH = path.join(__dirname, '..', 'config');
|
|
8
|
+
|
|
9
|
+
const { conf } = require('propertiesmanager');
|
|
10
|
+
const keycloakManager = require('keycloak-api-manager');
|
|
11
|
+
const { TEST_REALM, generateUniqueName } = require('../testConfig');
|
|
12
|
+
|
|
13
|
+
function requestAdmin(baseUrl, token, apiPath, method = 'GET', body) {
|
|
14
|
+
const url = new URL(apiPath, baseUrl);
|
|
15
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
16
|
+
const payload = body ? JSON.stringify(body) : null;
|
|
17
|
+
|
|
18
|
+
const options = {
|
|
19
|
+
method,
|
|
20
|
+
headers: {
|
|
21
|
+
Accept: 'application/json',
|
|
22
|
+
Authorization: `Bearer ${token}`,
|
|
23
|
+
...(payload ? { 'Content-Type': 'application/json' } : {}),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const req = transport.request(url, options, (res) => {
|
|
29
|
+
let data = '';
|
|
30
|
+
res.on('data', (chunk) => {
|
|
31
|
+
data += chunk;
|
|
32
|
+
});
|
|
33
|
+
res.on('end', () => {
|
|
34
|
+
let parsed = data;
|
|
35
|
+
try {
|
|
36
|
+
parsed = data ? JSON.parse(data) : null;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
parsed = data;
|
|
39
|
+
}
|
|
40
|
+
resolve({ status: res.statusCode, body: parsed });
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
req.on('error', reject);
|
|
45
|
+
if (payload) {
|
|
46
|
+
req.write(payload);
|
|
47
|
+
}
|
|
48
|
+
req.end();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function getAdminToken(baseUrl, username, password) {
|
|
53
|
+
const url = new URL('/realms/master/protocol/openid-connect/token', baseUrl);
|
|
54
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
55
|
+
|
|
56
|
+
const params = new URLSearchParams({
|
|
57
|
+
grant_type: 'password',
|
|
58
|
+
client_id: 'admin-cli',
|
|
59
|
+
username,
|
|
60
|
+
password,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const options = {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const req = transport.request(url, options, (res) => {
|
|
72
|
+
let data = '';
|
|
73
|
+
res.on('data', (chunk) => {
|
|
74
|
+
data += chunk;
|
|
75
|
+
});
|
|
76
|
+
res.on('end', () => {
|
|
77
|
+
if (res.statusCode === 200) {
|
|
78
|
+
const parsed = JSON.parse(data);
|
|
79
|
+
resolve(parsed.access_token);
|
|
80
|
+
} else {
|
|
81
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
req.on('error', reject);
|
|
87
|
+
req.write(params.toString());
|
|
88
|
+
req.end();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function shouldSkipFeature(err) {
|
|
93
|
+
if (!err || !err.message) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const text = err.message.toLowerCase();
|
|
97
|
+
return (
|
|
98
|
+
text.includes('feature not enabled') ||
|
|
99
|
+
text.includes('not enabled') ||
|
|
100
|
+
text.includes('not supported') ||
|
|
101
|
+
text.includes('http 404') ||
|
|
102
|
+
text.includes('unknown_error')
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
describe('Users Handler', function () {
|
|
107
|
+
this.timeout(60000);
|
|
108
|
+
|
|
109
|
+
const keycloakConfig = (conf && conf.keycloak) || {};
|
|
110
|
+
const testUserName = generateUniqueName('users-test');
|
|
111
|
+
const testEmail = `${testUserName}@example.com`;
|
|
112
|
+
const testPassword = 'UsersTest!123';
|
|
113
|
+
const testGroupName = generateUniqueName('users-group');
|
|
114
|
+
const testRealmRoleName = generateUniqueName('users-realm-role');
|
|
115
|
+
const testClientId = generateUniqueName('users-client');
|
|
116
|
+
const testClientRoleName = generateUniqueName('users-client-role');
|
|
117
|
+
|
|
118
|
+
let adminToken = null;
|
|
119
|
+
let userId = null;
|
|
120
|
+
let groupId = null;
|
|
121
|
+
let realmRole = null;
|
|
122
|
+
let client = null;
|
|
123
|
+
let clientRole = null;
|
|
124
|
+
|
|
125
|
+
before(async function () {
|
|
126
|
+
await keycloakManager.configure({
|
|
127
|
+
baseUrl: keycloakConfig.baseUrl,
|
|
128
|
+
realmName: keycloakConfig.realmName,
|
|
129
|
+
clientId: keycloakConfig.clientId,
|
|
130
|
+
clientSecret: keycloakConfig.clientSecret,
|
|
131
|
+
username: keycloakConfig.username,
|
|
132
|
+
password: keycloakConfig.password,
|
|
133
|
+
grantType: keycloakConfig.grantType,
|
|
134
|
+
tokenLifeSpan: keycloakConfig.tokenLifeSpan,
|
|
135
|
+
scope: keycloakConfig.scope,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
adminToken = await getAdminToken(
|
|
139
|
+
keycloakConfig.baseUrl,
|
|
140
|
+
keycloakConfig.username,
|
|
141
|
+
keycloakConfig.password
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Use shared test realm (created by enableServerFeatures)
|
|
145
|
+
keycloakManager.setConfig({ realmName: TEST_REALM });
|
|
146
|
+
|
|
147
|
+
const createdUser = await keycloakManager.users.create({
|
|
148
|
+
username: testUserName,
|
|
149
|
+
email: testEmail,
|
|
150
|
+
enabled: true,
|
|
151
|
+
firstName: 'Users',
|
|
152
|
+
lastName: 'Test',
|
|
153
|
+
});
|
|
154
|
+
userId = createdUser.id;
|
|
155
|
+
|
|
156
|
+
await keycloakManager.users.resetPassword({
|
|
157
|
+
id: userId,
|
|
158
|
+
credential: {
|
|
159
|
+
type: 'password',
|
|
160
|
+
value: testPassword,
|
|
161
|
+
temporary: false,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const createdGroup = await keycloakManager.groups.create({
|
|
166
|
+
name: testGroupName,
|
|
167
|
+
});
|
|
168
|
+
groupId = createdGroup.id;
|
|
169
|
+
|
|
170
|
+
await keycloakManager.roles.create({ name: testRealmRoleName });
|
|
171
|
+
realmRole = await keycloakManager.roles.findOneByName({ name: testRealmRoleName });
|
|
172
|
+
|
|
173
|
+
await keycloakManager.clients.create({
|
|
174
|
+
clientId: testClientId,
|
|
175
|
+
name: testClientId,
|
|
176
|
+
enabled: true,
|
|
177
|
+
publicClient: false,
|
|
178
|
+
protocol: 'openid-connect',
|
|
179
|
+
directAccessGrantsEnabled: true,
|
|
180
|
+
standardFlowEnabled: true,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const clients = await keycloakManager.clients.find({ clientId: testClientId });
|
|
184
|
+
client = clients[0];
|
|
185
|
+
|
|
186
|
+
await keycloakManager.clients.createRole({
|
|
187
|
+
id: client.id,
|
|
188
|
+
name: testClientRoleName,
|
|
189
|
+
description: 'Users test client role',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
clientRole = await keycloakManager.clients.findRole({
|
|
193
|
+
id: client.id,
|
|
194
|
+
roleName: testClientRoleName,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
after(async function () {
|
|
199
|
+
try {
|
|
200
|
+
keycloakManager.setConfig({ realmName: TEST_REALM });
|
|
201
|
+
} catch (err) {
|
|
202
|
+
// Best-effort cleanup.
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
if (userId) {
|
|
207
|
+
await keycloakManager.users.del({ id: userId });
|
|
208
|
+
}
|
|
209
|
+
} catch (err) {
|
|
210
|
+
// Best-effort cleanup.
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
if (client) {
|
|
215
|
+
await keycloakManager.clients.del({ id: client.id });
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
// Best-effort cleanup.
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
if (realmRole) {
|
|
223
|
+
await keycloakManager.roles.delByName({ name: realmRole.name });
|
|
224
|
+
}
|
|
225
|
+
} catch (err) {
|
|
226
|
+
// Best-effort cleanup.
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
if (groupId) {
|
|
231
|
+
await keycloakManager.groups.del({ id: groupId });
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
// Best-effort cleanup.
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Don't delete shared test realm
|
|
238
|
+
|
|
239
|
+
if (typeof keycloakManager.stop === 'function') {
|
|
240
|
+
keycloakManager.stop();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should find, findOne, count and update users', async function () {
|
|
245
|
+
const users = await keycloakManager.users.find({ username: testUserName });
|
|
246
|
+
expect(users).to.be.an('array');
|
|
247
|
+
expect(users.some((item) => item.id === userId)).to.equal(true);
|
|
248
|
+
|
|
249
|
+
const one = await keycloakManager.users.findOne({ id: userId });
|
|
250
|
+
expect(one).to.be.an('object');
|
|
251
|
+
expect(one.username).to.equal(testUserName);
|
|
252
|
+
|
|
253
|
+
const count = await keycloakManager.users.count({ username: testUserName });
|
|
254
|
+
expect(count).to.be.a('number');
|
|
255
|
+
expect(count).to.be.greaterThan(0);
|
|
256
|
+
|
|
257
|
+
const newFirstName = `UsersUpdated-${Date.now()}`;
|
|
258
|
+
await keycloakManager.users.update(
|
|
259
|
+
{ id: userId },
|
|
260
|
+
{ firstName: newFirstName }
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const updated = await keycloakManager.users.findOne({ id: userId });
|
|
264
|
+
expect(updated.firstName).to.equal(newFirstName);
|
|
265
|
+
|
|
266
|
+
const direct = await requestAdmin(
|
|
267
|
+
keycloakConfig.baseUrl,
|
|
268
|
+
adminToken,
|
|
269
|
+
`/admin/realms/${TEST_REALM}/users/${userId}`
|
|
270
|
+
);
|
|
271
|
+
expect(direct.status).to.equal(200);
|
|
272
|
+
expect(direct.body.firstName).to.equal(newFirstName);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should manage password and credentials', async function () {
|
|
276
|
+
const credentials = await keycloakManager.users.getCredentials({ id: userId });
|
|
277
|
+
expect(credentials).to.be.an('array');
|
|
278
|
+
|
|
279
|
+
const storageTypes = await keycloakManager.users.getUserStorageCredentialTypes({ id: userId });
|
|
280
|
+
expect(storageTypes).to.be.an('array');
|
|
281
|
+
|
|
282
|
+
if (!credentials.length) {
|
|
283
|
+
this.skip();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const credential = credentials[0];
|
|
288
|
+
await keycloakManager.users.updateCredentialLabel(
|
|
289
|
+
{ id: userId, credentialId: credential.id },
|
|
290
|
+
`label-${Date.now()}`
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should manage group membership', async function () {
|
|
295
|
+
await keycloakManager.users.addToGroup({ id: userId, groupId });
|
|
296
|
+
|
|
297
|
+
const groups = await keycloakManager.users.listGroups({ id: userId });
|
|
298
|
+
expect(groups).to.be.an('array');
|
|
299
|
+
expect(groups.some((group) => group.id === groupId)).to.equal(true);
|
|
300
|
+
|
|
301
|
+
const count = await keycloakManager.users.countGroups({ id: userId });
|
|
302
|
+
expect(count).to.be.a('number');
|
|
303
|
+
expect(count).to.be.greaterThan(0);
|
|
304
|
+
|
|
305
|
+
const direct = await requestAdmin(
|
|
306
|
+
keycloakConfig.baseUrl,
|
|
307
|
+
adminToken,
|
|
308
|
+
`/admin/realms/${TEST_REALM}/users/${userId}/groups`
|
|
309
|
+
);
|
|
310
|
+
expect(direct.status).to.equal(200);
|
|
311
|
+
expect(Array.isArray(direct.body)).to.equal(true);
|
|
312
|
+
|
|
313
|
+
await keycloakManager.users.delFromGroup({ id: userId, groupId });
|
|
314
|
+
const groupsAfter = await keycloakManager.users.listGroups({ id: userId });
|
|
315
|
+
expect(groupsAfter.some((group) => group.id === groupId)).to.equal(false);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should manage realm role mappings', async function () {
|
|
319
|
+
await keycloakManager.users.addRealmRoleMappings({
|
|
320
|
+
id: userId,
|
|
321
|
+
roles: [{ id: realmRole.id, name: realmRole.name }],
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const directRealmRoles = await keycloakManager.users.listRealmRoleMappings({ id: userId });
|
|
325
|
+
expect(directRealmRoles.some((role) => role.id === realmRole.id)).to.equal(true);
|
|
326
|
+
|
|
327
|
+
const compositeRealmRoles = await keycloakManager.users.listCompositeRealmRoleMappings({ id: userId });
|
|
328
|
+
expect(compositeRealmRoles).to.be.an('array');
|
|
329
|
+
|
|
330
|
+
const roleMappings = await keycloakManager.users.listRoleMappings({ id: userId });
|
|
331
|
+
expect(roleMappings).to.be.an('object');
|
|
332
|
+
|
|
333
|
+
const available = await keycloakManager.users.listAvailableRealmRoleMappings({ id: userId });
|
|
334
|
+
expect(available).to.be.an('array');
|
|
335
|
+
|
|
336
|
+
await keycloakManager.users.delRealmRoleMappings({
|
|
337
|
+
id: userId,
|
|
338
|
+
roles: [{ id: realmRole.id, name: realmRole.name }],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const afterDelete = await keycloakManager.users.listRealmRoleMappings({ id: userId });
|
|
342
|
+
expect(afterDelete.some((role) => role.id === realmRole.id)).to.equal(false);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should manage client role mappings', async function () {
|
|
346
|
+
await keycloakManager.users.addClientRoleMappings({
|
|
347
|
+
id: userId,
|
|
348
|
+
clientUniqueId: client.id,
|
|
349
|
+
roles: [{ id: clientRole.id, name: clientRole.name }],
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const roles = await keycloakManager.users.listClientRoleMappings({
|
|
353
|
+
id: userId,
|
|
354
|
+
clientUniqueId: client.id,
|
|
355
|
+
});
|
|
356
|
+
expect(roles).to.be.an('array');
|
|
357
|
+
expect(roles.some((role) => role.id === clientRole.id)).to.equal(true);
|
|
358
|
+
|
|
359
|
+
const composite = await keycloakManager.users.listCompositeClientRoleMappings({
|
|
360
|
+
id: userId,
|
|
361
|
+
clientUniqueId: client.id,
|
|
362
|
+
});
|
|
363
|
+
expect(composite).to.be.an('array');
|
|
364
|
+
|
|
365
|
+
const available = await keycloakManager.users.listAvailableClientRoleMappings({
|
|
366
|
+
id: userId,
|
|
367
|
+
clientUniqueId: client.id,
|
|
368
|
+
});
|
|
369
|
+
expect(available).to.be.an('array');
|
|
370
|
+
|
|
371
|
+
await keycloakManager.users.delClientRoleMappings({
|
|
372
|
+
id: userId,
|
|
373
|
+
clientUniqueId: client.id,
|
|
374
|
+
roles: [{ id: clientRole.id, name: clientRole.name }],
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const rolesAfter = await keycloakManager.users.listClientRoleMappings({
|
|
378
|
+
id: userId,
|
|
379
|
+
clientUniqueId: client.id,
|
|
380
|
+
});
|
|
381
|
+
expect(rolesAfter.some((role) => role.id === clientRole.id)).to.equal(false);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should list sessions and support logout', async function () {
|
|
385
|
+
const sessions = await keycloakManager.users.listSessions({ id: userId });
|
|
386
|
+
expect(sessions).to.be.an('array');
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const offlineSessions = await keycloakManager.users.listOfflineSessions({
|
|
390
|
+
id: userId,
|
|
391
|
+
clientId: testClientId,
|
|
392
|
+
});
|
|
393
|
+
expect(offlineSessions).to.be.an('array');
|
|
394
|
+
} catch (err) {
|
|
395
|
+
if (shouldSkipFeature(err)) {
|
|
396
|
+
this.skip();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
throw err;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
await keycloakManager.users.logout({ id: userId });
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should support impersonation when enabled', async function () {
|
|
406
|
+
try {
|
|
407
|
+
const response = await keycloakManager.users.impersonation({ id: userId });
|
|
408
|
+
expect(response).to.be.an('object');
|
|
409
|
+
} catch (err) {
|
|
410
|
+
if (shouldSkipFeature(err)) {
|
|
411
|
+
this.skip();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should list federated identities and handle optional provider linkage', async function () {
|
|
419
|
+
const identities = await keycloakManager.users.listFederatedIdentities({ id: userId });
|
|
420
|
+
expect(identities).to.be.an('array');
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
await keycloakManager.users.addToFederatedIdentity({
|
|
424
|
+
id: userId,
|
|
425
|
+
federatedIdentityId: 'google',
|
|
426
|
+
federatedIdentity: {
|
|
427
|
+
identityProvider: 'google',
|
|
428
|
+
userId: `federated-${Date.now()}`,
|
|
429
|
+
userName: `federated-${Date.now()}`,
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await keycloakManager.users.delFromFederatedIdentity({
|
|
434
|
+
id: userId,
|
|
435
|
+
federatedIdentityId: 'google',
|
|
436
|
+
});
|
|
437
|
+
} catch (err) {
|
|
438
|
+
if (shouldSkipFeature(err)) {
|
|
439
|
+
this.skip();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
// Set up environment for propertiesmanager
|
|
4
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'test';
|
|
5
|
+
process.env.PROPERTIES_PATH = path.join(__dirname, 'config');
|
|
6
|
+
|
|
7
|
+
const { conf } = require('propertiesmanager');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shared Test Configuration Module
|
|
11
|
+
*
|
|
12
|
+
* This module centralizes all test configuration using propertiesmanager.
|
|
13
|
+
* It loads configuration from test/config/ directory with the following merge order:
|
|
14
|
+
*
|
|
15
|
+
* 1. default.json - Base configuration for all environments
|
|
16
|
+
* 2. secrets.json - Sensitive credentials (gitignored)
|
|
17
|
+
* 3. local.json - Developer-specific overrides (gitignored, optional)
|
|
18
|
+
*
|
|
19
|
+
* Configuration Structure:
|
|
20
|
+
* - All files use environment wrappers: { "test": { ... } }
|
|
21
|
+
* - NODE_ENV determines which section to load (default: "test")
|
|
22
|
+
* - propertiesmanager automatically merges configs based on NODE_ENV
|
|
23
|
+
*
|
|
24
|
+
* Exports:
|
|
25
|
+
* - KEYCLOAK_CONFIG: Admin connection settings (baseUrl, username, password, etc.)
|
|
26
|
+
* - TEST_REALM: Name of the shared test realm
|
|
27
|
+
* - TEST_CLIENT_*: Client configuration for tests
|
|
28
|
+
* - TEST_USER_*: Test user credentials and details
|
|
29
|
+
* - TEST_ROLES: Array of role names to create
|
|
30
|
+
* - TEST_GROUP_NAME: Name of test group
|
|
31
|
+
* - TEST_CLIENT_SCOPE: Name of test client scope
|
|
32
|
+
* - generateUniqueName: Helper to create unique resource names
|
|
33
|
+
*
|
|
34
|
+
* Usage in tests:
|
|
35
|
+
* const { KEYCLOAK_CONFIG, TEST_REALM, TEST_USER_USERNAME } = require('./testConfig');
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const realmConfig = conf?.realm || {};
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
// Keycloak Admin Config (for admin connections)
|
|
42
|
+
KEYCLOAK_CONFIG: conf?.keycloak || {},
|
|
43
|
+
|
|
44
|
+
// Test Realm Configuration
|
|
45
|
+
TEST_REALM: realmConfig.name || 'keycloak-api-manager-test-realm',
|
|
46
|
+
|
|
47
|
+
// Test Client Configuration
|
|
48
|
+
TEST_CLIENT_ID: realmConfig.client?.clientId || 'test-client',
|
|
49
|
+
TEST_CLIENT_SECRET: realmConfig.client?.clientSecret || 'test-client-secret',
|
|
50
|
+
|
|
51
|
+
// Test User Configuration
|
|
52
|
+
TEST_USER_USERNAME: realmConfig.user?.username || 'test-user',
|
|
53
|
+
TEST_USER_PASSWORD: realmConfig.user?.password || 'test-password',
|
|
54
|
+
TEST_USER_EMAIL: realmConfig.user?.email || 'test-user@test.local',
|
|
55
|
+
TEST_USER_FIRSTNAME: realmConfig.user?.firstName || 'Test',
|
|
56
|
+
TEST_USER_LASTNAME: realmConfig.user?.lastName || 'User',
|
|
57
|
+
|
|
58
|
+
// Test Roles
|
|
59
|
+
TEST_ROLES: realmConfig.roles || ['test-role-1', 'test-role-2', 'test-admin-role'],
|
|
60
|
+
|
|
61
|
+
// Test Group
|
|
62
|
+
TEST_GROUP_NAME: realmConfig.group?.name || 'test-group',
|
|
63
|
+
|
|
64
|
+
// Test Client Scope
|
|
65
|
+
TEST_CLIENT_SCOPE: realmConfig.clientScope?.name || 'test-scope',
|
|
66
|
+
|
|
67
|
+
// Helper to generate unique names when needed
|
|
68
|
+
generateUniqueName: (prefix) => `${prefix}-${Date.now()}`
|
|
69
|
+
};
|
package/.mocharc.json
DELETED
package/docker-compose.yml
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
version: '3.8'
|
|
2
|
-
|
|
3
|
-
services:
|
|
4
|
-
keycloak:
|
|
5
|
-
image: keycloak/keycloak:latest
|
|
6
|
-
container_name: keycloak-test
|
|
7
|
-
ports:
|
|
8
|
-
- "8080:8080"
|
|
9
|
-
environment:
|
|
10
|
-
KEYCLOAK_ADMIN: admin
|
|
11
|
-
KEYCLOAK_ADMIN_PASSWORD: admin
|
|
12
|
-
KC_DB: dev-mem
|
|
13
|
-
KC_METRICS_ENABLED: 'false'
|
|
14
|
-
KC_HEALTH_ENABLED: 'true'
|
|
15
|
-
command:
|
|
16
|
-
- start-dev
|
|
17
|
-
healthcheck:
|
|
18
|
-
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
|
|
19
|
-
interval: 5s
|
|
20
|
-
timeout: 5s
|
|
21
|
-
retries: 12
|
|
22
|
-
networks:
|
|
23
|
-
- keycloak-network
|
|
24
|
-
|
|
25
|
-
networks:
|
|
26
|
-
keycloak-network:
|
|
27
|
-
driver: bridge
|