Skip to content

Commit f965914

Browse files
committed
add config importer to update keycloak
1 parent bb0b7ef commit f965914

File tree

4 files changed

+121
-69
lines changed

4 files changed

+121
-69
lines changed

fhir-info-gateway/docker-compose-smart_keycloak.yml renamed to fhir-info-gateway/importer/docker-compose-smart_keycloak.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ services:
1111
KEYCLOAK_PASSWORD: ${KC_ADMIN_PASSWORD}
1212
KEYCLOAK_REALM: ${KC_REALM_NAME}
1313
command: [ "-configFile", "config/backend-services-config.json" ]
14+
1415
networks:
1516
keycloak:
1617
name: keycloak_public
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
version: "3.9"
2+
services:
3+
update-keycloak-config:
4+
image: node:erbium-alpine
5+
environment:
6+
KEYCLOAK_SERVER_URL: ${KC_API_URL}
7+
KEYCLOAK_REALM: ${KC_REALM_NAME}
8+
KEYCLOAK_ADMIN_USER: ${KC_ADMIN_USERNAME}
9+
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
10+
command: sh -c "cd / && npm i axios && node keycloakConfig.js"
11+
configs:
12+
- source: keycloak-config-importer-updateConfig.js
13+
target: /keycloakConfig.js
14+
- source: keycloak-config-importer-updateConfig.json
15+
target: /keycloak-config.json
16+
deploy:
17+
replicas: 1
18+
restart_policy:
19+
condition: none
20+
networks:
21+
keycloak:
22+
configs:
23+
keycloak-config-importer-updateConfig.js:
24+
file: ./update-keycloak-config.js
25+
name: keycloak-config-importer-updateConfig.js-${keycloak_config_importer_updateConfig_js_DIGEST:?err}
26+
labels:
27+
name: keycloakConfig
28+
keycloak-config-importer-updateConfig.json:
29+
file: ./keycloak-config.json
30+
name: keycloak-config-importer-updateConfig.json-${keycloak_config_importer_updateConfig_json_DIGEST:?err}
31+
labels:
32+
name: keycloakConfigJson
33+
networks:
34+
keycloak:
35+
name: keycloak_public
36+
external: true

fhir-info-gateway/importer/update-keycloak-config.js

Lines changed: 77 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
const axios = require("axios");
2+
const fs = require("fs");
23

34
// Load the JSON payload
45
const payload = require("./keycloak-config.json");
6+
const { get } = require("http");
57

68
const serverUrl =
7-
process.env.KEYCLOAK_SERVER_URL || "http://192.168.15.250:9088";
9+
process.env.KEYCLOAK_SERVER_URL || "http://192.168.100.57:9088";
810
const adminUser = process.env.KEYCLOAK_ADMIN_USER || "admin";
911
const adminPassword =
1012
process.env.KEYCLOAK_ADMIN_PASSWORD || "dev_password_only";
1113
const adminClientId = process.env.KEYCLOAK_ADMIN_CLIENT_ID || "admin-cli";
1214
const realm = process.env.KEYCLOAK_REALM || "platform-realm";
15+
const serviceAccountUser =
16+
process.env.KEYCLOAK_SERVICE_ACCOUNT_USER || "service-account"; // Add service account user
1317

