From c438da8d8bbbcf3be1ece47cef44b2c9704cbfc4 Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Mon, 9 Feb 2026 18:12:08 +0000 Subject: [PATCH] Use cached realm attributes for PAR and CIBA config Closes #46100 Signed-off-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> Co-authored-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> --- .../infinispan/entities/CachedRealm.java | 4 +-- .../org/keycloak/models/jpa/RealmAdapter.java | 4 +-- .../org/keycloak/models/AbstractConfig.java | 15 +++++++++ .../java/org/keycloak/models/CibaConfig.java | 31 +++++++++++++++++++ .../java/org/keycloak/models/ParConfig.java | 22 +++++++++++++ 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java index 662fe7d637d..24a6b085e3b 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java @@ -528,11 +528,11 @@ public class CachedRealm extends AbstractExtendableRevisioned { } public CibaConfig getCibaConfig(Supplier modelSupplier) { - return new CibaConfig(modelSupplier.get()); + return CibaConfig.fromCache(modelSupplier, Collections.unmodifiableMap(attributes)); } public ParConfig getParConfig(Supplier modelSupplier) { - return new ParConfig(modelSupplier.get()); + return ParConfig.fromCache(modelSupplier, Collections.unmodifiableMap(attributes)); } public int getActionTokenGeneratedByAdminLifespan() { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index f00c5d18cab..d52b4103798 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -642,12 +642,12 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel attributes, String name, int defaultValue) { + var value = attributes.get(name); + if (StringUtil.isBlank(value)) { + return defaultValue; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } } diff --git a/server-spi/src/main/java/org/keycloak/models/CibaConfig.java b/server-spi/src/main/java/org/keycloak/models/CibaConfig.java index c0ca00bcbf3..4ab851c01ff 100644 --- a/server-spi/src/main/java/org/keycloak/models/CibaConfig.java +++ b/server-spi/src/main/java/org/keycloak/models/CibaConfig.java @@ -18,6 +18,9 @@ package org.keycloak.models; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; import org.keycloak.jose.jws.Algorithm; import org.keycloak.utils.StringUtil; @@ -53,6 +56,10 @@ public class CibaConfig extends AbstractConfig { public static final String CIBA_BACKCHANNEL_CLIENT_NOTIFICATION_ENDPOINT = "ciba.backchannel.client.notification.endpoint"; public static final String CIBA_BACKCHANNEL_AUTH_REQUEST_SIGNING_ALG = "ciba.backchannel.auth.request.signing.alg"; + /** + * @deprecated use {@link #fromCache(Supplier, Map)} or {@link #fromModel(RealmModel)} factory methods + */ + @Deprecated(since = "26.6", forRemoval = true) public CibaConfig(RealmModel realm) { this.backchannelTokenDeliveryMode = realm.getAttribute(CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE); if (this.backchannelTokenDeliveryMode == null) { @@ -71,6 +78,30 @@ public class CibaConfig extends AbstractConfig { this.realmForWrite = () -> realm; } + private CibaConfig(Supplier realmForWrite, String authRequestedUserHint, String backChannelTokenDeliveryMode, int expiresIn, int poolingInterval) { + this.authRequestedUserHint = authRequestedUserHint; + this.backchannelTokenDeliveryMode = backChannelTokenDeliveryMode; + this.expiresIn = expiresIn; + this.poolingInterval = poolingInterval; + this.realmForWrite = realmForWrite; + } + + public static CibaConfig fromModel(RealmModel realm) { + var backChannelTokenDeliveryMode = Objects.requireNonNullElse(realm.getAttribute(CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE), DEFAULT_CIBA_POLICY_TOKEN_DELIVERY_MODE); + var authRequestedUserHint = Objects.requireNonNullElse(realm.getAttribute(CIBA_AUTH_REQUESTED_USER_HINT), DEFAULT_CIBA_POLICY_AUTH_REQUESTED_USER_HINT); + var expiresIn = realm.getAttribute(CIBA_EXPIRES_IN, DEFAULT_CIBA_POLICY_EXPIRES_IN); + var poolingInterval = realm.getAttribute(CIBA_INTERVAL, DEFAULT_CIBA_POLICY_INTERVAL); + return new CibaConfig(() -> realm, authRequestedUserHint, backChannelTokenDeliveryMode, expiresIn, poolingInterval); + } + + public static CibaConfig fromCache(Supplier realmForWrite, Map realmAttributes) { + var backChannelTokenDeliveryMode = realmAttributes.getOrDefault(CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE, DEFAULT_CIBA_POLICY_TOKEN_DELIVERY_MODE); + var authRequestedUserHint = realmAttributes.getOrDefault(CIBA_AUTH_REQUESTED_USER_HINT, DEFAULT_CIBA_POLICY_AUTH_REQUESTED_USER_HINT); + var expiresIn = getIntAttribute(realmAttributes, CIBA_EXPIRES_IN, DEFAULT_CIBA_POLICY_EXPIRES_IN); + var poolingInterval = getIntAttribute(realmAttributes, CIBA_INTERVAL, DEFAULT_CIBA_POLICY_INTERVAL); + return new CibaConfig(realmForWrite, authRequestedUserHint, backChannelTokenDeliveryMode, expiresIn, poolingInterval); + } + public String getBackchannelTokenDeliveryMode(ClientModel client) { String mode = client.getAttribute(CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE_PER_CLIENT); if (StringUtil.isBlank(mode)) { diff --git a/server-spi/src/main/java/org/keycloak/models/ParConfig.java b/server-spi/src/main/java/org/keycloak/models/ParConfig.java index d49a4f4386a..926bdf368e7 100644 --- a/server-spi/src/main/java/org/keycloak/models/ParConfig.java +++ b/server-spi/src/main/java/org/keycloak/models/ParConfig.java @@ -16,6 +16,9 @@ */ package org.keycloak.models; +import java.util.Map; +import java.util.function.Supplier; + public class ParConfig extends AbstractConfig { // realm attribute names @@ -29,12 +32,31 @@ public class ParConfig extends AbstractConfig { // client attribute names public static final String REQUIRE_PUSHED_AUTHORIZATION_REQUESTS = "require.pushed.authorization.requests"; + /** + * @deprecated use {@link #fromCache(Supplier, Map)} or {@link #fromModel(RealmModel)} factory methods + */ + @Deprecated(since = "26.6", forRemoval = true) public ParConfig(RealmModel realm) { this.requestUriLifespan = realm.getAttribute(PAR_REQUEST_URI_LIFESPAN, DEFAULT_PAR_REQUEST_URI_LIFESPAN); this.realmForWrite = () -> realm; } + private ParConfig(Supplier realmForWrite, int requestUriLifespan) { + this.requestUriLifespan = requestUriLifespan; + this.realmForWrite = realmForWrite; + } + + public static ParConfig fromModel(RealmModel realm) { + var requestUriLifespan = realm.getAttribute(PAR_REQUEST_URI_LIFESPAN, DEFAULT_PAR_REQUEST_URI_LIFESPAN); + return new ParConfig(() -> realm, requestUriLifespan); + } + + public static ParConfig fromCache(Supplier realmForWrite, Map realmAttributes) { + var requestUriLifespan = getIntAttribute(realmAttributes, PAR_REQUEST_URI_LIFESPAN, DEFAULT_PAR_REQUEST_URI_LIFESPAN); + return new ParConfig(realmForWrite, requestUriLifespan); + } + public int getRequestUriLifespan() { return requestUriLifespan; }