diff --git a/docs/install-candig.md b/docs/install-candig.md
index b411a9f2c..fd3bdaf69 100644
--- a/docs/install-candig.md
+++ b/docs/install-candig.md
@@ -354,8 +354,6 @@ cp -i etc/env/example.env .env
LOCAL_IP_ADDR=xxx.xx.x.x
# change OS
VENV_OS=arm64mac
-# change keycloak
-KEYCLOAK_BASE_IMAGE=quay.io/c3genomics/keycloak:${KEYCLOAK_VERSION}.arm64
```
Edit /etc/hosts on the machine (`sudo nano /etc/hosts`):
diff --git a/etc/env/example.env b/etc/env/example.env
index da1b74be9..9c758412d 100644
--- a/etc/env/example.env
+++ b/etc/env/example.env
@@ -175,23 +175,18 @@ CACHE_TIME=0
# keycloak service
-KEYCLOAK_VERSION=16.1.1
-#KEYCLOAK_BASE_IMAGE=quay.io/c3genomics/keycloak:${KEYCLOAK_VERSION}.arm64
-KEYCLOAK_BASE_IMAGE=quay.io/keycloak/keycloak:${KEYCLOAK_VERSION}
+KEYCLOAK_VERSION=24.0.0
KEYCLOAK_REALM=candig
KEYCLOAK_CLIENT_ID=local_candig
KEYCLOAK_LOGIN_REDIRECT_PATH=/auth/login
KEYCLOAK_PORT=8080
-KEYCLOAK_CONTAINER_PORT=8080
-KEYCLOAK_HOST=0.0.0.0
KEYCLOAK_PUBLIC_PROTO=http
KEYCLOAK_PRIVATE_PROTO=http
KEYCLOAK_ENABLE_PROXY=false
KEYCLOAK_PUBLIC_URL=${KEYCLOAK_PUBLIC_PROTO}://${CANDIG_AUTH_DOMAIN}:${KEYCLOAK_PORT}
KEYCLOAK_PUBLIC_URL_PROD=${KEYCLOAK_PUBLIC_PROTO}://${CANDIG_AUTH_DOMAIN}
-KEYCLOAK_PRIVATE_URL=${KEYCLOAK_PRIVATE_PROTO}://${CANDIG_AUTH_DOMAIN}:${KEYCLOAK_CONTAINER_PORT}
+KEYCLOAK_PRIVATE_URL=${KEYCLOAK_PRIVATE_PROTO}://${CANDIG_AUTH_DOMAIN}:${KEYCLOAK_PORT}
KEYCLOAK_REALM_URL=${KEYCLOAK_PUBLIC_URL}/auth/realms/${KEYCLOAK_REALM}
-
KEYCLOAK_GENERATE_TEST_USER=1
# query service
diff --git a/lib/keycloak/Dockerfile b/lib/keycloak/Dockerfile
deleted file mode 100644
index 34290e575..000000000
--- a/lib/keycloak/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-ARG BASE_IMAGE
-FROM ${BASE_IMAGE}
-
-COPY ./configuration_templates/* /opt/jboss/keycloak/standalone/
\ No newline at end of file
diff --git a/lib/keycloak/client_setup.sh b/lib/keycloak/client_setup.sh
new file mode 100644
index 000000000..734d37a11
--- /dev/null
+++ b/lib/keycloak/client_setup.sh
@@ -0,0 +1,38 @@
+# This script creates and configures a client within a Keycloak realm
+
+echo
+echo -e "${BLUE}Creating client: $KEYCLOAK_CLIENT_ID${DEFAULT}"
+
+CREATE_OUTPUT=$(KCADM create clients -r "$KEYCLOAK_REALM" \
+ -s clientId="$KEYCLOAK_CLIENT_ID" \
+ -s enabled=true \
+ -s protocol=openid-connect \
+ -s publicClient=false \
+ -s clientAuthenticatorType=client-secret \
+ -s standardFlowEnabled=true \
+ -s directAccessGrantsEnabled=true \
+ -s 'redirectUris=["'"$TYK_LOGIN_TARGET_URL$KEYCLOAK_LOGIN_REDIRECT_PATH"'"]' \
+ -s 'webOrigins=["'"$TYK_LOGIN_TARGET_URL"'"]' 2>&1)
+# uncomment the line beblow to see the output
+# echo $CREATE_OUTPUT
+
+# Extract the client ID from the output
+CLIENT_ID=$(echo $CREATE_OUTPUT | grep -oE '[0-9a-fA-F-]{36}')
+
+# Create client scopes
+SCOPE_NAME="${KEYCLOAK_CLIENT_ID}-audience"
+CREATE_OUTPUT=$(KCADM create clients/$CLIENT_ID/protocol-mappers/models -r $KEYCLOAK_REALM \
+ -s name=$SCOPE_NAME \
+ -s protocol=openid-connect \
+ -s protocolMapper=oidc-audience-mapper \
+ -s config="{\"included.client.audience\" : \"$KEYCLOAK_CLIENT_ID\",\"id.token.claim\" : \"true\",\"access.token.claim\" : \"true\"}" 2>&1)
+# uncomment the line beblow to see the output
+# echo $CREATE_OUTPUT
+
+# EXPORT: Get the client secret and save it to secrets
+CLIENT_SECRET=$(KCADM get clients/"$CLIENT_ID"/client-secret -r "$KEYCLOAK_REALM" | jq -r '.value')
+echo "$CLIENT_SECRET" > tmp/secrets/keycloak-client-$KEYCLOAK_CLIENT_ID-secret
+
+# EXPORT: Encode the Keycloak client ID in base64 and save it to secrets
+KEYCLOAK_CLIENT_ID_64=$(echo -n "${KEYCLOAK_CLIENT_ID}" | base64)
+echo "$KEYCLOAK_CLIENT_ID_64" > "tmp/secrets/keycloak-client-${KEYCLOAK_CLIENT_ID}-id-64"
diff --git a/lib/keycloak/configuration_templates/application-users.properties b/lib/keycloak/configuration_templates/application-users.properties
deleted file mode 100644
index 24889c3f3..000000000
--- a/lib/keycloak/configuration_templates/application-users.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Properties declaration of users for the realm 'ApplicationRealm' which is the default realm
-# for application services on a new installation.
-#
-# This includes the following protocols: remote ejb, remote jndi, web, remote jms
-#
-# Users can be added to this properties file at any time, updates after the server has started
-# will be automatically detected.
-#
-# The format of this realm is as follows: -
-# username=HEX( MD5( username ':' realm ':' password))
-#
-# A utility script is provided which can be executed from the bin folder to add the users: -
-# - Linux
-# bin/add-user.sh
-#
-# - Windows
-# bin\add-user.bat
-#
-#$REALM_NAME=ApplicationRealm$ This line is used by the add-user utility to identify the realm name already used in this file.
-#
-# The following illustrates how an admin user could be defined, this
-# is for illustration only and does not correspond to a usable password.
-#
-#admin=2a0923285184943425d1f53ddd58ec7a
diff --git a/lib/keycloak/configuration_templates/logging.properties b/lib/keycloak/configuration_templates/logging.properties
deleted file mode 100644
index 3e7aeb1bf..000000000
--- a/lib/keycloak/configuration_templates/logging.properties
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# JBoss, Home of Professional Open Source.
-# Copyright 2013, Red Hat, Inc., and individual contributors
-# as indicated by the @author tags. See the copyright.txt file in the
-# distribution for a full listing of individual contributors.
-#
-# This is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation; either version 2.1 of
-# the License, or (at your option) any later version.
-#
-# This software is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this software; if not, write to the Free
-# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-#
-
-# Additional loggers to configure (the root logger is always configured)
-loggers=com.arjuna,jacorb,org.jboss.as.config,org.apache.tomcat.util.modeler,sun.rmi,jacorb.config
-
-logger.level=INFO
-logger.handlers=CONSOLE,FILE
-
-logger.com.arjuna.level=WARN
-logger.com.arjuna.useParentHandlers=true
-
-logger.jacorb.level=WARN
-logger.jacorb.useParentHandlers=true
-
-logger.org.jboss.as.config.level=DEBUG
-logger.org.jboss.as.config.useParentHandlers=true
-
-logger.org.apache.tomcat.util.modeler.level=WARN
-logger.org.apache.tomcat.util.modeler.useParentHandlers=true
-
-logger.sun.rmi.level=WARN
-logger.sun.rmi.useParentHandlers=true
-
-logger.jacorb.config.level=ERROR
-logger.jacorb.config.useParentHandlers=true
-
-handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
-handler.CONSOLE.level=INFO
-handler.CONSOLE.formatter=COLOR-PATTERN
-handler.CONSOLE.properties=autoFlush,target
-handler.CONSOLE.autoFlush=true
-handler.CONSOLE.target=SYSTEM_OUT
-
-handler.FILE=org.jboss.logmanager.handlers.PeriodicRotatingFileHandler
-handler.FILE.level=ALL
-handler.FILE.formatter=PATTERN
-handler.FILE.properties=autoFlush,append,fileName,suffix
-handler.FILE.constructorProperties=fileName,append
-handler.FILE.autoFlush=true
-handler.FILE.append=true
-handler.FILE.fileName=${org.jboss.boot.log.file:boot.log}
-handler.FILE.suffix=.yyyy-MM-dd
-
-formatter.COLOR-PATTERN=org.jboss.logmanager.formatters.PatternFormatter
-formatter.COLOR-PATTERN.properties=pattern
-formatter.COLOR-PATTERN.pattern=%K{level}%d{HH\:mm\:ss,SSS} %-5p [%c] (%t) %s%E%n
-
-formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
-formatter.PATTERN.properties=pattern
-formatter.PATTERN.pattern=%d{yyyy-MM-dd HH\:mm\:ss,SSS} %-5p [%c] (%t) %s%E%n
diff --git a/lib/keycloak/configuration_templates/mgmt-users.properties b/lib/keycloak/configuration_templates/mgmt-users.properties
deleted file mode 100644
index 1eb477657..000000000
--- a/lib/keycloak/configuration_templates/mgmt-users.properties
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Properties declaration of users for the realm 'ManagementRealm' which is the default realm
-# for new installations. Further authentication mechanism can be configured
-# as part of the in standalone.xml.
-#
-# Users can be added to this properties file at any time, updates after the server has started
-# will be automatically detected.
-#
-# By default the properties realm expects the entries to be in the format: -
-# username=HEX( MD5( username ':' realm ':' password))
-#
-# A utility script is provided which can be executed from the bin folder to add the users: -
-# - Linux
-# bin/add-user.sh
-#
-# - Windows
-# bin\add-user.bat
-#
-#$REALM_NAME=ManagementRealm$ This line is used by the add-user utility to identify the realm name already used in this file.
-#
-# On start-up the server will also automatically add a user $local - this user is specifically
-# for local tools running against this AS installation.
-#
-# The following illustrates how an admin user could be defined, this
-# is for illustration only and does not correspond to a usable password.
-#
-#admin=2a0923285184943425d1f53ddd58ec7a
diff --git a/lib/keycloak/configuration_templates/standalone-ha.xml b/lib/keycloak/configuration_templates/standalone-ha.xml
deleted file mode 100644
index 35da18455..000000000
--- a/lib/keycloak/configuration_templates/standalone-ha.xml
+++ /dev/null
@@ -1,636 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
- h2
-
- sa
- sa
-
-
-
- jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE
- h2
-
- sa
- sa
-
-
-
-
- org.h2.jdbcx.JdbcDataSource
-
-
-
-
-
-
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- auth
-
-
- classpath:${jboss.home.dir}/providers/*
-
-
- master
- 900
-
- 2592000
- true
- true
- ${env.KEYCLOAK_WELCOME_THEME:keycloak}
- ${jboss.home.dir}/themes
-
-
-
-
-
-
-
-
-
-
-
-
- jpa
-
-
- basic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- default
-
-
-
-
-
-
-
- ${keycloak.jta.lookup.provider:jboss}
-
-
-
-
-
-
-
-
-
-
- ${keycloak.x509cert.lookup.provider:default}
-
-
-
- ${keycloak.hostname.provider:request}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/keycloak/configuration_templates/standalone.xml b/lib/keycloak/configuration_templates/standalone.xml
deleted file mode 100644
index 461ac9497..000000000
--- a/lib/keycloak/configuration_templates/standalone.xml
+++ /dev/null
@@ -1,573 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
- h2
-
- sa
- sa
-
-
-
- jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE
- h2
-
- sa
- sa
-
-
-
-
- org.h2.jdbcx.JdbcDataSource
-
-
-
-
-
-
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- auth
-
- classpath:${jboss.home.dir}/providers/*
-
- master
- 900
-
- 2592000
- true
- true
- ${jboss.home.dir}/themes
-
-
-
-
-
-
-
-
-
-
-
-
- jpa
-
-
- basic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- default
-
-
-
-
-
-
-
- ${keycloak.jta.lookup.provider:jboss}
-
-
-
-
-
-
-
-
-
-
- ${keycloak.x509cert.lookup.provider:default}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/keycloak/credential_loader.sh b/lib/keycloak/credential_loader.sh
new file mode 100755
index 000000000..747081ddf
--- /dev/null
+++ b/lib/keycloak/credential_loader.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Load credentials from secrets
+export KEYCLOAK_ADMIN=$(< /run/secrets/keycloak-admin-user)
+export KEYCLOAK_ADMIN_PASSWORD=$(< /run/secrets/keycloak-admin-password)
+
+exec /opt/keycloak/bin/kc.sh "$@"
\ No newline at end of file
diff --git a/lib/keycloak/docker-compose.yml b/lib/keycloak/docker-compose.yml
index 76cf005f2..b85f7ed86 100644
--- a/lib/keycloak/docker-compose.yml
+++ b/lib/keycloak/docker-compose.yml
@@ -1,31 +1,30 @@
services:
keycloak:
- build:
- context: ${PWD}/lib/keycloak
- args:
- #- BASE_IMAGE=candig/keycloak:${KEYCLOAK_VERSION}
- - BASE_IMAGE=${KEYCLOAK_BASE_IMAGE}
+ image: keycloak/keycloak:${KEYCLOAK_VERSION}
labels:
- "candigv2=keycloak"
- command: [ "-b", "${KEYCLOAK_HOST}", "-Dkeycloak.migration.strategy=IGNORE_EXISTING" ]
- ports:
- - "${KEYCLOAK_CONTAINER_PORT}:8080"
- extra_hosts:
- - "${CANDIG_DOMAIN}:${LOCAL_IP_ADDR}"
volumes:
- - keycloak-data:/opt/jboss/keycloak/standalone
+ - ${PWD}/lib/keycloak/credential_loader.sh:/opt/keycloak/credential_loader.sh
+ - keycloak-data:/opt/keycloak/data
environment:
- - KEYCLOAK_USER_FILE=/run/secrets/keycloak-admin-user
- - KEYCLOAK_PASSWORD_FILE=/run/secrets/keycloak-admin-password
- - PROXY_ADDRESS_FORWARDING=${KEYCLOAK_ENABLE_PROXY}
- #- KEYCLOAK_FRONTEND_URL=${KEYCLOAK_PUBLIC_URL_PROD}/auth
+ KC_HOSTNAME: ${CANDIG_DOMAIN}
+ PROXY_ADDRESS_FORWARDING: ${KEYCLOAK_ENABLE_PROXY}
+ KC_HTTP_RELATIVE_PATH: /auth
+ KC_HEALTH_ENABLED: true
+ KC_METRICS_ENABLED: true
+ QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY: true
+ # KEYCLOAK_FRONTEND_URL: ${KEYCLOAK_PUBLIC_URL_PROD}/auth
secrets:
- - source: keycloak-admin-user
- target: /run/secrets/keycloak-admin-user
- - source: keycloak-admin-password
- target: /run/secrets/keycloak-admin-password
+ - keycloak-admin-user
+ - keycloak-admin-password
+ ports:
+ - "${KEYCLOAK_PORT}:8080"
+ extra_hosts:
+ - "${CANDIG_DOMAIN}:${LOCAL_IP_ADDR}"
+ entrypoint: ["/bin/sh", "-c"]
+ command: ["/opt/keycloak/credential_loader.sh start-dev"]
healthcheck:
- test: [ "CMD", "curl", "-f", "http://0.0.0.0:8080" ]
+ test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e \"GET /auth/health HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n\" >&3;grep \"HTTP/1.1 200 OK\" <&3"]
interval: 30s
- timeout: 20s
- retries: 3
+ timeout: 10s
+ retries: 3
\ No newline at end of file
diff --git a/lib/keycloak/keycloak_setup.sh b/lib/keycloak/keycloak_setup.sh
index 0cbd0bc3a..7dbec20d4 100644
--- a/lib/keycloak/keycloak_setup.sh
+++ b/lib/keycloak/keycloak_setup.sh
@@ -1,271 +1,61 @@
#!/usr/bin/env bash
-set -Euo pipefail
+# Entrypoint into Keycloak. It uses Keycloak Admin CLI (KCADM)
+# to setup realms, clients, and users for candig services.
-LOGFILE=$PWD/tmp/progress.txt
+set -euo pipefail
+# Terminal colors
+RED='\033[0;31m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+GREEN='\033[0;32m'
+DEFAULT='\033[0m'
-# Verify if keycloak container is running
-KEYCLOAK_CONTAINERS=$(echo "$(docker ps | grep keycloak | wc -l)")
-echo "Number of keycloak containers running: ${KEYCLOAK_CONTAINERS}" | tee -a $LOGFILE
-if [[ $KEYCLOAK_CONTAINERS -eq 1 ]]; then
- echo "Waiting for keycloak to start" | tee -a $LOGFILE
- while ! docker logs --tail 1000 "$(docker ps | grep keycloak | awk '{print $1}')" | grep "Undertow HTTPS listener https listening on 0.0.0.0"; do sleep 1; done
- echo "Keycloak container started." | tee -a $LOGFILE
-else
- echo "Too many (or too few) keycloak containers! Shut down all keycloak containers and then re-run make compose-keycloak."
- exit 1
-fi
-
-KEYCLOAK_CONTAINER=$(docker ps | grep keycloak | awk '{print $1}')
-add_user() {
- # CANDIG_AUTH_DOMAIN is the name of the keycloak server inside the compose network
- local username=$1
- local password=$2
-
- local JSON=' {
- "username": "'${username}'",
- "email": "'${username}'@test.ca",
- "enabled": true,
- "access": {
- "manageGroupMembership": true,
- "view": true,
- "mapRoles": true,
- "impersonate": true,
- "manage": true
- }
- }'
-
- user_id=`curl --stderr - \
- -i -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X POST -H "Content-Type: application/json" -d "${JSON}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${KEYCLOAK_REALM}/users" -k | grep -i "Location:" \
- | sed -E s/.*users.\([a-z0-9-]+\).*/\\\1/`
-
- echo "Created user ${user_id}" | tee -a $LOGFILE
-
- local password_json=' {
- "type": "rawPassword",
- "value": "'${password}'"
- }'
-
- curl \
- -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X PUT -H "Content-Type: application/json" -d "${password_json}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${KEYCLOAK_REALM}/users/${user_id}/reset-password" -k
-}
-
-get_token() {
- local keycloak_admin_user=$(cat tmp/secrets/keycloak-admin-user)
- local keycloak_admin_password=$(cat tmp/secrets/keycloak-admin-password)
-
- local BID=$(curl \
- -d "client_id=admin-cli" \
- -d "username=${keycloak_admin_user}" \
- -d "password=${keycloak_admin_password}" \
- -d "grant_type=password" \
- "${KEYCLOAK_PUBLIC_URL}/auth/realms/master/protocol/openid-connect/token" -k 2>/dev/null)
- # TODO: security issue fix this, -k flag above ignores cert, even if the url is https
-
- echo ${BID} | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["access_token"])'
-}
-
-set_realm() {
- local realm=$1
-
- local JSON='{
- "realm": "'${realm}'",
- "enabled": true
- }'
-
- local RESULT=$(curl \
- -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X POST -H "Content-Type: application/json" -d "${JSON}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms" -k)
-
- echo ${RESULT} | grep errorMessage
- if [[ $? == 0 ]]; then
- echo "Realm cannot be set: ${RESULT}"
+handle_error() {
+ echo -e "🚨🚨🚨 ${RED}AN ERROR OCCURRED DURING KEYCLOAK SETUP PROCESS${DEFAULT} 🚨🚨🚨"
exit 1
- fi
-}
-
-get_realm() {
- local realm=$1
-
- curl \
- -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${realm}" -k | jq .
-}
-
-get_realm_clients() {
- local realm=$1
-
- curl \
- -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${realm}/clients" -k | jq -S .
-}
-
-set_client() {
- local realm=$1
- local client=$2
- local redirect=$3
-
- # add client scope with protocol mappers
- scope_json='{
- "name": "'${realm}'",
- "protocol": "openid-connect",
- "attributes": {
- "include.in.token.scope": "true",
- "display.on.consent.screen": "true"
- },
- "protocolMappers": [
- {
- "name": "'${client}'-audience",
- "protocol": "openid-connect",
- "protocolMapper": "oidc-audience-mapper",
- "consentRequired": false,
- "config": {
- "included.client.audience": "'${client}'",
- "id.token.claim": "true",
- "access.token.claim": "true"
- }
- }
- ]
- }'
-
- new_scope=`curl --stderr - \
- -i -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X POST -H "Content-Type: application/json" -d "${scope_json}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${realm}/client-scopes" -k | grep -i "Location:" \
- | sed -E s/.*client-scopes.\([a-z0-9-]+\).*/\\\\1/`
-
- echo "Created client scope ${new_scope}" | tee -a $LOGFILE
-
- # Will add / to listen only if it is present
-
- local client_json='{
- "clientId": "'"${client}"'",
- "enabled": true,
- "protocol": "openid-connect",
- "implicitFlowEnabled": true,
- "standardFlowEnabled": true,
- "publicClient": false,
- "redirectUris": [
- "'${TYK_LOGIN_TARGET_URL}${redirect}'"
- ],
- "attributes": {
- "saml.assertion.signature": "false",
- "saml.authnstatement": "false",
- "saml.client.signature": "false",
- "saml.encrypt": "false",
- "saml.force.post.binding": "false",
- "saml.multivalued.roles": "false",
- "saml.onetimeuse.condition": "false",
- "saml.server.signature": "false",
- "saml.server.signature.keyinfo.ext": "false",
- "saml_force_name_id_format": "false"
- },
- "defaultClientScopes": [
- "web-origins",
- "roles",
- "profile",
- "'${realm}'",
- "email"
- ]
- }'
-
- new_client=`curl --stderr - \
- -i -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X POST -H "Content-Type: application/json" -d "${client_json}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${realm}/clients" -k | grep -i "Location:" \
- | sed -E s/.*clients.\([a-z0-9-]+\).*/\\\\1/`
- # TODO: security issue fix this, -k flag above ignores cert, even if the url is https
-
- echo "Created client ${new_client}" | tee -a $LOGFILE
}
-set_role() {
- local realm=$1
- local client=$2
- local role=$3
-
- local JSON='{
- "name": "'${role}'"
- }'
-
- role_id=`curl \
- -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- -X POST -H "Content-Type: application/json" -d "${JSON}" \
- "${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${realm}/roles" -k`
-
- echo "Created role ${role}" | tee -a $LOGFILE
-}
-
-get_secret() {
- id=$(curl -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- ${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${KEYCLOAK_REALM}/clients -k 2>/dev/null |
- python3 -c 'import json,sys;obj=json.load(sys.stdin); print([l["id"] for l in obj if l["clientId"] ==
- "'"$KEYCLOAK_CLIENT_ID"'" ][0])')
-
- curl -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
- ${KEYCLOAK_PUBLIC_URL}/auth/admin/realms/${KEYCLOAK_REALM}/clients/$id/client-secret -k 2>/dev/null |
- python3 -c 'import json,sys;obj=json.load(sys.stdin); print(obj["value"])'
-}
-
-get_public_key() {
- curl \
- ${KEYCLOAK_PUBLIC_URL}/auth/realms/${KEYCLOAK_REALM} -k 2>/dev/null |
- python3 -c 'import json,sys;obj=json.load(sys.stdin); print(obj["public_key"])'
-}
-
-# SCRIPT START
-
-echo " Starting setup calls to keycloak" | tee -a $LOGFILE
-
-echo "Getting keycloak token" | tee -a $LOGFILE
-KEYCLOAK_TOKEN=$(get_token)
-
-echo "Creating Realm ${KEYCLOAK_REALM}" | tee -a $LOGFILE
-set_realm ${KEYCLOAK_REALM}
-
-echo "Setting client ${KEYCLOAK_CLIENT_ID} in base64" | tee -a $LOGFILE
-export KEYCLOAK_CLIENT_ID_64=$(echo -n ${KEYCLOAK_CLIENT_ID} | base64)
-echo $KEYCLOAK_CLIENT_ID_64 > tmp/secrets/keycloak-client-$KEYCLOAK_CLIENT_ID-id-64
-
-echo "Remove ports on prod" | tee -a $LOGFILE
-if [[ ${KEYCLOAK_PUBLIC_URL} == *":443"* ]]; then
- echo "option 1";
- export KEYCLOAK_PUBLIC_URL_PROD=$(echo ${KEYCLOAK_PUBLIC_URL} | sed -e 's/\(:443\)\$//g')
-elif [[ ${KEYCLOAK_PUBLIC_URL} == *":80"* ]]; then
- echo "option 2";
- export KEYCLOAK_PUBLIC_URL_PROD=$(echo ${KEYCLOAK_PUBLIC_URL} | sed -e 's/\(:80\)\$//g')
-else
- echo "option 3";
- export KEYCLOAK_PUBLIC_URL_PROD=$KEYCLOAK_PUBLIC_URL
-fi ;
-
-echo "Setting client ${KEYCLOAK_CLIENT_ID}" | tee -a $LOGFILE
-set_client "${KEYCLOAK_REALM}" "${KEYCLOAK_CLIENT_ID}" "${KEYCLOAK_LOGIN_REDIRECT_PATH}"
-
-echo "Getting keycloak secret" | tee -a $LOGFILE
-KEYCLOAK_SECRET_RESPONSE=$(get_secret ${KEYCLOAK_REALM})
-export KEYCLOAK_SECRET=$KEYCLOAK_SECRET_RESPONSE
-echo $KEYCLOAK_SECRET > tmp/secrets/keycloak-client-$KEYCLOAK_CLIENT_ID-secret | tee -a $LOGFILE
-
-echo "Getting keycloak public key" | tee -a $LOGFILE
-KEYCLOAK_PUBLIC_KEY_RESPONSE=$(get_public_key ${KEYCLOAK_REALM})
-export KEYCLOAK_PUBLIC_KEY=$KEYCLOAK_PUBLIC_KEY_RESPONSE
-echo "Retrieved keycloak public key as ${KEYCLOAK_PUBLIC_KEY}" | tee -a $LOGFILE
-echo $KEYCLOAK_PUBLIC_KEY > tmp/secrets/keycloak-public-key | tee -a $LOGFILE
-
-if [[ ${KEYCLOAK_GENERATE_TEST_USER} == 1 ]]; then
- echo "Adding test user" | tee -a $LOGFILE
- add_user "$(sed s/@test.ca// tmp/secrets/keycloak-test-user)" "$(cat tmp/secrets/keycloak-test-user-password)"
- add_user "$(sed s/@test.ca// tmp/secrets/keycloak-test-user2)" "$(cat tmp/secrets/keycloak-test-user2-password)"
- add_user "$(sed s/@test.ca// tmp/secrets/keycloak-test-site-admin)" "$(cat tmp/secrets/keycloak-test-site-admin-password)"
+# Trap ERR signal to call handle_error function
+trap handle_error ERR
+
+####################################################
+# VARIABLES CONFIGURATION #
+####################################################
+KEYCLOAK_ADMIN=$(cat tmp/secrets/keycloak-admin-user)
+KEYCLOAK_ADMIN_PASSWORD=$(cat tmp/secrets/keycloak-admin-password)
+READY_CHECK_URL="http://${CANDIG_DOMAIN}:${KEYCLOAK_PORT}/auth/health/ready"
+KC_ADMIN_URL="http://${CANDIG_DOMAIN}:${KEYCLOAK_PORT}/auth"
+#####################################################
+
+echo -e "🚧🚧🚧 ${YELLOW}KEYCLOAK SETUP BEGIN${DEFAULT} 🚧🚧🚧"
+echo -n ">> waiting for keycloak to start"
+# keycloak is booting up before it can accept any requests
+until $(curl --output /dev/null --silent --fail --head "${READY_CHECK_URL}"); do
+ printf '.'
+ sleep 1
+done
+echo -e "\n${GREEN}Keycloak is ready ✅${DEFAULT}"
+
+# Get the Keycloak container ID
+KEYCLOAK_CONTAINER_ID=$(docker ps | grep keycloak/keycloak | awk '{print $1}')
+
+# Define the KCADM function to run commands inside the Keycloak container
+function KCADM() {
+ docker exec "$KEYCLOAK_CONTAINER_ID" /opt/keycloak/bin/kcadm.sh "$@"
+}
+
+# authenticate as admin
+KCADM config credentials --server $KC_ADMIN_URL --user $KEYCLOAK_ADMIN --password $KEYCLOAK_ADMIN_PASSWORD --realm master
+
+# create realm
+source ./lib/keycloak/realm_setup.sh
+# create client
+source ./lib/keycloak/client_setup.sh
+# create test users
+if [ "${KEYCLOAK_GENERATE_TEST_USER}" == "1" ]; then
+ source ./lib/keycloak/user_setup.sh
fi
-echo "Waiting for keycloak to restart" | tee -a $LOGFILE
-while ! docker logs --tail 5 ${KEYCLOAK_CONTAINER} | grep "Admin console listening on http://127.0.0.1:9990"; do sleep 1; done
-echo "Keycloak setup done!" | tee -a $LOGFILE
+echo -e "🎉🎉🎉 ${GREEN}KEYCLOAK SETUP DONE!${DEFAULT} 🎉🎉🎉"
diff --git a/lib/keycloak/realm_setup.sh b/lib/keycloak/realm_setup.sh
new file mode 100644
index 000000000..300d41150
--- /dev/null
+++ b/lib/keycloak/realm_setup.sh
@@ -0,0 +1,19 @@
+# This script creates and configures a realm within Keycloak
+
+echo
+echo -e "${BLUE}Creating realm: $KEYCLOAK_REALM${DEFAULT}"
+
+CREATE_OUTPUT=$(KCADM create realms -s realm="$KEYCLOAK_REALM" -s enabled=true 2>&1)
+# uncomment the line beblow to see the output
+# echo $CREATE_OUTPUT
+
+KCADM update events/config -r ${KEYCLOAK_REALM} \
+-s adminEventsEnabled=true \
+-s adminEventsDetailsEnabled=true \
+-s eventsEnabled=true \
+-s eventsExpiration=259200 \
+-s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
+
+# EXPORT: Get the realm public key and save it to secrets
+KEYCLOAK_PUBLIC_KEY=$(curl ${KEYCLOAK_PUBLIC_URL}/auth/realms/${KEYCLOAK_REALM} -k 2>/dev/null | jq -r '.public_key')
+echo "$KEYCLOAK_PUBLIC_KEY" > tmp/secrets/keycloak-public-key
\ No newline at end of file