1418
// Function definitions
1519
async function getAdminToken(
@@ -70,7 +74,7 @@ async function getRoleByName(roleName, keycloakBaseUrl, realm, adminToken) {
7074

7175
async function getOrCreateClient(client, keycloakBaseUrl, realm, adminToken) {
7276
try {
73-
const clientResponse = await axios.get(
77+
let clientResponse = await axios.get(
7478
`${keycloakBaseUrl}/admin/realms/${realm}/clients?clientId=${client.clientId}`,
7579
{
7680
headers: {
@@ -96,7 +100,7 @@ async function getOrCreateClient(client, keycloakBaseUrl, realm, adminToken) {
96100
console.log(`Updated client: ${client.clientId}`);
97101
} else {
98102
// Client does not exist, create a new one
99-
const newClientResponse = await axios.post(
103+
clientResponse = await axios.post(
100104
`${keycloakBaseUrl}/admin/realms/${realm}/clients`,
101105
client,
102106
{
@@ -107,8 +111,8 @@ async function getOrCreateClient(client, keycloakBaseUrl, realm, adminToken) {
107111
}
108112
);
109113
console.log(`Created client: ${client.clientId}`);
110-
return newClientResponse.data;
111114
}
115+
return clientResponse.data;
112116
} catch (error) {
113117
console.error(
114118
"Error creating or updating client:",
@@ -147,40 +151,12 @@ async function processKeycloakPayload(
147151
let roleId;
148152

149153
try {
150-
// Create or update client
154+
// Create of update client
151155

152156
// Step 1: Create or update a role for each client scope
153-
roleId = await getRoleByName(name, keycloakBaseUrl, realm, adminToken);
154-
if (roleId) {
155-
// Role exists, update it
156-
await axios.put(
157-
`${keycloakBaseUrl}/admin/realms/${realm}/roles-by-id/${roleId}`,
158-
role,
159-
{
160-
headers: {
161-
Authorization: `Bearer ${adminToken}`,
162-
"Content-Type": "application/json",
163-
},
164-
}
165-
);
166-
console.log(`Updated role: ${name}`);
167-
} else {
168-
// Role does not exist, create a new one
169-
const roleResponse = await axios.post(
170-
`${keycloakBaseUrl}/admin/realms/${realm}/roles`,
171-
role,
172-
{
173-
headers: {
174-
Authorization: `Bearer ${adminToken}`,
175-
"Content-Type": "application/json",
176-
},
177-
}
178-
);
179-
roleId = roleResponse.data.id;
180-
console.log(`Created role: ${name}`);
181-
}
182157

183158
// Step 2: Create or update the client scope
159+
184160
const clientScopeResponse = await axios.get(
185161
`${keycloakBaseUrl}/admin/realms/${realm}/client-scopes`,
186162
{
@@ -230,10 +206,11 @@ async function processKeycloakPayload(
230206
}
231207
);
232208
}
233-
209+
roleId = await getRoleByName(name, keycloakBaseUrl, realm, adminToken);
234210
// Step 3: Map the created role to the client scope
235211
await axios.post(
236212
`${keycloakBaseUrl}/admin/realms/${realm}/client-scopes/${clientScope.id}/scope-mappings/realm`,
213+
237214
[{ id: roleId, name }],
238215
{
239216
headers: {
@@ -249,9 +226,10 @@ async function processKeycloakPayload(
249226
})
250227
);
251228

252-
// Step 4: Create or update the service-account user
229+
// Step 5: Create or update the service-account user
230+
let userResponse, user, createdgroupResponse;
253231
try {
254-
const groupResponse = await axios.get(
232+
let groupResponse = await axios.get(
255233
`${keycloakBaseUrl}/admin/realms/${realm}/groups?search=${defaultGroup}`,
256234
{
257235
headers: {
@@ -260,12 +238,11 @@ async function processKeycloakPayload(
260238
},
261239
}
262240
);
263-
264241
let groupId = "";
265242
if (groupResponse.data.length > 0) {
266243
// Group exists, update it
267244
groupId = groupResponse.data[0].id;
268-
await axios.put(
245+
createdgroupResponse = await axios.put(
269246
`${keycloakBaseUrl}/admin/realms/${realm}/groups/${groupId}`,
270247
{
271248
name: defaultGroup,
@@ -279,7 +256,7 @@ async function processKeycloakPayload(
279256
);
280257
} else {
281258
// Group does not exist, create a new one
282-
const createdGroupResponse = await axios.post(
259+
createdgroupResponse = await axios.post(
283260
`${keycloakBaseUrl}/admin/realms/${realm}/groups`,
284261
{
285262
name: defaultGroup,
@@ -291,10 +268,20 @@ async function processKeycloakPayload(
291268
},
292269
}
293270
);
294-
groupId = createdGroupResponse.data.id;
295-
console.log(`Created group: ${defaultGroup}`);
271+
let createdGroup = await axios.get(
272+
`${keycloakBaseUrl}/admin/realms/${realm}/groups?search=${defaultGroup}`,
273+
{
274+
headers: {
275+
Authorization: `Bearer ${adminToken}`,
276+
"Content-Type": "application/json",
277+
},
278+
}
279+
);
280+
console.log(`Created group: `, createdGroup);
281+
groupId = createdGroup.data[0].id;
296282
}
297283

284+
const createdGroup = createdgroupResponse.data[0];
298285
const usersResponse = await axios.get(
299286
`${keycloakBaseUrl}/admin/realms/${realm}/users`,
300287
{
@@ -306,14 +293,13 @@ async function processKeycloakPayload(
306293
);
307294

308295
const users = usersResponse.data;
309-
const user = users.find(
310-
(u) => u.username === defaultUser.username.toLowerCase()
311-
);
296+
user = users.find((u) => u.username === defaultUser.username.toLowerCase());
312297

313298
if (user) {
314299
// User exists, update it
315-
await axios.put(
316-
`${keycloakBaseUrl}/admin/realms/${realm}/users/${user.id}`,
300+
const userId = user.id;
301+
userResponse = await axios.put(
302+
`${keycloakBaseUrl}/admin/realms/${realm}/users/${userId}`,
317303
defaultUser,
318304
{
319305
headers: {
@@ -325,7 +311,7 @@ async function processKeycloakPayload(
325311
console.log(`Updated user: ${defaultUser.username}`);
326312
} else {
327313
// User does not exist, create a new one
328-
const userResponse = await axios.post(
314+
userResponse = await axios.post(
329315
`${keycloakBaseUrl}/admin/realms/${realm}/users`,
330316
defaultUser,
331317
{
@@ -338,9 +324,13 @@ async function processKeycloakPayload(
338324
console.log(`Created user: ${defaultUser.username}`);
339325
}
340326

327+
const createdUser = userResponse.data;
328+
console.log("here", user);
341329
// Reset the password
342-
await axios.put(
343-
`${keycloakBaseUrl}/admin/realms/${realm}/users/${user.id}/reset-password`,
330+
const newPass = await axios.put(
331+
`${keycloakBaseUrl}/admin/realms/${realm}/users/${
332+
userResponse.id ? userResponse.id : user.id
333+
}/reset-password`,
344334
resetPassword,
345335
{
346336
headers: {
@@ -349,11 +339,12 @@ async function processKeycloakPayload(
349339
},
350340
}
351341
);
352-
console.log(`Reset password for user ${defaultUser.username}`);
353-
354-
// Add service-account user to the group
342+
console.log(`Reset password for user ${createdUser}`, newPass.data);
343+
// Step 5: Add service-account user to the group
355344
await axios.put(
356-
`${keycloakBaseUrl}/admin/realms/${realm}/users/${user.id}/groups/${groupId}`,
345+
`${keycloakBaseUrl}/admin/realms/${realm}/users/${
346+
createdUser.id ? createdUser.id : user.id
347+
}/groups/${groupId}`,
357348
{},
358349
{
359350
headers: {
@@ -362,22 +353,21 @@ async function processKeycloakPayload(
362353
},
363354
}
364355
);
365-
console.log(`Added user ${defaultUser.username} to group ${defaultGroup}`);
366-
356+
console.log(`Added ${createdUser} to group ${createdgroupResponse}`);
367357
const uniqueRolesArray = await getUniqueRolesArray(payload);
368358
for (const role of uniqueRolesArray) {
369-
const roleId = await getRoleByName(
359+
const roleID = await getRoleByName(
370360
role.name,
371361
keycloakBaseUrl,
372362
realm,
373363
adminToken
374364
);
375-
376-
await axios.post(
365+
console.log(roleID);
366+
const roleMapping = await axios.post(
377367
`${keycloakBaseUrl}/admin/realms/${realm}/groups/${groupId}/role-mappings/realm`,
378368
[
379369
{
380-
id: roleId,
370+
id: roleID,
381371
clientRole: false,
382372
composite: false,
383373
containerId: realm,
@@ -392,7 +382,7 @@ async function processKeycloakPayload(
392382
},
393383
}
394384
);
395-
console.log(`Added role mapping to group: ${role.name}`);
385+
console.log(`Added role mapping to group ${roleMapping}`, role);
396386
}
397387
} catch (error) {
398388
console.error(
@@ -401,8 +391,23 @@ async function processKeycloakPayload(
401391
);
402392
throw error;
403393
}
404-
}
405394

395+
// Step 6: Add role mapping to the group
396+
// Extract unique roles
397+
398+
// const rolesToBeMapped = [];
399+
// uniqueRolesArray.forEach(async (role) => {
400+
// const rolesToBeMappedPayload = await getRoleByName(
401+
// role.name,
402+
// keycloakBaseUrl,
403+
// realm,
404+
// adminToken
405+
// );
406+
// console.log(rolesToBeMappedPayload);
407+
// rolesToBeMapped.push(rolesToBeMappedPayload);
408+
// });
409+
// console.log("sdsdsd", rolesToBeMapped);
410+
}
406411
async function getUniqueRolesArray(payload) {
407412
const rolesSet = new Set();
408413
const { clientScopes } = payload;
@@ -417,7 +422,6 @@ async function getUniqueRolesArray(payload) {
417422
const uniqueRolesArray = Array.from(rolesSet).map((role) => JSON.parse(role));
418423
return uniqueRolesArray;
419424
}
420-
421425
// Call the function and handle the result
422426
async function main() {
423427
try {
@@ -428,14 +432,19 @@ async function main() {
428432
adminUser,
429433
adminPassword
430434
);
431-
432435
const client = payload.client;
433-
await getOrCreateClient(client, serverUrl, realm, adminToken);
434-
436+
const createorupdateClient = await getOrCreateClient(
437+
client,
438+
serverUrl,
439+
realm,
440+
adminToken
441+
);
442+
console.log(createorupdateClient);
435443
const uniqueRolesArray = await getUniqueRolesArray(payload);
444+
436445
for (const role of uniqueRolesArray) {
437446
const { name } = role;
438-
const roleId = await getRoleByName(name, serverUrl, realm, adminToken);
447+
let roleId = await getRoleByName(name, serverUrl, realm, adminToken);
439448

440449
if (roleId) {
441450
// Role exists, update it
@@ -462,10 +471,10 @@ async function main() {
462471
},
463472
}
464473
);
474+
roleId = roleResponse.data.id;
465475
console.log(`Created role: ${name}`);
466476
}
467477
}
468-
469478
await processKeycloakPayload(payload, serverUrl, realm, adminToken);
470479
console.log("Keycloak payload processed successfully");
471480
} catch (error) {

fhir-info-gateway/swarm.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ function initialize_package() {
4444
fi
4545

4646
(
47-
docker::deploy_service $STACK "${COMPOSE_FILE_PATH}" "docker-compose.yml" "$package_dev_compose_filename" "docker-compose-smart_keycloak.yml"
47+
docker::deploy_service $STACK "${COMPOSE_FILE_PATH}" "docker-compose.yml" "$package_dev_compose_filename" "importer/docker-compose-smart_keycloak.yml"
48+
49+
if [[ "${ACTION}" == "init" ]]; then
50+
docker::deploy_config_importer $STACK "$COMPOSE_FILE_PATH/importer/docker-compose.config.yml" "update-keycloak-config" "fhirinfo"
51+
fi
52+
docker service rm fhir-info-gateway_smart-config >> /dev/null 2>&1
4853
) || {
4954
log error "Failed to deploy package"
5055
exit 1
5156
}
57+
5258
}
5359

5460
function destroy_package() {

0 commit comments

Comments
 (0)