From 3cebb17bb3c75cba2df46c2efcf35f07187eb727 Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Fri, 27 Dec 2024 20:01:56 +1100 Subject: [PATCH] chore: update keycloak to version 26 --- Makefile | 54 +++++++++++++++---- services/api/package.json | 2 +- services/keycloak/Dockerfile | 6 +-- services/keycloak/custom-mapper/pom.xml | 8 +-- .../default-keycloak-entrypoint.sh | 5 ++ services/keycloak/entrypoints/kc-startup.sh | 8 ++- yarn.lock | 18 +++---- 7 files changed, 72 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 5897e7ff6f..daaf3e7efb 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,22 @@ UPSTREAM_TAG ?= latest # edge is the most current merged change BUILD_DEPLOY_IMAGE_TAG ?= edge +# UI_IMAGE_REPO and UI_IMAGE_TAG are an easy way to override the UI image used +# UI_IMAGE_REPO = uselagoon/ui +UI_IMAGE_TAG = pr-307 + +# SSHPORTALAPI_IMAGE_REPO and SSHPORTALAPI_IMAGE_TAG are an easy way to override the ssh portal api image used in the local stack lagoon-core +SSHPORTALAPI_IMAGE_REPO = shreddedbacon/ssh-portal-api +SSHPORTALAPI_IMAGE_TAG = latest + +# SSHTOKEN_IMAGE_REPO and SSHTOKEN_IMAGE_TAG are an easy way to override the ssh token image used in the local stack lagoon-core +SSHTOKEN_IMAGE_REPO = shreddedbacon/ssh-token +SSHTOKEN_IMAGE_TAG = latest + +# SSHPORTAL_IMAGE_REPO and SSHPORTAL_IMAGE_TAG are an easy way to override the ssh portal image used in the local stack lagoon-remote +# SSHPORTAL_IMAGE_REPO = +# SSHPORTAL_IMAGE_TAG = + # OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG and OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY # set this to a particular build image if required, defaults to nothing to consume what the chart provides OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG= @@ -346,11 +362,14 @@ logs: # Start all Lagoon Services up: ifeq ($(ARCH), darwin) - IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d + UI_IMAGE_TAG=$(UI_IMAGE_TAG) UI_IMAGE_REPO=$(UI_IMAGE_REPO) \ + IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) \ + -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d else # once this docker issue is fixed we may be able to do away with this # linux-specific workaround: https://github.com/docker/cli/issues/2290 KEYCLOAK_URL=$$(docker network inspect -f '{{(index .IPAM.Config 0).Gateway}}' bridge):8088 \ + UI_IMAGE_TAG=$(UI_IMAGE_TAG) UI_IMAGE_REPO=$(UI_IMAGE_REPO) \ IMAGE_REPO=$(CI_BUILD_TAG) \ docker compose -p $(CI_BUILD_TAG) -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d endif @@ -380,21 +399,28 @@ local-dev-yarn-stop: .PHONY: ui-development ui-development: build-ui-logs-development - IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d api api-db api-sidecar-handler local-api-data-watcher-pusher ui keycloak keycloak-db broker api-redis + UI_IMAGE_TAG=$(UI_IMAGE_TAG) UI_IMAGE_REPO=$(UI_IMAGE_REPO) \ + IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) \ + -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d api api-db api-sidecar-handler local-api-data-watcher-pusher ui keycloak keycloak-db broker api-redis $(MAKE) wait-for-keycloak .PHONY: api-development api-development: build-ui-logs-development - IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d api api-db api-sidecar-handler local-api-data-watcher-pusher keycloak keycloak-db broker api-redis + UI_IMAGE_TAG=$(UI_IMAGE_TAG) UI_IMAGE_REPO=$(UI_IMAGE_REPO) \ + IMAGE_REPO=$(CI_BUILD_TAG) docker compose -p $(CI_BUILD_TAG) \ + -f docker-compose.yaml -f docker-compose.local-dev.yaml --compatibility up -d api api-db api-sidecar-handler local-api-data-watcher-pusher keycloak keycloak-db broker api-redis $(MAKE) wait-for-keycloak .PHONY: ui-logs-development ui-logs-development: build-ui-logs-development - IMAGE_REPO=$(CI_BUILD_TAG) COMPOSE_STACK_NAME=$(CI_BUILD_TAG) ADDITIONAL_FLAGS="-f docker-compose.yaml -f docker-compose.local-dev.yaml" ADDITIONAL_SERVICES="ui" $(MAKE) compose-api-logs-development + UI_IMAGE_TAG=$(UI_IMAGE_TAG) UI_IMAGE_REPO=$(UI_IMAGE_REPO) \ + IMAGE_REPO=$(CI_BUILD_TAG) COMPOSE_STACK_NAME=$(CI_BUILD_TAG) \ + ADDITIONAL_FLAGS="-f docker-compose.yaml -f docker-compose.local-dev.yaml" ADDITIONAL_SERVICES="ui" $(MAKE) compose-api-logs-development .PHONY: api-logs-development api-logs-development: build-ui-logs-development - IMAGE_REPO=$(CI_BUILD_TAG) COMPOSE_STACK_NAME=$(CI_BUILD_TAG) ADDITIONAL_FLAGS="-f docker-compose.yaml -f docker-compose.local-dev.yaml" ADDITIONAL_SERVICES="" $(MAKE) compose-api-logs-development + IMAGE_REPO=$(CI_BUILD_TAG) COMPOSE_STACK_NAME=$(CI_BUILD_TAG) \ + ADDITIONAL_FLAGS="-f docker-compose.yaml -f docker-compose.local-dev.yaml" ADDITIONAL_SERVICES="" $(MAKE) compose-api-logs-development # compose-api-logs-development can be consumed by other repositories to start a local api # supported make variable passthrough are @@ -404,7 +430,9 @@ api-logs-development: build-ui-logs-development # ADDITIONAL_SERVICES - a way to pass through additional services ("ui", "ui ssh", etc..) .PHONY: compose-api-logs-development compose-api-logs-development: - docker compose -p $(COMPOSE_STACK_NAME) $(ADDITIONAL_FLAGS) --compatibility up -d $(ADDITIONAL_SERVICES) api api-db api-sidecar-handler actions-handler local-api-data-watcher-pusher keycloak keycloak-db broker api-redis logs2notifications local-minio mailhog + docker compose -p $(COMPOSE_STACK_NAME) $(ADDITIONAL_FLAGS) \ + --compatibility up -d $(ADDITIONAL_SERVICES) api api-db api-sidecar-handler actions-handler \ + local-api-data-watcher-pusher keycloak keycloak-db broker api-redis logs2notifications local-minio mailhog $(MAKE) CI_BUILD_TAG=$(COMPOSE_STACK_NAME) wait-for-keycloak ## CI targets @@ -417,7 +445,7 @@ STERN_VERSION = v2.6.1 CHART_TESTING_VERSION = v3.11.0 K3D_IMAGE = docker.io/rancher/k3s:v1.31.1-k3s1 TESTS = [nginx,api,features-kubernetes,bulk-deployment,features-kubernetes-2,features-variables,active-standby-kubernetes,tasks,drush,python,gitlab,github,bitbucket,services,workflows] -CHARTS_TREEISH = main +CHARTS_TREEISH = keycloak-tls-updates CHARTS_REPOSITORY = https://github.com/uselagoon/lagoon-charts.git #CHARTS_REPOSITORY = ../lagoon-charts TASK_IMAGES = task-activestandby @@ -681,6 +709,10 @@ endif INSTALL_LAGOON_DEPENDENCIES=false DOCKER_NETWORK=$(DOCKER_NETWORK) \ TESTS=$(TESTS) IMAGE_TAG=$(SAFE_BRANCH_NAME) DISABLE_CORE_HARBOR=true \ HELM=$(HELM) KUBECTL=$(KUBECTL) JQ=$(JQ) \ + UI_IMAGE_REPO=$(UI_IMAGE_REPO) UI_IMAGE_TAG=$(UI_IMAGE_TAG) \ + SSHPORTALAPI_IMAGE_REPO=$(SSHPORTALAPI_IMAGE_REPO) SSHPORTALAPI_IMAGE_TAG=$(SSHPORTALAPI_IMAGE_TAG) \ + SSHTOKEN_IMAGE_REPO=$(SSHTOKEN_IMAGE_REPO) SSHTOKEN_IMAGE_TAG=$(SSHTOKEN_IMAGE_TAG) \ + SSHPORTAL_IMAGE_REPO=$(SSHPORTAL_IMAGE_REPO) SSHPORTAL_IMAGE_TAG=$(SSHPORTAL_IMAGE_TAG) \ OVERRIDE_BUILD_DEPLOY_DIND_IMAGE=uselagoon/build-deploy-image:${BUILD_DEPLOY_IMAGE_TAG} \ $$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG) ] && echo 'OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG)') \ $$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY) ] && echo 'OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY)') \ @@ -778,8 +810,8 @@ k3d/push-images: k3d/get-lagoon-details: @export KUBECONFIG="$$(realpath ./kubeconfig.k3d.$(CI_BUILD_TAG))" && \ echo "===============================" && \ - echo "Lagoon UI URL: http://lagoon-ui.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io" \ - && echo "Lagoon API URL: http://lagoon-api.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/graphql" \ + echo "Lagoon UI URL: https://lagoon-ui.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io" \ + && echo "Lagoon API URL: https://lagoon-api.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/graphql" \ && echo "Lagoon API admin legacy token: $$(docker run \ -e JWTSECRET="$$($(KUBECTL) get secret -n lagoon-core lagoon-core-secrets -o jsonpath="{.data.JWTSECRET}" | base64 --decode)" \ -e JWTAUDIENCE=api.dev \ @@ -790,7 +822,7 @@ k3d/get-lagoon-details: && echo "SSH Core Service: lagoon-ssh.$$($(KUBECTL) -n lagoon-core get services lagoon-core-ssh -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io:$$($(KUBECTL) -n lagoon-core get services lagoon-core-ssh -o jsonpath='{.spec.ports[0].port}')" \ && echo "SSH Portal Service: lagoon-ssh-portal.$$($(KUBECTL) -n lagoon get services lagoon-remote-ssh-portal -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io:$$($(KUBECTL) -n lagoon get services lagoon-remote-ssh-portal -o jsonpath='{.spec.ports[0].port}')" \ && echo "SSH Token Service: lagoon-token.$$($(KUBECTL) -n lagoon-core get services lagoon-core-ssh-token -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io:$$($(KUBECTL) -n lagoon-core get services lagoon-core-ssh-token -o jsonpath='{.spec.ports[0].port}')" \ - && echo "Keycloak admin URL: http://lagoon-keycloak.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/auth" \ + && echo "Keycloak admin URL: https://lagoon-keycloak.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/auth" \ && echo "Keycloak admin password: $$($(KUBECTL) get secret -n lagoon-core lagoon-core-keycloak -o jsonpath="{.data.KEYCLOAK_ADMIN_PASSWORD}" | base64 --decode)" \ && echo "MailPit (email catching service): http://mailpit.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io" \ && echo "" \ @@ -832,7 +864,7 @@ k3d/seed-data: export ROUTER_PATTERN="\$${project}.\$${environment}.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" && \ export SEED_DATA=$$(envsubst < ./local-dev/k3d-seed-data/00-populate-kubernetes.gql | sed 's/"/\\"/g' | sed 's/\\n/\\\\n/g' | awk -F'\n' '{if(NR == 1) {printf $$0} else {printf "\\n"$$0}}') && \ export SEED_DATA_JSON="{\"query\": \"$$SEED_DATA\"}" && \ - wget --quiet --header "Content-Type: application/json" --header "Authorization: bearer $${LAGOON_LEGACY_ADMIN}" "http://lagoon-api.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/graphql" --post-data "$$SEED_DATA_JSON" --content-on-error -O - && \ + wget --no-check-certificate --quiet --header "Content-Type: application/json" --header "Authorization: bearer $${LAGOON_LEGACY_ADMIN}" "http://lagoon-api.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io/graphql" --post-data "$$SEED_DATA_JSON" --content-on-error -O - && \ echo "Loading API seed users" && \ cat ./local-dev/k3d-seed-data/seed-users.sh | $(KUBECTL) -n lagoon-core exec -i $$($(KUBECTL) -n lagoon-core get pods -l app.kubernetes.io/component=lagoon-core-keycloak -o json | $(JQ) -r '.items[0].metadata.name') -- sh -c "cat > /tmp/seed-users.sh" \ && $(KUBECTL) -n lagoon-core exec -it $$($(KUBECTL) -n lagoon-core get pods -l app.kubernetes.io/component=lagoon-core-keycloak -o json | $(JQ) -r '.items[0].metadata.name') -- bash '/tmp/seed-users.sh' \ diff --git a/services/api/package.json b/services/api/package.json index ec9d10ebca..6ba9ed01ac 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -22,7 +22,7 @@ "license": "MIT", "dependencies": { "@lagoon/commons": "4.0.0", - "@s3pweb/keycloak-admin-client-cjs": "^25.0.2", + "@s3pweb/keycloak-admin-client-cjs": "^26.0.0", "@supercharge/request-ip": "^1.1.2", "apollo-server-express": "^2.14.2", "aws-sdk": "^2.378.0", diff --git a/services/keycloak/Dockerfile b/services/keycloak/Dockerfile index 933cce2312..e41a209f8d 100644 --- a/services/keycloak/Dockerfile +++ b/services/keycloak/Dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.8.2-jdk-11 as builder +FROM maven:3.9.9-eclipse-temurin-21-alpine as builder # build the custom token mapper in builder COPY custom-mapper/. . RUN mvn clean compile package @@ -17,7 +17,7 @@ COPY javascript /tmp/lagoon-scripts RUN cd /tmp/lagoon-scripts && zip -r ../lagoon-scripts.jar * -FROM quay.io/keycloak/keycloak:24.0.5 +FROM quay.io/keycloak/keycloak:26.0.7 COPY --from=ubi-micro-build /mnt/rootfs / ARG LAGOON_VERSION @@ -87,7 +87,7 @@ COPY entrypoints/default-keycloak-entrypoint.sh /lagoon/entrypoints/99-default-k COPY startup-scripts /opt/keycloak/startup-scripts COPY themes/lagoon /opt/keycloak/themes/lagoon COPY --from=commons /tmp/lagoon-scripts.jar /opt/keycloak/providers/lagoon-scripts.jar -COPY --from=builder /target/custom-protocol-mapper-1.0.0.jar /opt/keycloak/providers/custom-protocol-mapper-1.0.0.jar +COPY --from=builder /target/custom-protocol-mapper-1.1.0.jar /opt/keycloak/providers/custom-protocol-mapper-1.1.0.jar COPY lagoon-realm-base-import.json /lagoon/seed/lagoon-realm-base-import.json diff --git a/services/keycloak/custom-mapper/pom.xml b/services/keycloak/custom-mapper/pom.xml index 9b00fba5ff..6dc866bce3 100644 --- a/services/keycloak/custom-mapper/pom.xml +++ b/services/keycloak/custom-mapper/pom.xml @@ -6,11 +6,11 @@ net.cake.keycloak.custom custom-protocol-mapper - 1.0.0 + 1.1.0 jar - 17.0.1 + 26.0.7 @@ -52,7 +52,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 true 1.8 @@ -62,7 +62,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.6.0 diff --git a/services/keycloak/entrypoints/default-keycloak-entrypoint.sh b/services/keycloak/entrypoints/default-keycloak-entrypoint.sh index 42562df1a7..5a65fb99cc 100755 --- a/services/keycloak/entrypoints/default-keycloak-entrypoint.sh +++ b/services/keycloak/entrypoints/default-keycloak-entrypoint.sh @@ -15,4 +15,9 @@ export KC_HOSTNAME_ADMIN_URL=${KEYCLOAK_FRONTEND_URL} export KC_DB_POOL_MAX_SIZE=${KEYCLOAK_DS_MAX_POOL_SIZE:-20} export KC_DB_POOL_MIN_SIZE=${KEYCLOAK_DS_MIN_POOL_SIZE:-0} +# https://www.keycloak.org/docs/latest/upgrading/#admin-bootstrapping-and-recovery +# https://www.keycloak.org/server/bootstrap-admin-recovery +export KC_BOOTSTRAP_ADMIN_USERNAME=$KEYCLOAK_ADMIN_USER +export KC_BOOTSTRAP_ADMIN_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD + KEYCLOAK_USER=$KEYCLOAK_ADMIN_USER KEYCLOAK_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD KEYCLOAK_ADMIN=$KEYCLOAK_ADMIN_USER /lagoon/kc-startup.sh "$@" diff --git a/services/keycloak/entrypoints/kc-startup.sh b/services/keycloak/entrypoints/kc-startup.sh index 2a96ef8dc6..abb603dc54 100755 --- a/services/keycloak/entrypoints/kc-startup.sh +++ b/services/keycloak/entrypoints/kc-startup.sh @@ -6,4 +6,10 @@ shopt -s failglob /opt/keycloak/startup-scripts/00-configure-lagoon.sh & disown -/opt/keycloak/bin/kc.sh "$@" --features="scripts,token-exchange,admin-fine-grained-authz" \ No newline at end of file +# https://www.keycloak.org/docs/latest/upgrading/#new-hostname-options +# https://www.keycloak.org/server/hostname +# "--hostname-backchannel-dynamic" +# Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. +# Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL. +/opt/keycloak/bin/kc.sh "$@" --features="scripts,token-exchange,admin-fine-grained-authz" \ + --hostname-backchannel-dynamic true --hostname ${KC_HOSTNAME_URL} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e904251128..25410c012d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -703,10 +703,10 @@ resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== -"@keycloak/keycloak-admin-client@25.0.4": - version "25.0.4" - resolved "https://registry.yarnpkg.com/@keycloak/keycloak-admin-client/-/keycloak-admin-client-25.0.4.tgz#2ec46bab133cc807df78ffd7ca7bba47ec8ed000" - integrity sha512-mZVFwly7cHZq1XpvJrrOutU0qrUbGo8NUdpb7PS4309x8yG2a4/WyZfh2lgiopBRQ6R/b24RsuHa4GetQPqT+g== +"@keycloak/keycloak-admin-client@26.0.6": + version "26.0.6" + resolved "https://registry.yarnpkg.com/@keycloak/keycloak-admin-client/-/keycloak-admin-client-26.0.6.tgz#b2ac56de7f4251c8a6a938745843e98000498646" + integrity sha512-pZmaSAyg+LwQ3qnZF+01ZkURpcoEdLAloUK5KOZjE9jyNd86EHdx98/XmTYaJIuQ6ydMXxTWWc5Grq18H+PvJQ== dependencies: camelize-ts "^3.0.0" url-join "^5.0.0" @@ -926,12 +926,12 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@s3pweb/keycloak-admin-client-cjs@^25.0.2": - version "25.0.4" - resolved "https://registry.yarnpkg.com/@s3pweb/keycloak-admin-client-cjs/-/keycloak-admin-client-cjs-25.0.4.tgz#925aa0f780233992e44d09a1efbfcbf4718f924a" - integrity sha512-0qQYvmftr4rZKO1JuDq077odNwQ8rS/FGlvBOSOoncvM2i7HljEHvMcsAHR3gNrOkawVlPoYwA+zZdtHICIU4A== +"@s3pweb/keycloak-admin-client-cjs@^26.0.0": + version "26.0.6" + resolved "https://registry.yarnpkg.com/@s3pweb/keycloak-admin-client-cjs/-/keycloak-admin-client-cjs-26.0.6.tgz#49b81da9980d2d3d058a616c8677d64d08a43a34" + integrity sha512-cF4SZcxj/CUK0hZ1dHmyPjVI1BcLvz7u6S8e9YUfqy7wweZScIl1/VpxPgfA9aaLCAcE6kAv++BjcQcvxzkmSg== dependencies: - "@keycloak/keycloak-admin-client" "25.0.4" + "@keycloak/keycloak-admin-client" "26.0.6" "@sinclair/typebox@^0.27.8": version "0.27.8"