keycloak-api-manager 4.0.0 → 5.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.
Files changed (48) hide show
  1. package/Handlers/attackDetectionHandler.js +64 -0
  2. package/Handlers/clientPoliciesHandler.js +120 -0
  3. package/Handlers/groupsHandler.js +32 -0
  4. package/Handlers/organizationsHandler.js +243 -0
  5. package/Handlers/serverInfoHandler.js +36 -0
  6. package/Handlers/userProfileHandler.js +121 -0
  7. package/README.md +83 -7157
  8. package/docs/architecture.md +47 -0
  9. package/docs/deployment.md +32 -0
  10. package/docs/keycloak-setup.md +47 -0
  11. package/docs/test-configuration.md +43 -0
  12. package/docs/testing.md +60 -0
  13. package/index.js +156 -240
  14. package/package.json +28 -15
  15. package/test/.mocharc.json +2 -2
  16. package/test/config/secrets.json +12 -0
  17. package/test/docker-keycloak/certs/keycloak.crt +58 -0
  18. package/test/docker-keycloak/certs/keycloak.key +28 -0
  19. package/test/docker-keycloak/docker-compose-https.yml +2 -0
  20. package/test/docker-keycloak/docker-compose.yml +4 -4
  21. package/test/helpers/matrix.js +16 -0
  22. package/test/matrix/auth.json +27 -0
  23. package/test/matrix/clients.json +45 -0
  24. package/test/matrix/realms-components-idp.json +37 -0
  25. package/test/matrix/users-roles-groups.json +26 -0
  26. package/test/package-lock.json +3032 -0
  27. package/test/specs/attackDetection.test.js +102 -0
  28. package/test/specs/clientCredentials.test.js +79 -0
  29. package/test/specs/clientPolicies.test.js +162 -0
  30. package/test/specs/{debugClientLibrary.test.js → diagnostics/debugClientLibrary.test.js} +2 -2
  31. package/test/specs/groupPermissions.test.js +87 -0
  32. package/test/specs/matrix/matrix-auth.test.js +112 -0
  33. package/test/specs/matrix/matrix-clients.test.js +59 -0
  34. package/test/specs/matrix/matrix-realms-components-idp.test.js +111 -0
  35. package/test/specs/matrix/matrix-users-roles-groups.test.js +68 -0
  36. package/test/specs/organizations.test.js +183 -0
  37. package/test/specs/serverInfo.test.js +140 -0
  38. package/test/specs/userProfile.test.js +135 -0
  39. package/test/{enableServerFeatures.js → support/enableServerFeatures.js} +43 -26
  40. package/test/{setup.js → support/setup.js} +3 -3
  41. package/test/support/testConfig.js +69 -0
  42. package/test/testConfig.js +1 -69
  43. package/test-output.log +72 -0
  44. package/test/TESTING.md +0 -327
  45. package/test/config/CONFIGURATION.md +0 -170
  46. package/test/diagnostic-protocol-mappers.js +0 -189
  47. package/test/docker-keycloak/DEPLOYMENT_GUIDE.md +0 -262
  48. package/test/helpers/setup.js +0 -186
@@ -42,9 +42,9 @@ const path = require('path');
42
42
 
43
43
  // Ensure NODE_ENV and config path are set before requiring testConfig
44
44
  process.env.NODE_ENV = process.env.NODE_ENV || 'test';
45
- process.env.PROPERTIES_PATH = path.join(__dirname, 'config');
45
+ process.env.PROPERTIES_PATH = path.join(__dirname, '../config');
46
46
 
47
- const keycloakManager = require('../index');
47
+ const keycloakManager = require('../../index');
48
48
  const {
49
49
  TEST_REALM,
50
50
  TEST_CLIENT_ID,
@@ -103,11 +103,21 @@ async function enableServerFeatures() {
103
103
  duplicateEmailsAllowed: false,
104
104
  resetPasswordAllowed: true,
105
105
  editUsernameAllowed: false,
106
- bruteForceProtected: false
106
+ bruteForceProtected: false,
107
+ organizationsEnabled: true // Enable Organizations (requires 'organization' feature flag)
107
108
  });
108
109
  console.log(` ✓ Test realm created: ${TEST_REALM}`);
109
110
  } else {
110
111
  console.log(` ✓ Test realm already exists: ${TEST_REALM}`);
112
+ // Update existing realm to ensure all required settings are enabled
113
+ await keycloakManager.realms.update(
114
+ { realm: TEST_REALM },
115
+ {
116
+ enabled: true,
117
+ bruteForceProtected: false,
118
+ organizationsEnabled: true
119
+ }
120
+ );
111
121
  }
