keycloak-api-manager 3.2.0 → 4.0.0
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/authenticationManagementHandler.js +1 -1
- package/Handlers/clientScopesHandler.js +5 -5
- package/Handlers/clientsHandler.js +241 -31
- package/Handlers/componentsHandler.js +2 -2
- package/Handlers/groupsHandler.js +18 -3
- package/Handlers/httpApiHelper.js +87 -0
- package/Handlers/identityProvidersHandler.js +3 -3
- package/Handlers/realmsHandler.js +26 -4
- package/Handlers/usersHandler.js +43 -10
- package/README.md +361 -26
- package/index.js +149 -29
- package/index.mjs +21 -0
- package/package.json +3 -2
- 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/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/.idea/vcs.xml +0 -6
- package/.idea/workspace.xml +0 -94
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enable Server Features - Shared Test Infrastructure Setup
|
|
3
|
+
*
|
|
4
|
+
* This script configures a shared Keycloak test realm with all necessary infrastructure
|
|
5
|
+
* before any tests run. It's called from setup.js in the global before() hook.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Uses configuration from testConfig.js (loaded via propertiesmanager)
|
|
9
|
+
* - Creates infrastructure only if it doesn't already exist (idempotent)
|
|
10
|
+
* - All tests share this realm, avoiding repeated creation/deletion overhead
|
|
11
|
+
*
|
|
12
|
+
* Infrastructure Created:
|
|
13
|
+
* 1. Test Realm - Isolated environment for all tests
|
|
14
|
+
* 2. Test Client - Service account client for authentication tests
|
|
15
|
+
* 3. Test User - Standard user with credentials for user management tests
|
|
16
|
+
* 4. Test Roles - Realm roles for RBAC tests
|
|
17
|
+
* 5. Test Group - Group for group management tests
|
|
18
|
+
* 6. Fine-grained Permissions - Admin permissions if supported by server
|
|
19
|
+
* 7. Client Scope - Scope for protocol mapper and scope mapping tests
|
|
20
|
+
*
|
|
21
|
+
* Known Server-Side Limitations (Cannot be enabled via API):
|
|
22
|
+
* - Installation Providers: Requires server configuration, not API-configurable
|
|
23
|
+
* - Protocol Mappers: Requires protocol mapper providers to be installed/enabled at server level
|
|
24
|
+
* - Authorization Services: Some features require explicit server configuration
|
|
25
|
+
* - Consents Feature: Requires server-side configuration
|
|
26
|
+
*
|
|
27
|
+
* Tests that require these features will be marked as skipped with appropriate messages.
|
|
28
|
+
* These are not bugs - they are legitimate environmental constraints.
|
|
29
|
+
*
|
|
30
|
+
* Performance Impact:
|
|
31
|
+
* - Setup runs once: ~10-20 seconds
|
|
32
|
+
* - Per-test overhead saved: ~2-5 seconds per test
|
|
33
|
+
* - Total time saved: ~5-10 minutes for full suite (59 tests)
|
|
34
|
+
*
|
|
35
|
+
* Configuration Source:
|
|
36
|
+
* - test/config/default.json - Base config
|
|
37
|
+
* - test/config/secrets.json - Passwords
|
|
38
|
+
* - test/config/local.json - Optional developer overrides
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const path = require('path');
|
|
42
|
+
|
|
43
|
+
// Ensure NODE_ENV and config path are set before requiring testConfig
|
|
44
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'test';
|
|
45
|
+
process.env.PROPERTIES_PATH = path.join(__dirname, 'config');
|
|
46
|
+
|
|
47
|
+
const keycloakManager = require('../index');
|
|
48
|
+
const {
|
|
49
|
+
TEST_REALM,
|
|
50
|
+
TEST_CLIENT_ID,
|
|
51
|
+
TEST_USER_USERNAME,
|
|
52
|
+
TEST_USER_PASSWORD,
|
|
53
|
+
TEST_USER_EMAIL,
|
|
54
|
+
TEST_USER_FIRSTNAME,
|
|
55
|
+
TEST_USER_LASTNAME,
|
|
56
|
+
TEST_ROLES,
|
|
57
|
+
TEST_GROUP_NAME,
|
|
58
|
+
TEST_CLIENT_SCOPE,
|
|
59
|
+
KEYCLOAK_CONFIG
|
|
60
|
+
} = require('./testConfig');
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Main setup function - creates shared test infrastructure
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
async function enableServerFeatures() {
|
|
67
|
+
const keycloakConfig = KEYCLOAK_CONFIG;
|
|
68
|
+
|
|
69
|
+
console.log('Configuring Keycloak Admin Client...');
|
|
70
|
+
await keycloakManager.configure({
|
|
71
|
+
baseUrl: keycloakConfig.baseUrl,
|
|
72
|
+
realmName: keycloakConfig.realmName,
|
|
73
|
+
clientId: keycloakConfig.clientId,
|
|
74
|
+
username: keycloakConfig.username,
|
|
75
|
+
password: keycloakConfig.password,
|
|
76
|
+
grantType: keycloakConfig.grantType
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Step 1: Create or verify test realm
|
|
81
|
+
console.log(`\n1. Setting up test realm: ${TEST_REALM}`);
|
|
82
|
+
const existingRealms = await keycloakManager.realms.find();
|
|
83
|
+
const realmExists = existingRealms.some(r => r.realm === TEST_REALM);
|
|
84
|
+
|
|
85
|
+
if (!realmExists) {
|
|
86
|
+
await keycloakManager.realms.create({
|
|
87
|
+
realm: TEST_REALM,
|
|
88
|
+
enabled: true,
|
|
89
|
+
displayName: 'Keycloak API Manager Test Realm',
|
|
90
|
+
loginTheme: 'keycloak',
|
|
91
|
+
accountTheme: 'keycloak',
|
|
92
|
+
adminTheme: 'keycloak',
|
|
93
|
+
emailTheme: 'keycloak',
|
|
94
|
+
accessTokenLifespan: 3600,
|
|
95
|
+
ssoSessionIdleTimeout: 1800,
|
|
96
|
+
ssoSessionMaxLifespan: 36000,
|
|
97
|
+
offlineSessionIdleTimeout: 2592000,
|
|
98
|
+
registrationAllowed: false,
|
|
99
|
+
registrationEmailAsUsername: false,
|
|
100
|
+
rememberMe: true,
|
|
101
|
+
verifyEmail: false,
|
|
102
|
+
loginWithEmailAllowed: true,
|
|
103
|
+
duplicateEmailsAllowed: false,
|
|
104
|
+
resetPasswordAllowed: true,
|
|
105
|
+
editUsernameAllowed: false,
|
|
106
|
+
bruteForceProtected: false
|
|
107
|
+
});
|
|
108
|
+
console.log(` ✓ Test realm created: ${TEST_REALM}`);
|
|
109
|
+
} else {
|
|
110
|
+
console.log(` ✓ Test realm already exists: ${TEST_REALM}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Switch to test realm for all subsequent operations
|
|
114
|
+
keycloakManager.setConfig({ realmName: TEST_REALM });
|
|
115
|
+
|
|
116
|
+
// 2. Create test client with all features enabled
|
|
117
|
+
console.log('\n2. Setting up test client...');
|
|
118
|
+
const clients = await keycloakManager.clients.find({ clientId: TEST_CLIENT_ID });
|
|
119
|
+
let testClient = clients.find(c => c.clientId === TEST_CLIENT_ID);
|
|
120
|
+
|
|
121
|
+
if (!testClient) {
|
|
122
|
+
const created = await keycloakManager.clients.create({
|
|
123
|
+
clientId: TEST_CLIENT_ID,
|
|
124
|
+
enabled: true,
|
|
125
|
+
protocol: 'openid-connect',
|
|
126
|
+
publicClient: false,
|
|
127
|
+
directAccessGrantsEnabled: true,
|
|
128
|
+
serviceAccountsEnabled: true,
|
|
129
|
+
authorizationServicesEnabled: true,
|
|
130
|
+
standardFlowEnabled: true,
|
|
131
|
+
implicitFlowEnabled: false,
|
|
132
|
+
bearerOnly: false,
|
|
133
|
+
consentRequired: false,
|
|
134
|
+
fullScopeAllowed: true,
|
|
135
|
+
redirectUris: ['http://localhost:*'],
|
|
136
|
+
webOrigins: ['*'],
|
|
137
|
+
attributes: {
|
|
138
|
+
'client.secret.creation.time': Date.now().toString()
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
const allClients = await keycloakManager.clients.find({ clientId: TEST_CLIENT_ID });
|
|
142
|
+
testClient = allClients.find(c => c.clientId === TEST_CLIENT_ID);
|
|
143
|
+
console.log(` ✓ Test client created: ${TEST_CLIENT_ID}`);
|
|
144
|
+
} else {
|
|
145
|
+
// Update to ensure all features are enabled
|
|
146
|
+
await keycloakManager.clients.update({
|
|
147
|
+
id: testClient.id
|
|
148
|
+
}, {
|
|
149
|
+
authorizationServicesEnabled: true,
|
|
150
|
+
serviceAccountsEnabled: true,
|
|
151
|
+
publicClient: false,
|
|
152
|
+
directAccessGrantsEnabled: true,
|
|
153
|
+
standardFlowEnabled: true
|
|
154
|
+
});
|
|
155
|
+
console.log(` ✓ Test client updated: ${TEST_CLIENT_ID}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 3. Create test user
|
|
159
|
+
console.log('\n3. Setting up test user...');
|
|
160
|
+
const users = await keycloakManager.users.find({ username: TEST_USER_USERNAME });
|
|
161
|
+
let testUser = users.find(u => u.username === TEST_USER_USERNAME);
|
|
162
|
+
|
|
163
|
+
if (!testUser) {
|
|
164
|
+
const created = await keycloakManager.users.create({
|
|
165
|
+
username: TEST_USER_USERNAME,
|
|
166
|
+
email: TEST_USER_EMAIL,
|
|
167
|
+
firstName: TEST_USER_FIRSTNAME,
|
|
168
|
+
lastName: TEST_USER_LASTNAME,
|
|
169
|
+
enabled: true,
|
|
170
|
+
emailVerified: true
|
|
171
|
+
});
|
|
172
|
+
await keycloakManager.users.resetPassword({
|
|
173
|
+
id: created.id,
|
|
174
|
+
credential: {
|
|
175
|
+
type: 'password',
|
|
176
|
+
value: TEST_USER_PASSWORD,
|
|
177
|
+
temporary: false
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
console.log(` ✓ Test user created: ${TEST_USER_USERNAME}`);
|
|
181
|
+
} else {
|
|
182
|
+
console.log(` ✓ Test user already exists: ${TEST_USER_USERNAME}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 4. Create test roles
|
|
186
|
+
console.log('\n4. Setting up test roles...');
|
|
187
|
+
const existingRoles = await keycloakManager.roles.find();
|
|
188
|
+
|
|
189
|
+
for (const roleName of TEST_ROLES) {
|
|
190
|
+
if (!existingRoles.some(r => r.name === roleName)) {
|
|
191
|
+
await keycloakManager.roles.create({
|
|
192
|
+
name: roleName,
|
|
193
|
+
description: `${roleName} for testing`
|
|
194
|
+
});
|
|
195
|
+
console.log(` ✓ Role created: ${roleName}`);
|
|
196
|
+
} else {
|
|
197
|
+
console.log(` ✓ Role already exists: ${roleName}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 5. Create test group
|
|
202
|
+
console.log('\n5. Setting up test groups...');
|
|
203
|
+
const groups = await keycloakManager.groups.find();
|
|
204
|
+
if (!groups.some(g => g.name === TEST_GROUP_NAME)) {
|
|
205
|
+
await keycloakManager.groups.create({
|
|
206
|
+
name: TEST_GROUP_NAME,
|
|
207
|
+
attributes: {
|
|
208
|
+
description: ['Test group for API testing']
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
console.log(' ✓ Test group created');
|
|
212
|
+
} else {
|
|
213
|
+
console.log(' ✓ Test group already exists');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 6. Enable fine-grained admin permissions
|
|
217
|
+
console.log('\n6. Enabling fine-grained admin permissions...');
|
|
218
|
+
try {
|
|
219
|
+
const currentPerms = await keycloakManager.realms.getUsersManagementPermissions({
|
|
220
|
+
realm: TEST_REALM
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (!currentPerms.enabled) {
|
|
224
|
+
await keycloakManager.realms.updateUsersManagementPermissions({
|
|
225
|
+
realm: TEST_REALM,
|
|
226
|
+
enabled: true
|
|
227
|
+
});
|
|
228
|
+
console.log(' ✓ Fine-grained admin permissions enabled');
|
|
229
|
+
} else {
|
|
230
|
+
console.log(' ✓ Fine-grained admin permissions already enabled');
|
|
231
|
+
}
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.log(` ⚠ Fine-grained permissions: ${err.message}`);
|
|
234
|
+
console.log(` ℹ This is typically a server configuration setting that requires`);
|
|
235
|
+
console.log(` enabling "authorizationServicesEnabled" in realm settings`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 7. Update realm to enable protocol mappers and installation providers
|
|
239
|
+
console.log('\n7. Enabling realm features...');
|
|
240
|
+
try {
|
|
241
|
+
await keycloakManager.realms.update(
|
|
242
|
+
{ realm: TEST_REALM },
|
|
243
|
+
{
|
|
244
|
+
installationProviders: ['docker-compose', 'docker', 'kubernetes', 'openshift'],
|
|
245
|
+
installationProvidersEnabled: true
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
console.log(' ✓ Installation providers enabled');
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.log(` ⚠ Installation providers: ${err.message}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 7. Update realm to enable protocol mappers and installation providers
|
|
254
|
+
console.log('\n7. Enabling realm features...');
|
|
255
|
+
try {
|
|
256
|
+
// Note: Some features like installation providers cannot be enabled via API
|
|
257
|
+
// They require server-side configuration in keycloak configuration files
|
|
258
|
+
console.log(' ℹ Installation providers and protocol mappers require server configuration');
|
|
259
|
+
} catch (err) {
|
|
260
|
+
console.log(` ⚠ Realm features: ${err.message}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 8. Create default client scopes
|
|
264
|
+
console.log('\n8. Setting up client scopes...');
|
|
265
|
+
const clientScopes = await keycloakManager.clientScopes.find();
|
|
266
|
+
if (!clientScopes.some(cs => cs.name === TEST_CLIENT_SCOPE)) {
|
|
267
|
+
await keycloakManager.clientScopes.create({
|
|
268
|
+
name: TEST_CLIENT_SCOPE,
|
|
269
|
+
description: 'Test client scope',
|
|
270
|
+
protocol: 'openid-connect'
|
|
271
|
+
});
|
|
272
|
+
console.log(' ✓ Test client scope created');
|
|
273
|
+
} else {
|
|
274
|
+
console.log(' ✓ Test client scope already exists');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 9. Enable authorization services for the test client to support all permissions scenarios
|
|
278
|
+
console.log('\n9. Configuring fine-grained permissions for test client...');
|
|
279
|
+
try {
|
|
280
|
+
const clients = await keycloakManager.clients.find({ clientId: TEST_CLIENT_ID });
|
|
281
|
+
const client = clients.find(c => c.clientId === TEST_CLIENT_ID);
|
|
282
|
+
|
|
283
|
+
if (client) {
|
|
284
|
+
await keycloakManager.clients.updateFineGrainPermission(
|
|
285
|
+
{ id: client.id },
|
|
286
|
+
{ enabled: true }
|
|
287
|
+
);
|
|
288
|
+
console.log(' ✓ Fine-grained permissions enabled for test client');
|
|
289
|
+
}
|
|
290
|
+
} catch (err) {
|
|
291
|
+
console.log(` ⚠ Client fine-grained permissions: ${err.message}`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
console.log('\n✓ Keycloak server configuration complete!');
|
|
295
|
+
console.log(`\nTest realm: ${TEST_REALM}`);
|
|
296
|
+
console.log(`Test client: ${TEST_CLIENT_ID}`);
|
|
297
|
+
console.log(`Test user: ${TEST_USER_USERNAME}:${TEST_USER_PASSWORD}`);
|
|
298
|
+
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error('\n✗ Error configuring Keycloak server:', error.message);
|
|
301
|
+
if (error.response?.data) {
|
|
302
|
+
console.error('Response data:', JSON.stringify(error.response.data, null, 2));
|
|
303
|
+
}
|
|
304
|
+
throw error;
|
|
305
|
+
} finally {
|
|
306
|
+
keycloakManager.stop();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
module.exports = enableServerFeatures;
|
|
311
|
+
|
|
312
|
+
// Allow running standalone
|
|
313
|
+
if (require.main === module) {
|
|
314
|
+
enableServerFeatures();
|
|
315
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const KcAdmClient = require('@keycloak/keycloak-admin-client').default;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple delay function
|
|
5
|
+
*/
|
|
6
|
+
function delay(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* PropertiesManager Configuration
|
|
12
|
+
*
|
|
13
|
+
* Structure:
|
|
14
|
+
* config/default.json - Base configuration (committed, safe values)
|
|
15
|
+
* config/local.json - Local overrides (git-ignored, auto-generated from Docker)
|
|
16
|
+
* config/secrets.json - Sensitive data (git-ignored, credentials)
|
|
17
|
+
*
|
|
18
|
+
* Priority (highest to lowest):
|
|
19
|
+
* 1. Environment variables (PM_KEYCLOAK_BASE_URL=...)
|
|
20
|
+
* 2. Command line (--keycloak.baseUrl=...)
|
|
21
|
+
* 3. config/secrets.json
|
|
22
|
+
* 4. config/local.json (auto-generated from Docker container)
|
|
23
|
+
* 5. config/default.json
|
|
24
|
+
*
|
|
25
|
+
* Note: local.json is automatically created from Docker container configuration
|
|
26
|
+
* when tests start up (see helpers/docker-helpers.js:updateConfigFromDocker)
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
let TEST_CONFIG = null;
|
|
30
|
+
let adminClient = null;
|
|
31
|
+
let propertiesLoaded = false;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Reset configuration cache (call when local.json is updated)
|
|
35
|
+
*/
|
|
36
|
+
function resetConfig() {
|
|
37
|
+
TEST_CONFIG = null;
|
|
38
|
+
delete require.cache[require.resolve('propertiesmanager')];
|
|
39
|
+
propertiesLoaded = false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Loads configuration from propertiesmanager
|
|
44
|
+
* Called lazily to ensure local.json is created from Docker first
|
|
45
|
+
*/
|
|
46
|
+
function loadConfig() {
|
|
47
|
+
if (TEST_CONFIG) {
|
|
48
|
+
return TEST_CONFIG;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Load propertiesmanager only when needed (after local.json is created)
|
|
52
|
+
// Using delete + require to force fresh load
|
|
53
|
+
if (propertiesLoaded) {
|
|
54
|
+
// Force reload by clearing cache
|
|
55
|
+
delete require.cache[require.resolve('propertiesmanager')];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { conf } = require('propertiesmanager');
|
|
59
|
+
propertiesLoaded = true;
|
|
60
|
+
|
|
61
|
+
TEST_CONFIG = {
|
|
62
|
+
baseUrl: conf.keycloak?.baseUrl || 'http://localhost:8080',
|
|
63
|
+
realmName: conf.keycloak?.realm || 'master',
|
|
64
|
+
username: conf.keycloak?.adminUsername || 'admin',
|
|
65
|
+
password: conf.keycloak?.adminPassword || 'admin',
|
|
66
|
+
clientId: conf.keycloak?.clientId || 'admin-cli',
|
|
67
|
+
clientSecret: conf.keycloak?.clientSecret,
|
|
68
|
+
grantType: conf.keycloak?.grantType || 'password',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
console.log('\n📍 Keycloak Configuration (from propertiesmanager):');
|
|
72
|
+
console.log(` Environment: ${process.env.NODE_ENV || 'test'}`);
|
|
73
|
+
console.log(` Base URL: ${TEST_CONFIG.baseUrl}`);
|
|
74
|
+
console.log(` Realm: ${TEST_CONFIG.realmName}`);
|
|
75
|
+
console.log(` ` + `Client ID: ${TEST_CONFIG.clientId}`);
|
|
76
|
+
console.log(` Grant Type: ${TEST_CONFIG.grantType}\n`);
|
|
77
|
+
|
|
78
|
+
return TEST_CONFIG;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Inizializza il client Keycloak admin
|
|
83
|
+
* Aspetta che Keycloak sia pronto prima di connettersi
|
|
84
|
+
*/
|
|
85
|
+
async function initializeAdminClient() {
|
|
86
|
+
if (adminClient) {
|
|
87
|
+
return adminClient;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Load configuration (after local.json has been created from Docker)
|
|
91
|
+
const config = loadConfig();
|
|
92
|
+
|
|
93
|
+
let retries = 30;
|
|
94
|
+
let lastError;
|
|
95
|
+
|
|
96
|
+
while (retries > 0) {
|
|
97
|
+
try {
|
|
98
|
+
adminClient = new KcAdmClient({
|
|
99
|
+
baseUrl: config.baseUrl,
|
|
100
|
+
realmName: config.realmName,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await adminClient.auth({
|
|
104
|
+
username: config.username,
|
|
105
|
+
password: config.password,
|
|
106
|
+
clientId: config.clientId,
|
|
107
|
+
grantType: config.grantType,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
console.log('✓ Keycloak admin client initialized');
|
|
111
|
+
return adminClient;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
lastError = err;
|
|
114
|
+
retries--;
|
|
115
|
+
if (retries > 0) {
|
|
116
|
+
console.log(`Waiting for Keycloak... (${retries} retries left)`);
|
|
117
|
+
await delay(2000);
|
|
118
|
+
} else {
|
|
119
|
+
console.error('OAuth2 Error Details:', {
|
|
120
|
+
message: err.message,
|
|
121
|
+
response: err.response?.data,
|
|
122
|
+
status: err.response?.status,
|
|
123
|
+
url: config.baseUrl
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw new Error(`Failed to connect to Keycloak after retries: ${lastError.message}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Crea il realm di test
|
|
134
|
+
*/
|
|
135
|
+
async function setupTestRealm() {
|
|
136
|
+
const client = await initializeAdminClient();
|
|
137
|
+
const config = loadConfig();
|
|
138
|
+
|
|
139
|
+
// Switcha a master realm per creare il test realm
|
|
140
|
+
client.realmName = 'master';
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Controlla se il realm esiste già
|
|
144
|
+
const realms = await client.realms.find();
|
|
145
|
+
const realmExists = realms.some((r) => r.realm === config.realmName);
|
|
146
|
+
|
|
147
|
+
if (!realmExists) {
|
|
148
|
+
await client.realms.create({
|
|
149
|
+
realm: config.realmName,
|
|
150
|
+
displayName: 'Test Realm',
|
|
151
|
+
enabled: true,
|
|
152
|
+
accessTokenLifespan: 3600,
|
|
153
|
+
refreshTokenMaxReuse: 0,
|
|
154
|
+
actionTokenGeneratedByAdminLifespan: 900,
|
|
155
|
+
actionTokenGeneratedByUserLifespan: 900,
|
|
156
|
+
});
|
|
157
|
+
console.log(`✓ Test realm '${config.realmName}' created`);
|
|
158
|
+
} else {
|
|
159
|
+
console.log(`✓ Test realm '${config.realmName}' already exists`);
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err.response?.status === 409) {
|
|
163
|
+
console.log(`✓ Test realm '${config.realmName}' already exists`);
|
|
164
|
+
} else {
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Switcha back al test realm
|
|
170
|
+
client.realmName = config.realmName;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Pulisce il realm di test
|
|
175
|
+
*/
|
|
176
|
+
async function cleanupTestRealm() {
|
|
177
|
+
if (!adminClient) return;
|
|
178
|
+
|
|
179
|
+
const config = loadConfig();
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
adminClient.realmName = 'master';
|
|
183
|
+
await adminClient.realms.del({ realm: config.realmName });
|
|
184
|
+
console.log(`✓ Test realm '${config.realmName}' deleted`);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (err.response?.status !== 404) {
|
|
187
|
+
console.warn(`Warning: Failed to delete test realm: ${err.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Ritorna il client admin configurato e autenticato
|
|
194
|
+
*/
|
|
195
|
+
function getAdminClient() {
|
|
196
|
+
if (!adminClient) {
|
|
197
|
+
throw new Error('Admin client not initialized. Call initializeAdminClient() first');
|
|
198
|
+
}
|
|
199
|
+
return adminClient;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Reset del client (principalmente per i test)
|
|
204
|
+
*/
|
|
205
|
+
function resetAdminClient() {
|
|
206
|
+
adminClient = null;
|
|
207
|
+
TEST_CONFIG = null; // Reset config too for fresh reload
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
loadConfig,
|
|
212
|
+
initializeAdminClient,
|
|
213
|
+
setupTestRealm,
|
|
214
|
+
cleanupTestRealm,
|
|
215
|
+
getAdminClient,
|
|
216
|
+
resetAdminClient,
|
|
217
|
+
resetConfig,
|
|
218
|
+
};
|