diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java index d1ac011ac6..55b3961a11 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java @@ -16,6 +16,7 @@ */ package org.keycloak.models.map.client; +import org.jboss.logging.Logger; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; @@ -39,6 +40,7 @@ import java.util.stream.Stream; */ public abstract class MapClientAdapter extends AbstractClientModel implements ClientModel { + private static final Logger LOG = Logger.getLogger(MapClientAdapter.class); private final MapProtocolMapperUtils pmUtils; public MapClientAdapter(KeycloakSession session, RealmModel realm, MapClientEntity entity) { @@ -286,7 +288,15 @@ public abstract class MapClientAdapter extends AbstractClientModel> a = attributes == null ? Collections.emptyMap() : attributes; return a.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> { - if (entry.getValue().isEmpty()) return null; + if (entry.getValue().isEmpty()) { + return null; + } else if (entry.getValue().size() > 1) { + // This could be caused by an inconsistency in the storage, a programming error, + // or a downgrade from a future version of Keycloak that already supports multi-valued attributes. + // The caller will not see the other values, and when this entity is later updated, the additional values will be lost. + LOG.warnf("Client '%s' realm '%s' has attribute '%s' with %d values, retrieving only the first", getClientId(), getRealm().getName(), entry.getKey(), + entry.getValue().size()); + } return entry.getValue().get(0); }) ); diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeAdapter.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeAdapter.java index 1ea41cbb2d..afe84db777 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeAdapter.java @@ -25,6 +25,8 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + +import org.jboss.logging.Logger; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; @@ -37,6 +39,7 @@ import org.keycloak.models.utils.RoleUtils; public class MapClientScopeAdapter extends AbstractClientScopeModel implements ClientScopeModel { + private static final Logger LOG = Logger.getLogger(MapClientScopeAdapter.class); private final MapProtocolMapperUtils pmUtils; public MapClientScopeAdapter(KeycloakSession session, RealmModel realm, MapClientScopeEntity entity) { @@ -115,7 +118,15 @@ public class MapClientScopeAdapter extends AbstractClientScopeModel> a = attributes == null ? Collections.emptyMap() : attributes; return a.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> { - if (entry.getValue().isEmpty()) return null; + if (entry.getValue().isEmpty()) { + return null; + } else if (entry.getValue().size() > 1) { + // This could be caused by an inconsistency in the storage, a programming error, + // or a downgrade from a future version of Keycloak that already supports multi-valued attributes. + // The caller will not see the other values, and when this entity is later updated, the additional values will be lost. + LOG.warnf("ClientScope '%s' realm '%s' has attribute '%s' with %d values, retrieving only the first", getName(), getRealm().getName(), entry.getKey(), + entry.getValue().size()); + } return entry.getValue().get(0); }) ); diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java index 2b0fd3e24b..a4f8c18c15 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java @@ -24,6 +24,8 @@ import static java.util.Objects.nonNull; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.common.enums.SslRequired; import org.keycloak.component.ComponentFactory; @@ -64,6 +66,7 @@ import org.keycloak.models.utils.ComponentUtil; public class MapRealmAdapter extends AbstractRealmModel implements RealmModel { + private static final Logger LOG = Logger.getLogger(MapRealmAdapter.class); private static final String ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN = "actionTokenGeneratedByUserLifespan"; private static final String DEFAULT_SIGNATURE_ALGORITHM = "defaultSignatureAlgorithm"; private static final String BRUTE_FORCE_PROTECTED = "bruteForceProtected"; @@ -207,7 +210,15 @@ public class MapRealmAdapter extends AbstractRealmModel implemen public Map getAttributes() { return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> { - if (entry.getValue().isEmpty()) return null; + if (entry.getValue().isEmpty()) { + return null; + } else if (entry.getValue().size() > 1) { + // This could be caused by an inconsistency in the storage, a programming error, + // or a downgrade from a future version of Keycloak that already supports multi-valued attributes. + // The caller will not see the other values, and when this entity is later updated, the additional values be will lost. + LOG.warnf("Realm '%s' has attribute '%s' with %d values, retrieving only the first", getId(), entry.getKey(), + entry.getValue().size()); + } return entry.getValue().get(0); }) );