112
122
 
113
123
  // Switch to test realm for all subsequent operations
@@ -201,56 +211,63 @@ async function enableServerFeatures() {
201
211
  // 5. Create test group
202
212
  console.log('\n5. Setting up test groups...');
203
213
  const groups = await keycloakManager.groups.find();
214
+ let testGroupId;
204
215
  if (!groups.some(g => g.name === TEST_GROUP_NAME)) {
205
- await keycloakManager.groups.create({
216
+ const groupResult = await keycloakManager.groups.create({
206
217
  name: TEST_GROUP_NAME,
207
218
  attributes: {
208
219
  description: ['Test group for API testing']
209
220
  }
210
221
  });
222
+ testGroupId = groupResult.id;
211
223
  console.log(' ✓ Test group created');
212
224
  } else {
225
+ testGroupId = groups.find(g => g.name === TEST_GROUP_NAME).id;
213
226
  console.log(' ✓ Test group already exists');
214
227
  }
215
228
 
216
229
  // 6. Enable fine-grained admin permissions
217
230
  console.log('\n6. Enabling fine-grained admin permissions...');
218
231
  try {
219
- const currentPerms = await keycloakManager.realms.getUsersManagementPermissions({
232
+ // Enable users management permissions
233
+ const currentUserPerms = await keycloakManager.realms.getUsersManagementPermissions({
220
234
  realm: TEST_REALM
221
235
  });
222
236
 
223
- if (!currentPerms.enabled) {
237
+ if (!currentUserPerms.enabled) {
224
238
  await keycloakManager.realms.updateUsersManagementPermissions({
225
239
  realm: TEST_REALM,
226
240
  enabled: true
227
241
  });
228
- console.log(' ✓ Fine-grained admin permissions enabled');
242
+ console.log(' ✓ Users fine-grained admin permissions enabled');
229
243
  } else {
230
- console.log(' ✓ Fine-grained admin permissions already enabled');
244
+ console.log(' ✓ Users fine-grained admin permissions already enabled');
231
245
  }
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
+ // Enable groups management permissions for the test group
248
+ if (testGroupId) {
249
+ try {
250
+ const groupPerms = await keycloakManager.groups.listPermissions({ id: testGroupId });
251
+ if (!groupPerms.enabled) {
252
+ await keycloakManager.groups.setPermissions(
253
+ { id: testGroupId },
254
+ { enabled: true }
255
+ );
256
+ console.log(' ✓ Groups fine-grained permissions enabled');
257
+ } else {
258
+ console.log(' ✓ Groups fine-grained permissions already enabled');
259
+ }
260
+ } catch (groupPermErr) {
261
+ console.log(` ⚠ Groups permissions: ${groupPermErr.message}`);
246
262
  }
247
- );
248
- console.log(' ✓ Installation providers enabled');
263
+ }
249
264
  } catch (err) {
250
- console.log(` ⚠ Installation providers: ${err.message}`);
265
+ console.log(` ⚠ Fine-grained permissions: ${err.message}`);
266
+ console.log(` ℹ This feature requires 'admin-fine-grained-authz' to be enabled`);
267
+ console.log(` in KC_FEATURES environment variable`);
251
268
  }
252
269
 
253
- // 7. Update realm to enable protocol mappers and installation providers
270
+ // 7. Realm features requiring server-side configuration
254
271
  console.log('\n7. Enabling realm features...');
255
272
  try {
256
273
  // Note: Some features like installation providers cannot be enabled via API
@@ -31,7 +31,7 @@ const { exec } = require('child_process');
31
31
  const path = require('path');
32
32
  const fs = require('fs');
33
33
  const os = require('os');
34
- const keycloakManager = require('../index');
34
+ const keycloakManager = require('../../index');
35
35
 
36
36
  let sshTunnelProcess = null;
37
37
 
@@ -92,7 +92,7 @@ before(async function() {
92
92
  console.log(`✓ SSH tunnel created: http://${sshTunnelUrl}\n`);
93
93
 
94
94
  // Update config to use tunnel
95
- const configPath = path.join(__dirname, 'config/local.json');
95
+ const configPath = path.join(__dirname, '../config/local.json');
96
96
  let config = {};
97
97
 
98
98
  if (fs.existsSync(configPath)) {
@@ -169,7 +169,7 @@ after(async function() {
169
169
 
170
170
  // Clean up SSH tunnel config file if it was created
171
171
  try {
172
- const configPath = path.join(__dirname, 'config/local.json');
172
+ const configPath = path.join(__dirname, '../config/local.json');
173
173
  if (fs.existsSync(configPath)) {
174
174
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
175
175
  // Only delete local.json if it contains SSH tunnel config
@@ -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
+ };
@@ -1,69 +1 @@
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
- };
1
+ module.exports = require('./support/testConfig');
@@ -0,0 +1,72 @@
1
+
2
+ > keycloak-api-manager@4.1.0 test
3
+ > npm --prefix test install && npm --prefix test test
4
+
5
+
6
+ up to date, audited 260 packages in 2s
7
+
8
+ 48 packages are looking for funding
9
+ run `npm fund` for details
10
+
11
+ 12 vulnerabilities (3 low, 3 moderate, 4 high, 2 critical)
12
+
13
+ To address all issues possible (including breaking changes), run:
14
+ npm audit fix --force
15
+
16
+ Some issues need review, and may require choosing
17
+ a different dependency.
18
+
19
+ Run `npm audit` for details.
20
+
21
+ > keycloak-api-manager-tests@1.0.0 test
22
+ > NODE_ENV=test NODE_PATH=./node_modules mocha --exit
23
+
24
+
25
+ Exception during run: /Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/Handlers/userProfileHandler.js:79
26
+ body: JSON.strasync function(filter) {
27
+ ^^^^^^^^
28
+
29
+ SyntaxError: Unexpected token 'function'
30
+ at wrapSafe (node:internal/modules/cjs/loader:1515:18)
31
+ at Module._compile (node:internal/modules/cjs/loader:1537:20)
32
+ at Object..js (node:internal/modules/cjs/loader:1708:10)
33
+ at Module.load (node:internal/modules/cjs/loader:1318:32)
34
+ at Function._load (node:internal/modules/cjs/loader:1128:12)
35
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
36
+ at wrapModuleLoad (node:internal/modules/cjs/loader:219:24)
37
+ at Module.require (node:internal/modules/cjs/loader:1340:12)
38
+ at require (node:internal/modules/helpers:138:16)
39
+ at Object.<anonymous> (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/index.js:15:24)
40
+ at Module._compile (node:internal/modules/cjs/loader:1565:14)
41
+ at Object..js (node:internal/modules/cjs/loader:1708:10)
42
+ at Module.load (node:internal/modules/cjs/loader:1318:32)
43
+ at Function._load (node:internal/modules/cjs/loader:1128:12)
44
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
45
+ at wrapModuleLoad (node:internal/modules/cjs/loader:219:24)
46
+ at Module.require (node:internal/modules/cjs/loader:1340:12)
47
+ at require (node:internal/modules/helpers:138:16)
48
+ at Object.<anonymous> (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/enableServerFeatures.js:47:25)
49
+ at Module._compile (node:internal/modules/cjs/loader:1565:14)
50
+ at Object..js (node:internal/modules/cjs/loader:1708:10)
51
+ at Module.load (node:internal/modules/cjs/loader:1318:32)
52
+ at Function._load (node:internal/modules/cjs/loader:1128:12)
53
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
54
+ at wrapModuleLoad (node:internal/modules/cjs/loader:219:24)
55
+ at Module.require (node:internal/modules/cjs/loader:1340:12)
56
+ at require (node:internal/modules/helpers:138:16)
57
+ at Object.<anonymous> (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/setup.js:29:30)
58
+ at Module._compile (node:internal/modules/cjs/loader:1565:14)
59
+ at Object..js (node:internal/modules/cjs/loader:1708:10)
60
+ at Module.load (node:internal/modules/cjs/loader:1318:32)
61
+ at Function._load (node:internal/modules/cjs/loader:1128:12)
62
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
63
+ at wrapModuleLoad (node:internal/modules/cjs/loader:219:24)
64
+ at cjsLoader (node:internal/modules/esm/translators:263:5)
65
+ at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:196:7)
66
+ at ModuleJob.run (node:internal/modules/esm/module_job:271:25)
67
+ at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:547:26)
68
+ at async formattedImport (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/node_modules/mocha/lib/nodejs/esm-utils.js:9:14)
69
+ at async exports.requireOrImport (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/node_modules/mocha/lib/nodejs/esm-utils.js:42:28)
70
+ at async exports.loadFilesAsync (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/node_modules/mocha/lib/nodejs/esm-utils.js:100:20)
71
+ at async singleRun (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/node_modules/mocha/lib/cli/run-helpers.js:162:3)
72
+ at async exports.handler (/Users/Alessandro/Src/WorkSpace/WorkspaceDemo/Idealia/Keyclock/keycloak-api-manager/test/node_modules/mocha/lib/cli/run.js:375:5)