keycloak-api-manager 3.1.0 → 3.2.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.
@@ -0,0 +1,284 @@
1
+ const { expect } = require('chai');
2
+ const { getAdminClient } = require('./config');
3
+
4
+ describe('Groups Handler', function () {
5
+ this.timeout(15000);
6
+ let client;
7
+ let testGroupId;
8
+ let testSubGroupId;
9
+ let testRoleId;
10
+ let testUserId;
11
+
12
+ before(async function () {
13
+ client = getAdminClient();
14
+
15
+ // Create test role for role mappings
16
+ const role = await client.roles.create(
17
+ { realm: 'test-realm' },
18
+ { name: `groups-test-role-${Date.now()}` }
19
+ );
20
+ testRoleId = role.id;
21
+
22
+ // Create test user for group membership
23
+ const user = await client.users.create(
24
+ { realm: 'test-realm' },
25
+ {
26
+ username: `groups-test-user-${Date.now()}`,
27
+ enabled: true,
28
+ }
29
+ );
30
+ testUserId = user.id;
31
+ });
32
+
33
+ // ==================== GROUP CRUD ====================
34
+ describe('CRUD Operations', function () {
35
+ describe('create', function () {
36
+ it('should create a group with valid representation', async function () {
37
+ const groupRep = {
38
+ name: `test-group-${Date.now()}`,
39
+ };
40
+
41
+ const result = await client.groups.create(
42
+ { realm: 'test-realm' },
43
+ groupRep
44
+ );
45
+
46
+ expect(result).to.have.property('id');
47
+ testGroupId = result.id;
48
+ });
49
+
50
+ it('should fail creating duplicate group name', async function () {
51
+ const groupName = `unique-group-${Date.now()}`;
52
+
53
+ await client.groups.create(
54
+ { realm: 'test-realm' },
55
+ { name: groupName }
56
+ );
57
+
58
+ try {
59
+ await client.groups.create(
60
+ { realm: 'test-realm' },
61
+ { name: groupName }
62
+ );
63
+ // Note: Some Keycloak versions allow duplicates at root level
64
+ } catch (err) {
65
+ expect(err).to.exist;
66
+ }
67
+ });
68
+ });
69
+
70
+ describe('find', function () {
71
+ it('should list all groups', async function () {
72
+ const groups = await client.groups.find({ realm: 'test-realm' });
73
+
74
+ expect(groups).to.be.an('array');
75
+ expect(groups.length).to.be.greaterThan(0);
76
+ });
77
+
78
+ it('should search groups by name', async function () {
79
+ const groups = await client.groups.find({
80
+ realm: 'test-realm',
81
+ search: 'test-group',
82
+ });
83
+
84
+ expect(groups).to.be.an('array');
85
+ });
86
+
87
+ it('should support pagination', async function () {
88
+ const groups = await client.groups.find({
89
+ realm: 'test-realm',
90
+ first: 0,
91
+ max: 5,
92
+ });
93
+
94
+ expect(groups).to.be.an('array');
95
+ });
96
+ });
97
+
98
+ describe('findOne', function () {
99
+ it('should find a specific group by id', async function () {
100
+ const group = await client.groups.findOne({
101
+ realm: 'test-realm',
102
+ id: testGroupId,
103
+ });
104
+
105
+ expect(group).to.exist;
106
+ expect(group.id).to.equal(testGroupId);
107
+ });
108
+ });
109
+
110
+ describe('count', function () {
111
+ it('should count total groups', async function () {
112
+ const count = await client.groups.count({ realm: 'test-realm' });
113
+
114
+ expect(count).to.be.a('number');
115
+ expect(count).to.be.greaterThan(0);
116
+ });
117
+ });
118
+
119
+ describe('update', function () {
120
+ it('should update group attributes', async function () {
121
+ const updateRep = {
122
+ name: `updated-group-${Date.now()}`,
123
+ attributes: {
124
+ department: ['Engineering'],
125
+ },
126
+ };
127
+
128
+ await client.groups.update(
129
+ { realm: 'test-realm', id: testGroupId },
130
+ updateRep
131
+ );
132
+
133
+ const updated = await client.groups.findOne({
134
+ realm: 'test-realm',
135
+ id: testGroupId,
136
+ });
137
+
138
+ expect(updated.name).to.include('updated-group');
139
+ });
140
+ });
141
+ });
142
+
143
+ // ==================== SUBGROUPS ====================
144
+ describe('Subgroups', function () {
145
+ describe('listSubGroups', function () {
146
+ it('should list subgroups of a group', async function () {
147
+ const subgroups = await client.groups.listSubGroups({
148
+ realm: 'test-realm',
149
+ id: testGroupId,
150
+ });
151
+
152
+ expect(subgroups).to.be.an('array');
153
+ });
154
+ });
155
+
156
+ // Note: Creating subgroups might require special handling in some Keycloak versions
157
+ });
158
+
159
+ // ==================== REALM ROLE MAPPINGS ====================
160
+ describe('Realm Role Mappings', function () {
161
+ describe('addRealmRoleMappings', function () {
162
+ it('should add realm roles to group', async function () {
163
+ await client.groups.addRealmRoleMappings({
164
+ realm: 'test-realm',
165
+ id: testGroupId,
166
+ roles: [
167
+ {
168
+ id: testRoleId,
169
+ name: `groups-test-role-${Date.now()}`,
170
+ },
171
+ ],
172
+ });
173
+
174
+ // Verify role added
175
+ });
176
+ });
177
+
178
+ describe('listRoleMappings', function () {
179
+ it('should list all role mappings for group', async function () {
180
+ const mappings = await client.groups.listRoleMappings({
181
+ realm: 'test-realm',
182
+ id: testGroupId,
183
+ });
184
+
185
+ expect(mappings).to.be.an('object');
186
+ expect(mappings).to.have.property('realmMappings');
187
+ });
188
+ });
189
+
190
+ describe('listRealmRoleMappings', function () {
191
+ it('should list realm role mappings', async function () {
192
+ const mappings = await client.groups.listRealmRoleMappings({
193
+ realm: 'test-realm',
194
+ id: testGroupId,
195
+ });
196
+
197
+ expect(mappings).to.be.an('array');
198
+ });
199
+ });
200
+
201
+ describe('listCompositeRealmRoleMappings', function () {
202
+ it('should list composite realm role mappings', async function () {
203
+ const mappings = await client.groups.listCompositeRealmRoleMappings({
204
+ realm: 'test-realm',
205
+ id: testGroupId,
206
+ });
207
+
208
+ expect(mappings).to.be.an('array');
209
+ });
210
+ });
211
+
212
+ describe('listAvailableRealmRoleMappings', function () {
213
+ it('should list available realm roles for group', async function () {
214
+ const available = await client.groups.listAvailableRealmRoleMappings({
215
+ realm: 'test-realm',
216
+ id: testGroupId,
217
+ });
218
+
219
+ expect(available).to.be.an('array');
220
+ });
221
+ });
222
+
223
+ describe('delRealmRoleMappings', function () {
224
+ it('should remove realm roles from group', async function () {
225
+ const mappings = await client.groups.listRealmRoleMappings({
226
+ realm: 'test-realm',
227
+ id: testGroupId,
228
+ });
229
+
230
+ if (mappings.length > 0) {
231
+ await client.groups.delRealmRoleMappings({
232
+ realm: 'test-realm',
233
+ id: testGroupId,
234
+ roles: mappings,
235
+ });
236
+
237
+ // Verify removed
238
+ }
239
+ });
240
+ });
241
+ });
242
+
243
+ // ==================== CLIENT ROLE MAPPINGS ====================
244
+ describe('Client Role Mappings', function () {
245
+ describe('listAvailableClientRoleMappings', function () {
246
+ it('should list available client roles for group', async function () {
247
+ // Get a client first
248
+ const clients = await client.clients.find({ realm: 'test-realm' });
249
+ if (clients.length > 0) {
250
+ const available = await client.groups.listAvailableClientRoleMappings({
251
+ realm: 'test-realm',
252
+ id: testGroupId,
253
+ clientUniqueId: clients[0].id,
254
+ });
255
+
256
+ expect(available).to.be.an('array');
257
+ }
258
+ });
259
+ });
260
+ });
261
+
262
+ // ==================== CLEANUP ====================
263
+ after(async function () {
264
+ try {
265
+ if (testGroupId) {
266
+ await client.groups.del({ realm: 'test-realm', id: testGroupId });
267
+ }
268
+ if (testRoleId) {
269
+ const role = await client.roles.findOneById({
270
+ realm: 'test-realm',
271
+ id: testRoleId,
272
+ });
273
+ if (role) {
274
+ await client.roles.delByName({ realm: 'test-realm', name: role.name });
275
+ }
276
+ }
277
+ if (testUserId) {
278
+ await client.users.del({ realm: 'test-realm', id: testUserId });
279
+ }
280
+ } catch (err) {
281
+ console.error('Cleanup error:', err.message);
282
+ }
283
+ });
284
+ });
@@ -0,0 +1,197 @@
1
+ const { expect } = require('chai');
2
+ const { getAdminClient } = require('./config');
3
+
4
+ describe('Identity Providers Handler', function () {
5
+ this.timeout(15000);
6
+ let client;
7
+ let testIdpAlias;
8
+ let testMapperId;
9
+
10
+ before(function () {
11
+ client = getAdminClient();
12
+ testIdpAlias = `test-idp-${Date.now()}`;
13
+ });
14
+
15
+ // ==================== IDENTITY PROVIDER CRUD ====================
16
+ describe('CRUD Operations', function () {
17
+ describe('create', function () {
18
+ it('should create an identity provider with valid representation', async function () {
19
+ const idpRep = {
20
+ alias: testIdpAlias,
21
+ displayName: 'Test IDP',
22
+ providerId: 'oidc',
23
+ enabled: true,
24
+ config: {
25
+ clientId: 'test-client',
26
+ clientSecret: 'test-secret',
27
+ tokenUrl: 'https://example.com/token',
28
+ authorizationUrl: 'https://example.com/authorize',
29
+ userInfoUrl: 'https://example.com/userinfo',
30
+ },
31
+ };
32
+
33
+ const result = await client.identityProviders.create(
34
+ { realm: 'test-realm' },
35
+ idpRep
36
+ );
37
+
38
+ expect(result).to.have.property('alias');
39
+ expect(result.alias).to.equal(testIdpAlias);
40
+ });
41
+ });
42
+
43
+ describe('find', function () {
44
+ it('should list all identity providers', async function () {
45
+ const idps = await client.identityProviders.find({ realm: 'test-realm' });
46
+
47
+ expect(idps).to.be.an('array');
48
+ expect(idps.some((idp) => idp.alias === testIdpAlias)).to.be.true;
49
+ });
50
+ });
51
+
52
+ describe('findOne', function () {
53
+ it('should find a specific identity provider by alias', async function () {
54
+ const idp = await client.identityProviders.findOne({
55
+ realm: 'test-realm',
56
+ alias: testIdpAlias,
57
+ });
58
+
59
+ expect(idp).to.exist;
60
+ expect(idp.alias).to.equal(testIdpAlias);
61
+ });
62
+ });
63
+
64
+ describe('update', function () {
65
+ it('should update identity provider attributes', async function () {
66
+ const updateRep = {
67
+ displayName: 'Updated IDP Name',
68
+ enabled: false,
69
+ };
70
+
71
+ await client.identityProviders.update(
72
+ { realm: 'test-realm', alias: testIdpAlias },
73
+ updateRep
74
+ );
75
+
76
+ const updated = await client.identityProviders.findOne({
77
+ realm: 'test-realm',
78
+ alias: testIdpAlias,
79
+ });
80
+
81
+ expect(updated.displayName).to.equal('Updated IDP Name');
82
+ expect(updated.enabled).to.be.false;
83
+ });
84
+ });
85
+ });
86
+
87
+ // ==================== IDENTITY PROVIDER MAPPERS ====================
88
+ describe('Mappers', function () {
89
+ describe('createMapper', function () {
90
+ it('should create a mapper for identity provider', async function () {
91
+ const mapperRep = {
92
+ name: 'test-mapper',
93
+ identityProviderAlias: testIdpAlias,
94
+ identityProviderMapper: 'oidc-user-attribute-idp-mapper',
95
+ config: {
96
+ 'user.attribute': 'email',
97
+ 'claim': 'email',
98
+ },
99
+ };
100
+
101
+ const result = await client.identityProviders.createMapper(
102
+ { realm: 'test-realm' },
103
+ mapperRep
104
+ );
105
+
106
+ expect(result).to.exist;
107
+ testMapperId = result.id;
108
+ });
109
+ });
110
+
111
+ describe('findMappers', function () {
112
+ it('should find all mappers for identity provider', async function () {
113
+ const mappers = await client.identityProviders.findMappers({
114
+ realm: 'test-realm',
115
+ alias: testIdpAlias,
116
+ });
117
+
118
+ expect(mappers).to.be.an('array');
119
+ });
120
+ });
121
+
122
+ describe('findOneMapper', function () {
123
+ it('should find a specific mapper by id', async function () {
124
+ if (testMapperId) {
125
+ const mapper = await client.identityProviders.findOneMapper({
126
+ realm: 'test-realm',
127
+ alias: testIdpAlias,
128
+ id: testMapperId,
129
+ });
130
+
131
+ expect(mapper).to.exist;
132
+ }
133
+ });
134
+ });
135
+
136
+ describe('delMapper', function () {
137
+ it('should delete a mapper', async function () {
138
+ if (testMapperId) {
139
+ await client.identityProviders.delMapper({
140
+ realm: 'test-realm',
141
+ alias: testIdpAlias,
142
+ id: testMapperId,
143
+ });
144
+
145
+ // Verify deleted
146
+ }
147
+ });
148
+ });
149
+ });
150
+
151
+ // ==================== IDENTITY PROVIDER MANAGEMENT ====================
152
+ describe('Management Operations', function () {
153
+ describe('findFactory', function () {
154
+ it('should retrieve identity provider factory', async function () {
155
+ try {
156
+ const factory = await client.identityProviders.findFactory({
157
+ realm: 'test-realm',
158
+ providerId: 'oidc',
159
+ });
160
+
161
+ expect(factory).to.exist;
162
+ } catch (err) {
163
+ // Factory endpoint might not be available
164
+ }
165
+ });
166
+ });
167
+
168
+ describe('listPermissions', function () {
169
+ it('should list identity provider permissions', async function () {
170
+ try {
171
+ const perms = await client.identityProviders.listPermissions({
172
+ realm: 'test-realm',
173
+ alias: testIdpAlias,
174
+ });
175
+
176
+ expect(perms).to.be.an('object');
177
+ } catch (err) {
178
+ // Permissions might not be enabled
179
+ }
180
+ });
181
+ });
182
+ });
183
+
184
+ // ==================== CLEANUP ====================
185
+ after(async function () {
186
+ try {
187
+ if (testIdpAlias) {
188
+ await client.identityProviders.del({
189
+ realm: 'test-realm',
190
+ alias: testIdpAlias,
191
+ });
192
+ }
193
+ } catch (err) {
194
+ console.error('Cleanup error:', err.message);
195
+ }
196
+ });
197
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Mocha Root Setup Hook
3
+ * Orchestrates Docker container lifecycle and Keycloak initialization
4
+ */
5
+
6
+ const { startDocker, stopDocker, waitForHealthy } = require('./docker-helpers');
7
+ const { initializeAdminClient, setupTestRealm, cleanupTestRealm } = require('./config');
8
+
9
+ // Root hook plugin for Mocha
10
+ exports.mochaHooks = {
11
+ async beforeAll() {
12
+ this.timeout(120000); // 2 minutes max for setup
13
+
14
+ console.log('\n========== TEST SETUP ==========');
15
+ console.log('Starting Docker containers...');
16
+
17
+ try {
18
+ // Start Docker Compose
19
+ await startDocker();
20
+
21
+ // Wait for services to be healthy
22
+ await waitForHealthy();
23
+
24
+ // Initialize Keycloak admin client
25
+ await initializeAdminClient();
26
+
27
+ // Setup test realm
28
+ await setupTestRealm();
29
+
30
+ console.log('✓ Test environment ready\n');
31
+ } catch (err) {
32
+ console.error('✗ Test setup failed:', err.message);
33
+ throw err;
34
+ }
35
+ },
36
+
37
+ async afterAll() {
38
+ this.timeout(60000); // 1 minute max for teardown
39
+
40
+ console.log('\n========== TEST TEARDOWN ==========');
41
+
42
+ try {
43
+ // Cleanup Keycloak test realm
44
+ await cleanupTestRealm();
45
+
46
+ // Stop Docker Compose
47
+ await stopDocker();
48
+
49
+ console.log('✓ Test environment cleaned up\n');
50
+ } catch (err) {
51
+ console.error('✗ Test teardown failed:', err.message);
52
+ // Don't throw during cleanup to allow partial cleanup
53
+ }
54
+ },
55
+ };