diff --git a/config.yaml b/config.yaml index 3d77445b..c422b527 100644 --- a/config.yaml +++ b/config.yaml @@ -27,6 +27,7 @@ packages: - database-postgres - reprocess-mediator - fhir-ig-importer + - fhir-info-gateway profiles: - name: cdr-dw @@ -47,6 +48,7 @@ profiles: - kafka-unbundler-consumer - fhir-ig-importer - reprocess-mediator + - fhir-info-gateway envFiles: - cdr-dw.env @@ -63,6 +65,8 @@ profiles: - client-registry-jempi - identity-access-manager-keycloak - openhim-mapping-mediator + - fhir-ig-importer + - fhir-info-gateway envFiles: - cdr.env diff --git a/fhir-info-gateway/docker-compose.dev.yml b/fhir-info-gateway/docker-compose.dev.yml new file mode 100644 index 00000000..7b96e520 --- /dev/null +++ b/fhir-info-gateway/docker-compose.dev.yml @@ -0,0 +1,8 @@ +version: '3.9' + +services: + fhir-info-gateway: + ports: + - target: 8080 + published: 8880 + mode: host diff --git a/fhir-info-gateway/docker-compose.yml b/fhir-info-gateway/docker-compose.yml new file mode 100644 index 00000000..27658a50 --- /dev/null +++ b/fhir-info-gateway/docker-compose.yml @@ -0,0 +1,33 @@ +version: "3.9" +services: + fhir-info-gateway: + image: ${FHIR_INFO_GATEWAY_IMAGE} + networks: + openhim: + keycloak: + default: + environment: + TOKEN_ISSUER: ${KC_API_URL}/realms/${KC_REALM_NAME} + ACCESS_CHECKER: ${ACCESS_CHECKER} + PROXY_TO: ${GATEWAY_MPI_PROXY_URL} + BACKEND_TYPE: ${BACKEND_TYPE} + RUN_MODE: ${RUN_MODE} + deploy: + replicas: ${FHIR_INFO_GATEWAY_INSTANCES} + placement: + max_replicas_per_node: ${FHIR_INFO_GATEWAY_MAX_REPLICAS_PER_NODE} + resources: + limits: + cpus: ${FHIR_INFO_GATEWAY_CPU_LIMIT} + memory: ${FHIR_INFO_GATEWAY_MEMORY_LIMIT} + reservations: + cpus: ${FHIR_INFO_GATEWAY_CPU_RESERVE} + memory: ${FHIR_INFO_GATEWAY_MEMORY_RESERVE} +networks: + openhim: + name: openhim_public + external: true + keycloak: + name: keycloak_public + external: true + default: diff --git a/fhir-info-gateway/importer/docker-compose-smart_keycloak.yml b/fhir-info-gateway/importer/docker-compose-smart_keycloak.yml new file mode 100644 index 00000000..b60c6bcd --- /dev/null +++ b/fhir-info-gateway/importer/docker-compose-smart_keycloak.yml @@ -0,0 +1,18 @@ +version: "3.9" + +services: + smart-config: + image: jembi/keycloak-config:v0.0.1 + networks: + keycloak: + environment: + KEYCLOAK_BASE_URL: ${KC_API_URL} + KEYCLOAK_USER: ${KC_ADMIN_USERNAME} + KEYCLOAK_PASSWORD: ${KC_ADMIN_PASSWORD} + KEYCLOAK_REALM: ${KC_REALM_NAME} + command: [ "-configFile", "config/backend-services-config.json" ] + +networks: + keycloak: + name: keycloak_public + external: true diff --git a/fhir-info-gateway/importer/docker-compose.config.yml b/fhir-info-gateway/importer/docker-compose.config.yml new file mode 100644 index 00000000..b2e4ff32 --- /dev/null +++ b/fhir-info-gateway/importer/docker-compose.config.yml @@ -0,0 +1,36 @@ +version: "3.9" +services: + update-keycloak-config: + image: node:erbium-alpine + environment: + KEYCLOAK_SERVER_URL: ${KC_API_URL} + KEYCLOAK_REALM: ${KC_REALM_NAME} + KEYCLOAK_ADMIN_USER: ${KC_ADMIN_USERNAME} + KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD} + command: sh -c "cd / && npm i axios && node keycloakConfig.js" + configs: + - source: keycloak-config-importer-updateConfig.js + target: /keycloakConfig.js + - source: keycloak-config-importer-updateConfig.json + target: /keycloak-config.json + deploy: + replicas: 1 + restart_policy: + condition: none + networks: + keycloak: +configs: + keycloak-config-importer-updateConfig.js: + file: ./update-keycloak-config.js + name: keycloak-config-importer-updateConfig.js-${keycloak_config_importer_updateConfig_js_DIGEST:?err} + labels: + name: keycloakConfig + keycloak-config-importer-updateConfig.json: + file: ./keycloak-config.json + name: keycloak-config-importer-updateConfig.json-${keycloak_config_importer_updateConfig_json_DIGEST:?err} + labels: + name: keycloakConfigJson +networks: + keycloak: + name: keycloak_public + external: true diff --git a/fhir-info-gateway/importer/keycloak-config.json b/fhir-info-gateway/importer/keycloak-config.json new file mode 100644 index 00000000..95ea4dce --- /dev/null +++ b/fhir-info-gateway/importer/keycloak-config.json @@ -0,0 +1,231 @@ +{ + "clientScopes": { + "system/*.rs": { + "protocol": "openid-connect", + "description": "Read access to all resources", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "admin", + "name": "administrator", + "description": "Has full access to all resources" + } + }, + + "system/Patient.cruds": { + "protocol": "openid-connect", + "description": "Read access to all data", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "admin", + "name": "administrator", + "description": "Has full access to all resources" + } + }, + "system/Patient.cud": { + "protocol": "openid-connect", + "description": "Read and write access to all Patient", + "attributes": { + "include.in.token.scope": "false" + }, + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "manager", + "name": "manager", + "description": "Has limited access to all resources" + } + }, + "system/Patient.rs": { + "protocol": "openid-connect", + "description": "Read access to all Patient", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "user", + "name": "user", + "description": "Has read access to all resources" + } + }, + "system/Encounter.rs": { + "protocol": "openid-connect", + "description": "Read access to all Encounter data", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "user", + "name": "user", + "description": "Has read access to all resources" + } + }, + "system/Observation.rs": { + "protocol": "openid-connect", + "description": "Read access to all Observation data", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "user", + "name": "user", + "description": "Has read access to all resources" + } + }, + "system/Encounter.cruds": { + "protocol": "openid-connect", + "description": "Read, write and search access to all Encounter data", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "admin", + "name": "administrator", + "description": "Has full access to all resources" + } + }, + "system/Encounter.cud": { + "protocol": "openid-connect", + "description": "Read and write access to all Encounter data", + "attributes": { + "include.in.token.scope": "false" + }, + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "manager", + "name": "manager", + "description": "Has limited access to all resources" + } + }, + "system/Observation.cruds": { + "protocol": "openid-connect", + "description": "Read access to all Observation data", + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "admin", + "name": "administrator", + "description": "Has full access to all resources" + } + }, + "system/Observation.cud": { + "protocol": "openid-connect", + "description": "Read and write access to all Observation data", + "attributes": { + "include.in.token.scope": "false" + }, + "mappers": { + "Audience Mapper": { + "protocol": "openid-connect", + "protocolmapper": "oidc-audience-mapper", + "config": { + "access.token.claim": "true" + } + } + }, + "role": { + "id": "manager", + "name": "manager", + "description": "Has limited access to all resources" + } + } + }, + + "client": { + "protocol": "openid-connect", + "clientId": "emr", + "name": "EMR user", + "description": "", + "publicClient": false, + "authorizationServicesEnabled": false, + "serviceAccountsEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "standardFlowEnabled": true, + "frontchannelLogout": true, + "alwaysDisplayInConsole": false, + "attributes": { + "oauth2.device.authorization.grant.enabled": false, + "oidc.ciba.grant.enabled": false + } + }, + "groups": { + "fhirUser": {} + }, + "defaultGroup": "fhir-user-group", + "defaultUser": { + "username": "fhirUser", + "firstName": "FHIR", + "lastName": "User", + "email": "fhir@jembi.org", + "emailVerified": false, + "enabled": true, + "groups": ["fhirUser"] + }, + "resetPassword": { + "temporary": false, + "type": "password", + "value": "dev_password_only" + } +} diff --git a/fhir-info-gateway/importer/update-keycloak-config.js b/fhir-info-gateway/importer/update-keycloak-config.js new file mode 100644 index 00000000..6c84bc18 --- /dev/null +++ b/fhir-info-gateway/importer/update-keycloak-config.js @@ -0,0 +1,463 @@ +const axios = require("axios"); +const fs = require("fs"); + +// Load the JSON payload +const payload = require("./keycloak-config.json"); +const { get } = require("http"); + +const serverUrl = + process.env.KEYCLOAK_SERVER_URL || "http://192.168.100.57:9088"; +const adminUser = process.env.KEYCLOAK_ADMIN_USER || "admin"; +const adminPassword = + process.env.KEYCLOAK_ADMIN_PASSWORD || "dev_password_only"; +const adminClientId = process.env.KEYCLOAK_ADMIN_CLIENT_ID || "admin-cli"; +const realm = process.env.KEYCLOAK_REALM || "platform-realm"; +const serviceAccountUser = + process.env.KEYCLOAK_SERVICE_ACCOUNT_USER || "service-account"; // Add service account user + +// Function definitions +async function getAdminToken( + keycloakBaseUrl, + realm, + clientId, + adminUser, + adminPassword +) { + try { + const tokenResponse = await axios.post( + `${keycloakBaseUrl}/realms/master/protocol/openid-connect/token`, + new URLSearchParams({ + grant_type: "password", + client_id: clientId, + username: adminUser, + password: adminPassword, + }), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + } + ); + + return tokenResponse.data.access_token; + } catch (error) { + console.error( + "Error fetching admin token:", + error.response ? error.response.data : error.message + ); + throw error; + } +} + +async function getRoleByName(roleName, keycloakBaseUrl, realm, adminToken) { + try { + const response = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/roles`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + + const role = response.data.find((r) => r.name === roleName); + return role ? role.id : null; + } catch (error) { + console.error( + "Error fetching role by name:", + error.response ? error.response.data : error.message + ); + throw error; + } +} + +async function getOrCreateClient(client, keycloakBaseUrl, realm, adminToken) { + try { + let clientResponse = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/clients?clientId=${client.clientId}`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + + if (clientResponse.data.length > 0) { + // Client exists, update it + const clientId = clientResponse.data[0].id; + await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/clients/${clientId}`, + client, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Updated client: ${client.clientId}`); + } else { + // Client does not exist, create a new one + clientResponse = await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/clients`, + client, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Created client: ${client.clientId}`); + } + return clientResponse.data; + } catch (error) { + console.error( + "Error creating or updating client:", + error.response ? error.response.data : error.message + ); + //throw error; + } +} + +async function processKeycloakPayload( + payload, + keycloakBaseUrl, + realm, + adminToken +) { + const { clientScopes, defaultUser, client, defaultGroup, resetPassword } = + payload; + + if (!clientScopes) { + throw new Error("clientScopes is not defined in the payload"); + } + + await Promise.all( + Object.entries(clientScopes).map(async ([scopeName, scope]) => { + if (!scope) { + console.error(`Scope is undefined for scopeName: ${scopeName}`); + return; + } + + console.log(`Processing scope: ${scopeName}`); + + const { role } = scope; + + const { name, description } = role; + + let roleId; + + try { + const clientScopeResponse = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/client-scopes`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + }, + } + ); + + let clientScope = clientScopeResponse.data.find( + (cs) => cs.name === scopeName + ); + + if (!clientScope) { + // Client scope does not exist, create a new one + const newClientScopeResponse = await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/client-scopes`, + scope, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + clientScope = newClientScopeResponse.data; + } + + if (!clientScope || !clientScope.id) { + throw new Error(`Client scope ${scopeName} does not have a valid ID`); + } else { + // Map scopes to the client + const clientResponse = await getOrCreateClient( + client, + keycloakBaseUrl, + realm, + adminToken + ); + await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/clients/${clientResponse[0].id}/default-client-scopes/${clientScope.id}`, + {}, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + } + roleId = await getRoleByName(name, keycloakBaseUrl, realm, adminToken); + // Map the created role to the client scope + await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/client-scopes/${clientScope.id}/scope-mappings/realm`, + + [{ id: roleId, name }], + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Mapped role ${name} to client scope ${scopeName}`); + } catch (error) { + console.error("Error processing scope:", error); + } + }) + ); + + // Create or update the service-account user + let userResponse, user, createdgroupResponse; + try { + let groupResponse = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/groups?search=${defaultGroup}`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + let groupId = ""; + if (groupResponse.data.length > 0) { + // Group exists, update it + groupId = groupResponse.data[0].id; + createdgroupResponse = await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/groups/${groupId}`, + { + name: defaultGroup, + }, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + } else { + // Group does not exist, create a new one + createdgroupResponse = await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/groups`, + { + name: defaultGroup, + }, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + let createdGroup = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/groups?search=${defaultGroup}`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Created group: `, createdGroup); + groupId = createdGroup.data[0].id; + } + + const createdGroup = createdgroupResponse.data[0]; + const usersResponse = await axios.get( + `${keycloakBaseUrl}/admin/realms/${realm}/users`, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + + const users = usersResponse.data; + user = users.find((u) => u.username === defaultUser.username.toLowerCase()); + + if (user) { + // User exists, update it + const userId = user.id; + userResponse = await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/users/${userId}`, + defaultUser, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Updated user: ${defaultUser.username}`); + } else { + // User does not exist, create a new one + userResponse = await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/users`, + defaultUser, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Created user: ${defaultUser.username}`); + } + + const createdUser = userResponse.data; + console.log("here", user); + // Reset the password + const newPass = await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/users/${ + userResponse.id ? userResponse.id : user.id + }/reset-password`, + resetPassword, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Reset password for user ${createdUser}`, newPass.data); + // Step 5: Add service-account user to the group + await axios.put( + `${keycloakBaseUrl}/admin/realms/${realm}/users/${ + createdUser.id ? createdUser.id : user.id + }/groups/${groupId}`, + {}, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Added ${createdUser} to group ${createdgroupResponse}`); + const uniqueRolesArray = await getUniqueRolesArray(payload); + for (const role of uniqueRolesArray) { + const roleID = await getRoleByName( + role.name, + keycloakBaseUrl, + realm, + adminToken + ); + console.log(roleID); + const roleMapping = await axios.post( + `${keycloakBaseUrl}/admin/realms/${realm}/groups/${groupId}/role-mappings/realm`, + [ + { + id: roleID, + clientRole: false, + composite: false, + containerId: realm, + name: role.name, + description: role.description, + }, + ], + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Added role mapping to group ${roleMapping}`, role); + } + } catch (error) { + console.error( + "Error creating or updating user:", + error.response ? error.response.data : error.message + ); + throw error; + } +} +async function getUniqueRolesArray(payload) { + const rolesSet = new Set(); + const { clientScopes } = payload; + + for (const key in clientScopes) { + if (clientScopes[key].role) { + rolesSet.add(JSON.stringify(clientScopes[key].role)); + } + } + + // Convert Set to Array and parse back to objects + const uniqueRolesArray = Array.from(rolesSet).map((role) => JSON.parse(role)); + return uniqueRolesArray; +} +// Call the function and handle the result +async function main() { + try { + const adminToken = await getAdminToken( + serverUrl, + realm, + adminClientId, + adminUser, + adminPassword + ); + const client = payload.client; + const createorupdateClient = await getOrCreateClient( + client, + serverUrl, + realm, + adminToken + ); + console.log(createorupdateClient); + const uniqueRolesArray = await getUniqueRolesArray(payload); + + for (const role of uniqueRolesArray) { + const { name } = role; + let roleId = await getRoleByName(name, serverUrl, realm, adminToken); + + if (roleId) { + // Role exists, update it + await axios.put( + `${serverUrl}/admin/realms/${realm}/roles-by-id/${roleId}`, + role, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + console.log(`Updated role: ${name}`); + } else { + // Role does not exist, create a new one + const roleResponse = await axios.post( + `${serverUrl}/admin/realms/${realm}/roles`, + role, + { + headers: { + Authorization: `Bearer ${adminToken}`, + "Content-Type": "application/json", + }, + } + ); + roleId = roleResponse.data.id; + console.log(`Created role: ${name}`); + } + } + await processKeycloakPayload(payload, serverUrl, realm, adminToken); + console.log("Keycloak payload processed successfully"); + } catch (error) { + console.error("Error processing Keycloak payload:", error); + } +} + +main(); diff --git a/fhir-info-gateway/package-metadata.json b/fhir-info-gateway/package-metadata.json new file mode 100644 index 00000000..4e11abc3 --- /dev/null +++ b/fhir-info-gateway/package-metadata.json @@ -0,0 +1,25 @@ +{ + "id": "fhir-info-gateway", + "name": "FHIR Info Gateway", + "description": "Implement the FHIR Info Gateway as a platform package which sits between the OpenHIM and MPI Mediator and any other direct FHIR access", + "type": "infrastructure", + "version": "0.0.1", + "dependencies": ["mpi-mediator"], + "environmentVariables": { + "GATEWAY_MPI_PROXY_URL": "http://mpi-mediator:3000/fhir", + "ACCESS_CHECKER": "patient", + "RUN_MODE": "DEV", + "FHIR_INFO_GATEWAY_IMAGE": "jembi/fhir-info-gateway:scope-checker", + "BACKEND_TYPE": "HAPI", + "KC_API_URL": "http://identity-access-manager-keycloak:9088", + "KC_REALM_NAME": "platform-realm", + "KC_ADMIN_PASSWORD": "dev_password_only", + "KC_ADMIN_USERNAME": "admin", + "FHIR_INFO_GATEWAY_INSTANCES": "1", + "FHIR_INFO_GATEWAY_MAX_REPLICAS_PER_NODE": "1", + "FHIR_INFO_GATEWAY_CPU_LIMIT": "0", + "FHIR_INFO_GATEWAY_MEMORY_LIMIT": "2G", + "FHIR_INFO_GATEWAY_CPU_RESERVE": "0.05", + "FHIR_INFO_GATEWAY_MEMORY_RESERVE": "500M" + } +} diff --git a/fhir-info-gateway/swarm.sh b/fhir-info-gateway/swarm.sh new file mode 100644 index 00000000..9dee3de1 --- /dev/null +++ b/fhir-info-gateway/swarm.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +declare ACTION="" +declare MODE="" +declare COMPOSE_FILE_PATH="" +declare UTILS_PATH="" +declare STACK="fhir-info-gateway" + +function init_vars() { + ACTION=$1 + MODE=$2 + + COMPOSE_FILE_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" || { + echo "Failed to change directory" + exit 1 + } + pwd -P + ) + + UTILS_PATH="${COMPOSE_FILE_PATH}/../utils" + + + readonly ACTION + readonly MODE + readonly COMPOSE_FILE_PATH + readonly UTILS_PATH + readonly STACK +} + +# shellcheck disable=SC1091 +function import_sources() { + source "${UTILS_PATH}/docker-utils.sh" + source "${UTILS_PATH}/log.sh" +} + +function initialize_package() { + local package_dev_compose_filename="" + if [[ "${MODE}" == "dev" ]]; then + log info "Running package in DEV mode" + package_dev_compose_filename="docker-compose.dev.yml" + else + log info "Running package in PROD mode" + fi + + ( + docker::deploy_service $STACK "${COMPOSE_FILE_PATH}" "docker-compose.yml" "$package_dev_compose_filename" "importer/docker-compose-smart_keycloak.yml" + + if [[ "${ACTION}" == "init" ]]; then + docker::deploy_config_importer $STACK "$COMPOSE_FILE_PATH/importer/docker-compose.config.yml" "update-keycloak-config" "fhirinfo" + fi + docker service rm fhir-info-gateway_smart-config >> /dev/null 2>&1 + ) || { + log error "Failed to deploy package" + exit 1 + } + +} + +function destroy_package() { + docker::stack_destroy "$STACK" +} + +main() { + init_vars "$@" + import_sources + + if [[ "${ACTION}" == "init" ]] || [[ "${ACTION}" == "up" ]]; then + log info "Running package in Single node mode" + + initialize_package + elif [[ "${ACTION}" == "down" ]]; then + log info "Scaling down package" + + docker::scale_services "$STACK" 0 + elif [[ "${ACTION}" == "destroy" ]]; then + log info "Destroying package" + + destroy_package + else + log error "Valid options are: init, up, down, or destroy" + fi +} + +main "$@" diff --git a/interoperability-layer-openhim/docker-compose.yml b/interoperability-layer-openhim/docker-compose.yml index 59a1d95e..684bb005 100644 --- a/interoperability-layer-openhim/docker-compose.yml +++ b/interoperability-layer-openhim/docker-compose.yml @@ -25,6 +25,10 @@ services: - api_openid_clientId=${KC_OPENHIM_CLIENT_ID} - api_openid_clientSecret=${KC_OPENHIM_CLIENT_SECRET} - openhimConsoleBaseUrl=${OPENHIM_CONSOLE_BASE_URL} + - authentication_enableJWTAuthentication=true + - authentication_jwt_jwksUri=${KC_API_URL}/realms/${KC_REALM_NAME}/protocol/openid-connect/certs + - authentication_jwt_algorithms=RS256 + - authentication_jwt_issuer=${KC_FRONTEND_URL}/realms/${KC_REALM_NAME} deploy: replicas: ${OPENHIM_CORE_INSTANCES} placement: