From 6d97a573e6d06389d6f7fdde67f295e9286183ca Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 26 Feb 2021 11:45:38 +0100 Subject: [PATCH] KEYCLOAK-17696 Make MapStorageFactory amphibian --- .../map-storage-concurrenthashmap.cli | 2 +- .../InfinispanUserSessionProvider.java | 5 + .../infinispan/UserLoginFailureAdapter.java | 5 + ...stractRootAuthenticationSessionEntity.java | 104 -- .../MapAuthenticationSessionAdapter.java | 2 +- ...henticationSessionAuthNoteUpdateEvent.java | 2 +- .../MapRootAuthenticationSessionAdapter.java | 13 +- .../MapRootAuthenticationSessionEntity.java | 81 +- .../MapRootAuthenticationSessionProvider.java | 39 +- ...tAuthenticationSessionProviderFactory.java | 23 +- .../authorization/MapAuthorizationStore.java | 13 +- .../MapAuthorizationStoreFactory.java | 57 +- .../MapPermissionTicketStore.java | 52 +- .../map/authorization/MapPolicyStore.java | 37 +- .../authorization/MapResourceServerStore.java | 31 +- .../map/authorization/MapResourceStore.java | 37 +- .../map/authorization/MapScopeStore.java | 30 +- .../adapter/AbstractResourceModel.java | 2 +- .../adapter/MapPermissionTicketAdapter.java | 22 +- .../adapter/MapPolicyAdapter.java | 28 +- .../adapter/MapResourceAdapter.java | 16 +- .../adapter/MapResourceServerAdapter.java | 9 +- .../adapter/MapScopeAdapter.java | 9 +- .../AbstractPermissionTicketEntity.java | 127 -- .../entity/AbstractPolicyEntity.java | 191 --- .../entity/AbstractResourceEntity.java | 183 --- .../entity/AbstractResourceServerEntity.java | 79 -- .../entity/AbstractScopeEntity.java | 86 -- .../entity/MapPermissionTicketEntity.java | 109 +- .../authorization/entity/MapPolicyEntity.java | 173 ++- .../entity/MapResourceEntity.java | 163 ++- .../entity/MapResourceServerEntity.java | 61 +- .../authorization/entity/MapScopeEntity.java | 68 +- .../map/client/AbstractClientEntity.java | 482 -------- .../models/map/client/MapClientAdapter.java | 9 +- .../models/map/client/MapClientEntity.java | 463 +++++++- .../models/map/client/MapClientProvider.java | 53 +- .../map/client/MapClientProviderFactory.java | 29 +- .../AbstractClientScopeEntity.java | 170 --- .../clientscope/MapClientScopeAdapter.java | 9 +- .../map/clientscope/MapClientScopeEntity.java | 157 ++- .../clientscope/MapClientScopeProvider.java | 41 +- .../MapClientScopeProviderFactory.java | 21 +- .../common/AbstractMapProviderFactory.java | 44 +- .../models/map/common/Serialization.java | 10 +- .../models/map/group/AbstractGroupEntity.java | 134 --- .../models/map/group/MapGroupAdapter.java | 9 +- .../models/map/group/MapGroupEntity.java | 119 +- .../models/map/group/MapGroupProvider.java | 45 +- .../map/group/MapGroupProviderFactory.java | 26 +- .../AbstractUserLoginFailureEntity.java | 129 --- .../MapUserLoginFailureAdapter.java | 4 +- .../MapUserLoginFailureEntity.java | 109 +- .../MapUserLoginFailureProvider.java | 32 +- .../MapUserLoginFailureProviderFactory.java | 70 +- .../models/map/realm/AbstractRealmEntity.java | 1031 ----------------- .../models/map/realm/MapRealmAdapter.java | 9 +- .../models/map/realm/MapRealmEntity.java | 1015 +++++++++++++++- .../models/map/realm/MapRealmProvider.java | 50 +- .../map/realm/MapRealmProviderFactory.java | 22 +- .../models/map/role/AbstractRoleEntity.java | 150 --- .../models/map/role/MapRoleAdapter.java | 9 +- .../models/map/role/MapRoleEntity.java | 135 ++- .../models/map/role/MapRoleProvider.java | 56 +- .../map/role/MapRoleProviderFactory.java | 23 +- .../MapServerInfoProviderFactory.java | 23 +- .../map/storage/MapFieldPredicates.java | 256 ++-- .../map/storage/MapKeycloakTransaction.java | 10 +- .../models/map/storage/MapStorage.java | 14 +- .../map/storage/MapStorageProvider.java | 5 +- .../storage/MapStorageProviderFactory.java | 14 +- .../map/storage/ModelCriteriaBuilder.java | 2 +- .../map/storage/StringKeyConvertor.java | 126 ++ .../storage/chm/ConcurrentHashMapStorage.java | 9 +- .../chm/ConcurrentHashMapStorageProvider.java | 6 +- ...ncurrentHashMapStorageProviderFactory.java | 160 ++- .../UserSessionConcurrentHashMapStorage.java | 24 +- .../models/map/user/AbstractUserEntity.java | 371 ------ .../models/map/user/MapUserAdapter.java | 10 +- .../models/map/user/MapUserEntity.java | 357 +++++- .../models/map/user/MapUserProvider.java | 99 +- .../map/user/MapUserProviderFactory.java | 25 +- ...tractAuthenticatedClientSessionEntity.java | 205 ---- ...stractAuthenticatedClientSessionModel.java | 6 +- .../AbstractUserSessionEntity.java | 288 ----- .../userSession/AbstractUserSessionModel.java | 20 +- .../MapAuthenticatedClientSessionAdapter.java | 9 +- .../MapAuthenticatedClientSessionEntity.java | 184 ++- .../userSession/MapUserSessionAdapter.java | 27 +- .../map/userSession/MapUserSessionEntity.java | 267 ++++- .../userSession/MapUserSessionProvider.java | 136 +-- .../MapUserSessionProviderFactory.java | 79 +- .../map/userSession/SessionExpiration.java | 4 +- ...bstractUserEntityCredentialsOrderTest.java | 4 +- .../authorization/store/StoreFactorySpi.java | 5 +- .../org/keycloak/models/ServerInfoSpi.java | 4 +- .../models/UserLoginFailureModel.java | 1 + .../keycloak/models/UserSessionProvider.java | 9 +- ...efaultComponentFactoryProviderFactory.java | 19 +- .../resources/META-INF/keycloak-server.json | 4 +- .../org/keycloak/testsuite/model/Config.java | 31 +- .../testsuite/model/KeycloakModelTest.java | 18 +- .../testsuite/model/MapStorageTest.java | 196 ++++ .../parameters/ConcurrentHashMapStorage.java | 5 +- .../testsuite/model/parameters/Map.java | 13 +- .../session/UserSessionProviderModelTest.java | 2 +- testsuite/model/test-all-profiles.sh | 6 +- 107 files changed, 4834 insertions(+), 4743 deletions(-) delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authSession/AbstractRootAuthenticationSessionEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPermissionTicketEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPolicyEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceServerEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractScopeEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/client/AbstractClientEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/clientscope/AbstractClientScopeEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/loginFailure/AbstractUserLoginFailureEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/realm/AbstractRealmEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/role/AbstractRoleEntity.java create mode 100644 model/map/src/main/java/org/keycloak/models/map/storage/StringKeyConvertor.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/user/AbstractUserEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionEntity.java delete mode 100644 model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionEntity.java create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java diff --git a/distribution/server-dist/src/main/docs/examples/map-storage-concurrenthashmap.cli b/distribution/server-dist/src/main/docs/examples/map-storage-concurrenthashmap.cli index fcbc3c30d1..edcc742199 100644 --- a/distribution/server-dist/src/main/docs/examples/map-storage-concurrenthashmap.cli +++ b/distribution/server-dist/src/main/docs/examples/map-storage-concurrenthashmap.cli @@ -30,6 +30,6 @@ embed-server /subsystem=keycloak-server/spi=userSessions:add(default-provider=map) /subsystem=keycloak-server/spi=mapStorage:add(default-provider=concurrenthashmap) -/subsystem=keycloak-server/spi=mapStorage/provider=concurrenthashmap:add(properties={dir="${jboss.server.data.dir}/map"},enabled=true) +/subsystem=keycloak-server/spi=mapStorage/provider=concurrenthashmap:add(properties={dir="${jboss.server.data.dir}/map",keyType.realms=string,keyType.authz-resource-servers=string},enabled=true) quit \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java index 4ac224ee85..765a00b58c 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java @@ -276,6 +276,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider { .map(entity -> this.wrap(realm, entity, offline)); } + @Override + public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, String clientSessionId, boolean offline) { + return getClientSession(userSession, client, clientSessionId == null ? null : UUID.fromString(clientSessionId), offline); + } + @Override public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) { AuthenticatedClientSessionEntity entity = getClientSessionEntity(clientSessionId, offline); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java index 658de000af..44838957ae 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java @@ -136,4 +136,9 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel { provider.getLoginFailuresTx().addTask(key, task); } + @Override + public String getId() { + return key.toString(); + } + } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/AbstractRootAuthenticationSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/authSession/AbstractRootAuthenticationSessionEntity.java deleted file mode 100644 index 859a112206..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/AbstractRootAuthenticationSessionEntity.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2020 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.models.map.authSession; - -import org.keycloak.models.map.common.AbstractEntity; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Martin Kanis - */ -public abstract class AbstractRootAuthenticationSessionEntity implements AbstractEntity { - - private K id; - private String realmId; - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - private int timestamp; - private Map authenticationSessions = new ConcurrentHashMap<>(); - - protected AbstractRootAuthenticationSessionEntity() { - this.id = null; - this.realmId = null; - } - - public AbstractRootAuthenticationSessionEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getRealmId() { - return realmId; - } - - public void setRealmId(String realmId) { - this.updated |= !Objects.equals(this.realmId, realmId); - this.realmId = realmId; - } - - public int getTimestamp() { - return timestamp; - } - - public void setTimestamp(int timestamp) { - this.updated |= !Objects.equals(this.timestamp, timestamp); - this.timestamp = timestamp; - } - - public Map getAuthenticationSessions() { - return authenticationSessions; - } - - public void setAuthenticationSessions(Map authenticationSessions) { - this.updated |= !Objects.equals(this.authenticationSessions, authenticationSessions); - this.authenticationSessions = authenticationSessions; - } - - public MapAuthenticationSessionEntity removeAuthenticationSession(String tabId) { - MapAuthenticationSessionEntity entity = this.authenticationSessions.remove(tabId); - this.updated |= entity != null; - return entity; - } - - public void addAuthenticationSession(String tabId, MapAuthenticationSessionEntity entity) { - this.updated |= !Objects.equals(this.authenticationSessions.put(tabId, entity), entity); - } - - public void clearAuthenticationSessions() { - this.updated |= !this.authenticationSessions.isEmpty(); - this.authenticationSessions.clear(); - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAdapter.java index f2ca89580f..09a5b6fa24 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAdapter.java @@ -37,7 +37,7 @@ public class MapAuthenticationSessionAdapter implements AuthenticationSessionMod private final KeycloakSession session; private final MapRootAuthenticationSessionAdapter parent; private final String tabId; - private MapAuthenticationSessionEntity entity; + private final MapAuthenticationSessionEntity entity; public MapAuthenticationSessionAdapter(KeycloakSession session, MapRootAuthenticationSessionAdapter parent, String tabId, MapAuthenticationSessionEntity entity) { diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAuthNoteUpdateEvent.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAuthNoteUpdateEvent.java index a28ec028cb..3307b28f62 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAuthNoteUpdateEvent.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapAuthenticationSessionAuthNoteUpdateEvent.java @@ -81,6 +81,6 @@ public class MapAuthenticationSessionAuthNoteUpdateEvent implements ClusterEvent @Override public String toString() { return String.format("AuthenticationSessionAuthNoteUpdateEvent [ authSessionId=%s, tabId=%s, clientUUID=%s, authNotesFragment=%s ]", - authSessionId, clientUUID, authNotesFragment); + authSessionId, tabId, clientUUID, authNotesFragment); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionAdapter.java index 4b5dca1316..670ff08a48 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionAdapter.java @@ -31,17 +31,12 @@ import java.util.stream.Collectors; /** * @author Martin Kanis */ -public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticationSessionModel { +public abstract class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticationSessionModel> { - public MapRootAuthenticationSessionAdapter(KeycloakSession session, RealmModel realm, MapRootAuthenticationSessionEntity entity) { + public MapRootAuthenticationSessionAdapter(KeycloakSession session, RealmModel realm, MapRootAuthenticationSessionEntity entity) { super(session, realm, entity); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public RealmModel getRealm() { return session.realms().getRealm(entity.getRealmId()); @@ -102,9 +97,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat public void removeAuthenticationSessionByTabId(String tabId) { if (entity.removeAuthenticationSession(tabId) != null) { if (entity.getAuthenticationSessions().isEmpty()) { - MapRootAuthenticationSessionProvider authenticationSessionProvider = - (MapRootAuthenticationSessionProvider) session.authenticationSessions(); - authenticationSessionProvider.tx.delete(entity.getId()); + session.authenticationSessions().removeRootAuthenticationSession(realm, this); } else { entity.setTimestamp(Time.currentTime()); } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionEntity.java index 055ec41944..60e4efa8ed 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionEntity.java @@ -16,18 +16,89 @@ */ package org.keycloak.models.map.authSession; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractEntity; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; /** * @author Martin Kanis */ -public class MapRootAuthenticationSessionEntity extends AbstractRootAuthenticationSessionEntity { +public class MapRootAuthenticationSessionEntity implements AbstractEntity { + + private K id; + private String realmId; + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; + private int timestamp; + private Map authenticationSessions = new ConcurrentHashMap<>(); protected MapRootAuthenticationSessionEntity() { - super(); + this.id = null; + this.realmId = null; } - public MapRootAuthenticationSessionEntity(UUID id, String realmId) { - super(id, realmId); + public MapRootAuthenticationSessionEntity(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; + } + + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + public String getRealmId() { + return realmId; + } + + public void setRealmId(String realmId) { + this.updated |= !Objects.equals(this.realmId, realmId); + this.realmId = realmId; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.updated |= !Objects.equals(this.timestamp, timestamp); + this.timestamp = timestamp; + } + + public Map getAuthenticationSessions() { + return authenticationSessions; + } + + public void setAuthenticationSessions(Map authenticationSessions) { + this.updated |= !Objects.equals(this.authenticationSessions, authenticationSessions); + this.authenticationSessions = authenticationSessions; + } + + public MapAuthenticationSessionEntity removeAuthenticationSession(String tabId) { + MapAuthenticationSessionEntity entity = this.authenticationSessions.remove(tabId); + this.updated |= entity != null; + return entity; + } + + public void addAuthenticationSession(String tabId, MapAuthenticationSessionEntity entity) { + this.updated |= !Objects.equals(this.authenticationSessions.put(tabId, entity), entity); + } + + public void clearAuthenticationSessions() { + this.updated |= !this.authenticationSessions.isEmpty(); + this.authenticationSessions.clear(); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProvider.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProvider.java index 587f8d2c51..80864992ab 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProvider.java @@ -36,7 +36,6 @@ import org.keycloak.sessions.RootAuthenticationSessionModel; import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields; import java.util.Map; import java.util.Objects; -import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; @@ -45,17 +44,16 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace; /** * @author Martin Kanis */ -public class MapRootAuthenticationSessionProvider implements AuthenticationSessionProvider { +public class MapRootAuthenticationSessionProvider implements AuthenticationSessionProvider { private static final Logger LOG = Logger.getLogger(MapRootAuthenticationSessionProvider.class); private final KeycloakSession session; - protected final MapKeycloakTransaction tx; - private final MapStorage sessionStore; + protected final MapKeycloakTransaction, RootAuthenticationSessionModel> tx; + private final MapStorage, RootAuthenticationSessionModel> sessionStore; - private static final Predicate ALWAYS_FALSE = role -> false; private static final String AUTHENTICATION_SESSION_EVENTS = "AUTHENTICATION_SESSION_EVENTS"; - public MapRootAuthenticationSessionProvider(KeycloakSession session, MapStorage sessionStore) { + public MapRootAuthenticationSessionProvider(KeycloakSession session, MapStorage, RootAuthenticationSessionModel> sessionStore) { this.session = session; this.sessionStore = sessionStore; this.tx = sessionStore.createTransaction(session); @@ -63,21 +61,26 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi session.getTransactionManager().enlistAfterCompletion(tx); } - private Function entityToAdapterFunc(RealmModel realm) { + private Function, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapRootAuthenticationSessionAdapter(session, realm, registerEntityForChanges(origEntity)); + return origEntity -> new MapRootAuthenticationSessionAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return sessionStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } - private MapRootAuthenticationSessionEntity registerEntityForChanges(MapRootAuthenticationSessionEntity origEntity) { - MapRootAuthenticationSessionEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapRootAuthenticationSessionEntity::isUpdated); + private MapRootAuthenticationSessionEntity registerEntityForChanges(MapRootAuthenticationSessionEntity origEntity) { + MapRootAuthenticationSessionEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapRootAuthenticationSessionEntity::isUpdated); return res; } - private Predicate entityRealmFilter(String realmId) { + private Predicate> entityRealmFilter(String realmId) { if (realmId == null) { - return MapRootAuthenticationSessionProvider.ALWAYS_FALSE; + return c -> false; } return entity -> Objects.equals(realmId, entity.getRealmId()); } @@ -92,12 +95,12 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi public RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm, String id) { Objects.requireNonNull(realm, "The provided realm can't be null!"); - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? sessionStore.getKeyConvertor().yieldNewUniqueKey() : sessionStore.getKeyConvertor().fromString(id); LOG.tracef("createRootAuthenticationSession(%s)%s", realm.getName(), getShortStackTrace()); // create map authentication session entity - MapRootAuthenticationSessionEntity entity = new MapRootAuthenticationSessionEntity(entityId, realm.getId()); + MapRootAuthenticationSessionEntity entity = new MapRootAuthenticationSessionEntity<>(entityId, realm.getId()); entity.setRealmId(realm.getId()); entity.setTimestamp(Time.currentTime()); @@ -119,7 +122,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi LOG.tracef("getRootAuthenticationSession(%s, %s)%s", realm.getName(), authenticationSessionId, getShortStackTrace()); - MapRootAuthenticationSessionEntity entity = tx.read(UUID.fromString(authenticationSessionId)); + MapRootAuthenticationSessionEntity entity = tx.read(sessionStore.getKeyConvertor().fromStringSafe(authenticationSessionId)); return (entity == null || !entityRealmFilter(realm.getId()).test(entity)) ? null : entityToAdapterFunc(realm).apply(entity); @@ -128,7 +131,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi @Override public void removeRootAuthenticationSession(RealmModel realm, RootAuthenticationSessionModel authenticationSession) { Objects.requireNonNull(authenticationSession, "The provided root authentication session can't be null!"); - tx.delete(UUID.fromString(authenticationSession.getId())); + tx.delete(sessionStore.getKeyConvertor().fromString(authenticationSession.getId())); } @Override @@ -147,7 +150,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.TIMESTAMP, Operator.LT, expired); - long deletedCount = tx.delete(UUID.randomUUID(), mcb); + long deletedCount = tx.delete(sessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb); LOG.debugf("Removed %d expired authentication sessions for realm '%s'", deletedCount, realm.getName()); } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java index d7fc635c99..1d2f1d15ef 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java @@ -17,33 +17,30 @@ package org.keycloak.models.map.authSession; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProvider; -import org.keycloak.models.map.storage.MapStorageProviderFactory; import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.sessions.AuthenticationSessionProviderFactory; import org.keycloak.sessions.RootAuthenticationSessionModel; -import java.util.UUID; /** * @author Martin Kanis */ -public class MapRootAuthenticationSessionProviderFactory extends AbstractMapProviderFactory +public class MapRootAuthenticationSessionProviderFactory extends AbstractMapProviderFactory, RootAuthenticationSessionModel> implements AuthenticationSessionProviderFactory { - private MapStorage store; - - @Override - public void postInit(KeycloakSessionFactory factory) { - MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("sessions", UUID.class, MapRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class); + public MapRootAuthenticationSessionProviderFactory() { + super(MapRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class); } @Override public AuthenticationSessionProvider create(KeycloakSession session) { - return new MapRootAuthenticationSessionProvider(session, store); + return new MapRootAuthenticationSessionProvider<>(session, getStorage(session)); } + + @Override + public String getHelpText() { + return "Authentication session provider"; + } + } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStore.java index 833901ad8d..418a3148ff 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStore.java @@ -18,11 +18,6 @@ package org.keycloak.models.map.authorization; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.model.PermissionTicket; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.store.PermissionTicketStore; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceServerStore; @@ -30,14 +25,8 @@ import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; -import org.keycloak.models.map.authorization.entity.MapPolicyEntity; -import org.keycloak.models.map.authorization.entity.MapResourceEntity; -import org.keycloak.models.map.authorization.entity.MapResourceServerEntity; -import org.keycloak.models.map.authorization.entity.MapScopeEntity; import org.keycloak.models.map.storage.MapStorage; -import java.util.UUID; /** * @author mhajas @@ -51,7 +40,7 @@ public class MapAuthorizationStore implements StoreFactory { private final PermissionTicketStore permissionTicketStore; private boolean readOnly; - public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) { + public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) { this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider); this.policyStore = new MapPolicyStore(session, policyStore, provider); this.resourceServerStore = new MapResourceServerStore(session, resourceServerStore, provider); diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStoreFactory.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStoreFactory.java index 64fce0bf1a..0c6c12990c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStoreFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapAuthorizationStoreFactory.java @@ -26,32 +26,49 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.store.AuthorizationStoreFactory; import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.component.AmphibianProviderFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; import org.keycloak.models.map.authorization.entity.MapPolicyEntity; import org.keycloak.models.map.authorization.entity.MapResourceEntity; import org.keycloak.models.map.authorization.entity.MapResourceServerEntity; import org.keycloak.models.map.authorization.entity.MapScopeEntity; +import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorageProvider; import org.keycloak.models.map.storage.MapStorageProviderFactory; -import java.util.UUID; +import org.keycloak.models.map.storage.MapStorageSpi; +import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory; /** * @author mhajas */ -public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory { +public class MapAuthorizationStoreFactory implements AmphibianProviderFactory, AuthorizationStoreFactory { - private MapStorage permissionTicketStore; - private MapStorage policyStore; - private MapStorage resourceServerStore; - private MapStorage resourceStore; - private MapStorage scopeStore; + public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID; + + private Config.Scope storageConfigScope; @Override public StoreFactory create(KeycloakSession session) { + MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(), + MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME); + final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session); AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class); + + + MapStorage permissionTicketStore; + MapStorage policyStore; + MapStorage resourceServerStore; + MapStorage resourceStore; + MapStorage scopeStore; + + permissionTicketStore = mapStorageProvider.getStorage(MapPermissionTicketEntity.class, PermissionTicket.class); + policyStore = mapStorageProvider.getStorage(MapPolicyEntity.class, Policy.class); + resourceServerStore = mapStorageProvider.getStorage(MapResourceServerEntity.class, ResourceServer.class); + resourceStore = mapStorageProvider.getStorage(MapResourceEntity.class, Resource.class); + scopeStore = mapStorageProvider.getStorage(MapScopeEntity.class, Scope.class); + return new MapAuthorizationStore(session, permissionTicketStore, policyStore, @@ -63,21 +80,8 @@ public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory { } @Override - public void init(Config.Scope config) { - - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - AuthorizationStoreFactory.super.postInit(factory); - - MapStorageProviderFactory mapStorageProvider = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - permissionTicketStore = mapStorageProvider.getStorage("authzPermissionTickets", UUID.class, MapPermissionTicketEntity.class, PermissionTicket.class); - policyStore = mapStorageProvider.getStorage("authzPolicies", UUID.class, MapPolicyEntity.class, Policy.class); - resourceServerStore = mapStorageProvider.getStorage("authzResourceServers", String.class, MapResourceServerEntity.class, ResourceServer.class); - resourceStore = mapStorageProvider.getStorage("authzResources", UUID.class, MapResourceEntity.class, Resource.class); - scopeStore = mapStorageProvider.getStorage("authzScopes", UUID.class, MapScopeEntity.class, Scope.class); - + public void init(org.keycloak.Config.Scope config) { + this.storageConfigScope = config.scope("storage"); } @Override @@ -87,6 +91,11 @@ public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory { @Override public String getId() { - return "map"; + return PROVIDER_ID; + } + + @Override + public String getHelpText() { + return "Authorization store provider"; } } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java index 07c99632c7..3633522c53 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java @@ -28,7 +28,6 @@ import org.keycloak.authorization.store.ResourceStore; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter; -import org.keycloak.models.map.authorization.entity.AbstractPermissionTicketEntity; import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; import org.keycloak.models.map.common.Serialization; import org.keycloak.models.map.storage.MapKeycloakTransaction; @@ -37,11 +36,11 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import java.util.Collections; +import java.util.Comparator; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -49,30 +48,35 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.utils.StreamsUtil.distinctByKey; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapPermissionTicketStore implements PermissionTicketStore { +public class MapPermissionTicketStore> implements PermissionTicketStore { private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class); private final AuthorizationProvider authorizationProvider; - final MapKeycloakTransaction tx; - private final MapStorage permissionTicketStore; + final MapKeycloakTransaction, PermissionTicket> tx; + private final MapStorage, PermissionTicket> permissionTicketStore; - public MapPermissionTicketStore(KeycloakSession session, MapStorage permissionTicketStore, AuthorizationProvider provider) { + public MapPermissionTicketStore(KeycloakSession session, MapStorage, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) { this.authorizationProvider = provider; this.permissionTicketStore = permissionTicketStore; this.tx = permissionTicketStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private MapPermissionTicketEntity registerEntityForChanges(MapPermissionTicketEntity origEntity) { - final MapPermissionTicketEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapPermissionTicketEntity::isUpdated); + private MapPermissionTicketEntity registerEntityForChanges(MapPermissionTicketEntity origEntity) { + final MapPermissionTicketEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapPermissionTicketEntity::isUpdated); return res; } - private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) { + private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) { if (origEntity == null) return null; // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapPermissionTicketAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()); + return new MapPermissionTicketAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) { + @Override + public String getId() { + return permissionTicketStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } private ModelCriteriaBuilder forResourceServer(String resourceServerId) { @@ -116,13 +120,14 @@ public class MapPermissionTicketStore implements PermissionTicketStore { + ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists."); } - MapPermissionTicketEntity entity = new MapPermissionTicketEntity(UUID.randomUUID()); - entity.setResourceId(UUID.fromString(resourceId)); + final K newId = permissionTicketStore.getKeyConvertor().yieldNewUniqueKey(); + MapPermissionTicketEntity entity = new MapPermissionTicketEntity<>(newId); + entity.setResourceId(resourceId); entity.setRequester(requester); entity.setCreatedTimestamp(System.currentTimeMillis()); if (scopeId != null) { - entity.setScopeId(UUID.fromString(scopeId)); + entity.setScopeId(scopeId); } entity.setOwner(owner); @@ -136,7 +141,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore { @Override public void delete(String id) { LOG.tracef("delete(%s)%s", id, getShortStackTrace()); - tx.delete(UUID.fromString(id)); + tx.delete(permissionTicketStore.getKeyConvertor().fromString(id)); } @Override @@ -204,7 +209,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore { if (r == null || r.isEmpty()) { return Collections.emptyList(); } - mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.IN, r.stream().map(Resource::getId).collect(Collectors.toList())); + mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.IN, r.stream().map(Resource::getId)); } mcb = mcb.and( @@ -213,8 +218,9 @@ public class MapPermissionTicketStore implements PermissionTicketStore { .toArray(ModelCriteriaBuilder[]::new) ); + Comparator> c = Comparator.comparing(MapPermissionTicketEntity::getId); return paginatedStream(tx.getUpdatedNotRemoved(mcb) - .sorted(MapPermissionTicketEntity.COMPARE_BY_ID), firstResult, maxResult) + .sorted(c), firstResult, maxResult) .map(this::entityToAdapter) .collect(Collectors.toList()); } @@ -278,14 +284,14 @@ public class MapPermissionTicketStore implements PermissionTicketStore { .compare(SearchableFields.REQUESTER, Operator.EQ, requester) .compare(SearchableFields.GRANTED_TIMESTAMP, Operator.EXISTS); - Function ticketResourceMapper; + Function, Resource> ticketResourceMapper; ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore(); if (name != null) { ticketResourceMapper = ticket -> { Map filterOptionMap = new EnumMap<>(Resource.FilterOption.class); - filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId().toString()}); + filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId()}); filterOptionMap.put(Resource.FilterOption.NAME, new String[] {name}); List resource = resourceStore.findByResourceServer(filterOptionMap, ticket.getResourceServerId(), -1, 1); @@ -294,11 +300,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore { }; } else { ticketResourceMapper = ticket -> resourceStore - .findById(ticket.getResourceId().toString(), ticket.getResourceServerId()); + .findById(ticket.getResourceId(), ticket.getResourceServerId()); } return paginatedStream(tx.getUpdatedNotRemoved(mcb) - .filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId)) + .filter(distinctByKey(MapPermissionTicketEntity::getResourceId)) .sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID) .map(ticketResourceMapper) .filter(Objects::nonNull), first, max) @@ -311,10 +317,10 @@ public class MapPermissionTicketStore implements PermissionTicketStore { .compare(SearchableFields.OWNER, Operator.EQ, owner); return paginatedStream(tx.getUpdatedNotRemoved(mcb) - .filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId)) + .filter(distinctByKey(MapPermissionTicketEntity::getResourceId)) .sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID), first, max) .map(ticket -> authorizationProvider.getStoreFactory().getResourceStore() - .findById(ticket.getResourceId().toString(), ticket.getResourceServerId())) + .findById(ticket.getResourceId(), ticket.getResourceServerId())) .collect(Collectors.toList()); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java index b4e56417b2..1abfee600c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java @@ -26,7 +26,6 @@ import org.keycloak.authorization.store.PolicyStore; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter; -import org.keycloak.models.map.authorization.entity.AbstractPolicyEntity; import org.keycloak.models.map.authorization.entity.MapPolicyEntity; import org.keycloak.models.map.common.Serialization; import org.keycloak.models.map.storage.MapKeycloakTransaction; @@ -38,37 +37,41 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapPolicyStore implements PolicyStore { +public class MapPolicyStore implements PolicyStore { private static final Logger LOG = Logger.getLogger(MapPolicyStore.class); private final AuthorizationProvider authorizationProvider; - final MapKeycloakTransaction tx; - private final MapStorage policyStore; + final MapKeycloakTransaction, Policy> tx; + private final MapStorage, Policy> policyStore; - public MapPolicyStore(KeycloakSession session, MapStorage policyStore, AuthorizationProvider provider) { + public MapPolicyStore(KeycloakSession session, MapStorage, Policy> policyStore, AuthorizationProvider provider) { this.authorizationProvider = provider; this.policyStore = policyStore; this.tx = policyStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private MapPolicyEntity registerEntityForChanges(MapPolicyEntity origEntity) { - final MapPolicyEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapPolicyEntity::isUpdated); + private MapPolicyEntity registerEntityForChanges(MapPolicyEntity origEntity) { + final MapPolicyEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapPolicyEntity::isUpdated); return res; } - private Policy entityToAdapter(MapPolicyEntity origEntity) { + private Policy entityToAdapter(MapPolicyEntity origEntity) { if (origEntity == null) return null; // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapPolicyAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()); + return new MapPolicyAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) { + @Override + public String getId() { + return policyStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } private ModelCriteriaBuilder forResourceServer(String resourceServerId) { @@ -92,8 +95,8 @@ public class MapPolicyStore implements PolicyStore { throw new ModelDuplicateException("Policy with name '" + representation.getName() + "' for " + resourceServer.getId() + " already exists"); } - UUID uid = representation.getId() == null ? UUID.randomUUID() : UUID.fromString(representation.getId()); - MapPolicyEntity entity = new MapPolicyEntity(uid); + K uid = representation.getId() == null ? policyStore.getKeyConvertor().yieldNewUniqueKey() : policyStore.getKeyConvertor().fromString(representation.getId()); + MapPolicyEntity entity = new MapPolicyEntity<>(uid); entity.setType(representation.getType()); entity.setName(representation.getName()); entity.setResourceServerId(resourceServer.getId()); @@ -106,7 +109,7 @@ public class MapPolicyStore implements PolicyStore { @Override public void delete(String id) { LOG.tracef("delete(%s)%s", id, getShortStackTrace()); - tx.delete(UUID.fromString(id)); + tx.delete(policyStore.getKeyConvertor().fromString(id)); } @Override @@ -155,9 +158,9 @@ public class MapPolicyStore implements PolicyStore { } return paginatedStream(tx.getUpdatedNotRemoved(mcb) - .sorted(AbstractPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult) - .map(MapPolicyEntity::getId) - .map(UUID::toString) + .sorted(MapPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult) + .map(MapPolicyEntity::getId) + .map(K::toString) .map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)) // We need to go through cache .collect(Collectors.toList()); } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceServerStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceServerStore.java index ef0a12002d..7ea4ae2a03 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceServerStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceServerStore.java @@ -41,30 +41,35 @@ import org.keycloak.storage.StorageId; import static org.keycloak.common.util.StackUtil.getShortStackTrace; -public class MapResourceServerStore implements ResourceServerStore { +public class MapResourceServerStore implements ResourceServerStore { private static final Logger LOG = Logger.getLogger(MapResourceServerStore.class); private final AuthorizationProvider authorizationProvider; - final MapKeycloakTransaction tx; - private final MapStorage resourceServerStore; + final MapKeycloakTransaction, ResourceServer> tx; + private final MapStorage, ResourceServer> resourceServerStore; - public MapResourceServerStore(KeycloakSession session, MapStorage resourceServerStore, AuthorizationProvider provider) { + public MapResourceServerStore(KeycloakSession session, MapStorage, ResourceServer> resourceServerStore, AuthorizationProvider provider) { this.resourceServerStore = resourceServerStore; this.tx = resourceServerStore.createTransaction(session); this.authorizationProvider = provider; session.getTransactionManager().enlist(tx); } - private MapResourceServerEntity registerEntityForChanges(MapResourceServerEntity origEntity) { - final MapResourceServerEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapResourceServerEntity::isUpdated); + private MapResourceServerEntity registerEntityForChanges(MapResourceServerEntity origEntity) { + final MapResourceServerEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapResourceServerEntity::isUpdated); return res; } - private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) { + private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) { if (origEntity == null) return null; // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapResourceServerAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()); + return new MapResourceServerAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) { + @Override + public String getId() { + return resourceServerStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } @Override @@ -77,11 +82,11 @@ public class MapResourceServerStore implements ResourceServerStore { throw new ModelException("Creating resource server from federated ClientModel not supported"); } - if (tx.read(clientId) != null) { + if (tx.read(resourceServerStore.getKeyConvertor().fromString(clientId)) != null) { throw new ModelDuplicateException("Resource server already exists: " + clientId); } - MapResourceServerEntity entity = new MapResourceServerEntity(clientId); + MapResourceServerEntity entity = new MapResourceServerEntity<>(resourceServerStore.getKeyConvertor().fromString(clientId)); tx.create(entity.getId(), entity); @@ -113,7 +118,7 @@ public class MapResourceServerStore implements ResourceServerStore { .map(Scope::getId) .forEach(scopeStore::delete); - tx.delete(id); + tx.delete(resourceServerStore.getKeyConvertor().fromString(id)); } @Override @@ -125,7 +130,7 @@ public class MapResourceServerStore implements ResourceServerStore { } - MapResourceServerEntity entity = tx.read(id); + MapResourceServerEntity entity = tx.read(resourceServerStore.getKeyConvertor().fromStringSafe(id)); return entityToAdapter(entity); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java index 4fbea321a8..728fc88ea1 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java @@ -26,7 +26,6 @@ import org.keycloak.authorization.store.ResourceStore; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.map.authorization.adapter.MapResourceAdapter; -import org.keycloak.models.map.authorization.entity.AbstractResourceEntity; import org.keycloak.models.map.authorization.entity.MapResourceEntity; import org.keycloak.models.map.common.Serialization; import org.keycloak.models.map.storage.MapKeycloakTransaction; @@ -35,40 +34,45 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import java.util.Arrays; +import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapResourceStore implements ResourceStore { +public class MapResourceStore> implements ResourceStore { private static final Logger LOG = Logger.getLogger(MapResourceStore.class); private final AuthorizationProvider authorizationProvider; - final MapKeycloakTransaction tx; - private final MapStorage resourceStore; + final MapKeycloakTransaction, Resource> tx; + private final MapStorage, Resource> resourceStore; - public MapResourceStore(KeycloakSession session, MapStorage resourceStore, AuthorizationProvider provider) { + public MapResourceStore(KeycloakSession session, MapStorage, Resource> resourceStore, AuthorizationProvider provider) { this.resourceStore = resourceStore; this.tx = resourceStore.createTransaction(session); session.getTransactionManager().enlist(tx); authorizationProvider = provider; } - private MapResourceEntity registerEntityForChanges(MapResourceEntity origEntity) { - final MapResourceEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapResourceEntity::isUpdated); + private MapResourceEntity registerEntityForChanges(MapResourceEntity origEntity) { + final MapResourceEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapResourceEntity::isUpdated); return res; } - private Resource entityToAdapter(MapResourceEntity origEntity) { + private Resource entityToAdapter(MapResourceEntity origEntity) { if (origEntity == null) return null; // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapResourceAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()); + return new MapResourceAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) { + @Override + public String getId() { + return resourceStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } private ModelCriteriaBuilder forResourceServer(String resourceServerId) { @@ -92,8 +96,8 @@ public class MapResourceStore implements ResourceStore { throw new ModelDuplicateException("Resource with name '" + name + "' for " + resourceServer.getId() + " already exists for request owner " + owner); } - UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id); - MapResourceEntity entity = new MapResourceEntity(uid); + K uid = id == null ? resourceStore.getKeyConvertor().yieldNewUniqueKey(): resourceStore.getKeyConvertor().fromString(id); + MapResourceEntity entity = new MapResourceEntity<>(uid); entity.setName(name); entity.setResourceServerId(resourceServer.getId()); @@ -108,7 +112,7 @@ public class MapResourceStore implements ResourceStore { public void delete(String id) { LOG.tracef("delete(%s)%s", id, getShortStackTrace()); - tx.delete(UUID.fromString(id)); + tx.delete(resourceStore.getKeyConvertor().fromString(id)); } @Override @@ -129,9 +133,10 @@ public class MapResourceStore implements ResourceStore { private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer consumer, int firstResult, int maxResult) { LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace()); + Comparator> c = Comparator.comparing(MapResourceEntity::getId); paginatedStream(tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) .compare(SearchableFields.OWNER, Operator.EQ, ownerId)) - .sorted(MapResourceEntity.COMPARE_BY_ID), firstResult, maxResult) + .sorted(c), firstResult, maxResult) .map(this::entityToAdapter) .forEach(consumer); } @@ -174,7 +179,7 @@ public class MapResourceStore implements ResourceStore { ); return paginatedStream(tx.getUpdatedNotRemoved(mcb) - .sorted(AbstractResourceEntity.COMPARE_BY_NAME), firstResult, maxResult) + .sorted(MapResourceEntity.COMPARE_BY_NAME), firstResult, maxResult) .map(this::entityToAdapter) .collect(Collectors.toList()); } diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java index feaf37813c..4f5707043d 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java @@ -36,36 +36,40 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.stream.Collectors; import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapScopeStore implements ScopeStore { +public class MapScopeStore implements ScopeStore { private static final Logger LOG = Logger.getLogger(MapScopeStore.class); private final AuthorizationProvider authorizationProvider; - final MapKeycloakTransaction tx; - private final MapStorage scopeStore; + final MapKeycloakTransaction, Scope> tx; + private final MapStorage, Scope> scopeStore; - public MapScopeStore(KeycloakSession session, MapStorage scopeStore, AuthorizationProvider provider) { + public MapScopeStore(KeycloakSession session, MapStorage, Scope> scopeStore, AuthorizationProvider provider) { this.authorizationProvider = provider; this.scopeStore = scopeStore; this.tx = scopeStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private MapScopeEntity registerEntityForChanges(MapScopeEntity origEntity) { - final MapScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapScopeEntity::isUpdated); + private MapScopeEntity registerEntityForChanges(MapScopeEntity origEntity) { + final MapScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapScopeEntity::isUpdated); return res; } - private Scope entityToAdapter(MapScopeEntity origEntity) { + private Scope entityToAdapter(MapScopeEntity origEntity) { if (origEntity == null) return null; // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapScopeAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()); + return new MapScopeAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) { + @Override + public String getId() { + return scopeStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } private ModelCriteriaBuilder forResourceServer(String resourceServerId) { @@ -90,8 +94,8 @@ public class MapScopeStore implements ScopeStore { throw new ModelDuplicateException("Scope with name '" + name + "' for " + resourceServer.getId() + " already exists"); } - UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id); - MapScopeEntity entity = new MapScopeEntity(uid); + K uid = id == null ? scopeStore.getKeyConvertor().yieldNewUniqueKey(): scopeStore.getKeyConvertor().fromString(id); + MapScopeEntity entity = new MapScopeEntity<>(uid); entity.setName(name); entity.setResourceServerId(resourceServer.getId()); @@ -104,7 +108,7 @@ public class MapScopeStore implements ScopeStore { @Override public void delete(String id) { LOG.tracef("delete(%s)%s", id, getShortStackTrace()); - tx.delete(UUID.fromString(id)); + tx.delete(scopeStore.getKeyConvertor().fromString(id)); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/AbstractResourceModel.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/AbstractResourceModel.java index fb3301bfb1..9ff1da8f56 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/AbstractResourceModel.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/AbstractResourceModel.java @@ -24,7 +24,7 @@ import org.keycloak.models.map.common.AbstractEntity; import java.util.Objects; -public abstract class AbstractResourceModel extends AbstractAuthorizationModel implements Resource { +public abstract class AbstractResourceModel> extends AbstractAuthorizationModel implements Resource { protected final E entity; diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapPermissionTicketAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapPermissionTicketAdapter.java index 3f349ce2b4..1a02bdeb91 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapPermissionTicketAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapPermissionTicketAdapter.java @@ -23,23 +23,17 @@ import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.store.StoreFactory; + + import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; - -import java.util.UUID; - import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy; -public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel { +public abstract class MapPermissionTicketAdapter> extends AbstractPermissionTicketModel> { - public MapPermissionTicketAdapter(MapPermissionTicketEntity entity, StoreFactory storeFactory) { + public MapPermissionTicketAdapter(MapPermissionTicketEntity entity, StoreFactory storeFactory) { super(entity, storeFactory); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getOwner() { return entity.getOwner(); @@ -52,13 +46,13 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel { +public abstract class MapPolicyAdapter extends AbstractPolicyModel> { - public MapPolicyAdapter(MapPolicyEntity entity, StoreFactory storeFactory) { + public MapPolicyAdapter(MapPolicyEntity entity, StoreFactory storeFactory) { super(entity, storeFactory); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getType() { return entity.getType(); @@ -123,7 +117,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel { public Set getAssociatedPolicies() { String resourceServerId = entity.getResourceServerId(); return entity.getAssociatedPoliciesIds().stream() - .map(policyId -> storeFactory.getPolicyStore().findById(policyId.toString(), resourceServerId)) + .map(policyId -> storeFactory.getPolicyStore().findById(policyId, resourceServerId)) .collect(Collectors.toSet()); } @@ -131,7 +125,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel { public Set getResources() { String resourceServerId = entity.getResourceServerId(); return entity.getResourceIds().stream() - .map(resourceId -> storeFactory.getResourceStore().findById(resourceId.toString(), resourceServerId)) + .map(resourceId -> storeFactory.getResourceStore().findById(resourceId, resourceServerId)) .collect(Collectors.toSet()); } @@ -139,7 +133,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel { public Set getScopes() { String resourceServerId = entity.getResourceServerId(); return entity.getScopeIds().stream() - .map(scopeId -> storeFactory.getScopeStore().findById(scopeId.toString(), resourceServerId)) + .map(scopeId -> storeFactory.getScopeStore().findById(scopeId, resourceServerId)) .collect(Collectors.toSet()); } @@ -157,37 +151,37 @@ public class MapPolicyAdapter extends AbstractPolicyModel { @Override public void addScope(Scope scope) { throwExceptionIfReadonly(); - entity.addScope(UUID.fromString(scope.getId())); + entity.addScope(scope.getId()); } @Override public void removeScope(Scope scope) { throwExceptionIfReadonly(); - entity.removeScope(UUID.fromString(scope.getId())); + entity.removeScope(scope.getId()); } @Override public void addAssociatedPolicy(Policy associatedPolicy) { throwExceptionIfReadonly(); - entity.addAssociatedPolicy(UUID.fromString(associatedPolicy.getId())); + entity.addAssociatedPolicy(associatedPolicy.getId()); } @Override public void removeAssociatedPolicy(Policy associatedPolicy) { throwExceptionIfReadonly(); - entity.removeAssociatedPolicy(UUID.fromString(associatedPolicy.getId())); + entity.removeAssociatedPolicy(associatedPolicy.getId()); } @Override public void addResource(Resource resource) { throwExceptionIfReadonly(); - entity.addResource(UUID.fromString(resource.getId())); + entity.addResource(resource.getId()); } @Override public void removeResource(Resource resource) { throwExceptionIfReadonly(); - entity.removeResource(UUID.fromString(resource.getId())); + entity.removeResource(resource.getId()); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java index 21987877bb..00b3f80465 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java @@ -19,27 +19,21 @@ package org.keycloak.models.map.authorization.adapter; import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.store.StoreFactory; -import org.keycloak.models.map.authorization.entity.MapResourceEntity; +import org.keycloak.models.map.authorization.entity.MapResourceEntity; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; -public class MapResourceAdapter extends AbstractResourceModel { +public abstract class MapResourceAdapter extends AbstractResourceModel> { - public MapResourceAdapter(MapResourceEntity entity, StoreFactory storeFactory) { + public MapResourceAdapter(MapResourceEntity entity, StoreFactory storeFactory) { super(entity, storeFactory); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getName() { return entity.getName(); @@ -88,7 +82,7 @@ public class MapResourceAdapter extends AbstractResourceModel public List getScopes() { return entity.getScopeIds().stream() .map(id -> storeFactory - .getScopeStore().findById(id.toString(), entity.getResourceServerId())) + .getScopeStore().findById(id, entity.getResourceServerId())) .collect(Collectors.toList()); } @@ -127,7 +121,7 @@ public class MapResourceAdapter extends AbstractResourceModel @Override public void updateScopes(Set scopes) { throwExceptionIfReadonly(); - entity.setScopeIds(scopes.stream().map(Scope::getId).map(UUID::fromString).collect(Collectors.toSet())); + entity.setScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toSet())); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceServerAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceServerAdapter.java index 7aa5b8932c..e1aa858392 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceServerAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceServerAdapter.java @@ -23,17 +23,12 @@ import org.keycloak.models.map.authorization.entity.MapResourceServerEntity; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; -public class MapResourceServerAdapter extends AbstractResourceServerModel { +public abstract class MapResourceServerAdapter extends AbstractResourceServerModel> { - public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) { + public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) { super(entity, storeFactory); } - @Override - public String getId() { - return entity.getId(); - } - @Override public boolean isAllowRemoteResourceManagement() { return entity.isAllowRemoteResourceManagement(); diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapScopeAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapScopeAdapter.java index 21080939d9..ce8a6ddc63 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapScopeAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapScopeAdapter.java @@ -22,17 +22,12 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.map.authorization.entity.MapScopeEntity; -public class MapScopeAdapter extends AbstractScopeModel { +public abstract class MapScopeAdapter extends AbstractScopeModel> { - public MapScopeAdapter(MapScopeEntity entity, StoreFactory storeFactory) { + public MapScopeAdapter(MapScopeEntity entity, StoreFactory storeFactory) { super(entity, storeFactory); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getName() { return entity.getName(); diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPermissionTicketEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPermissionTicketEntity.java deleted file mode 100644 index 9f9c4afaa1..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPermissionTicketEntity.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.authorization.entity; - -import org.keycloak.models.map.common.AbstractEntity; - -import java.util.Objects; - -public abstract class AbstractPermissionTicketEntity implements AbstractEntity { - - private final K id; - private String owner; - private String requester; - private Long createdTimestamp; - private Long grantedTimestamp; - private K resourceId; - private K scopeId; - private String resourceServerId; - private K policyId; - private boolean updated = false; - - protected AbstractPermissionTicketEntity(K id) { - this.id = id; - } - - public AbstractPermissionTicketEntity() { - this.id = null; - } - - @Override - public K getId() { - return id; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.updated |= !Objects.equals(this.owner, owner); - this.owner = owner; - } - - public String getRequester() { - return requester; - } - - public void setRequester(String requester) { - this.updated |= !Objects.equals(this.requester, requester); - this.requester = requester; - } - - public Long getCreatedTimestamp() { - return createdTimestamp; - } - - public void setCreatedTimestamp(Long createdTimestamp) { - this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp); - this.createdTimestamp = createdTimestamp; - } - - public Long getGrantedTimestamp() { - return grantedTimestamp; - } - - public void setGrantedTimestamp(Long grantedTimestamp) { - this.updated |= !Objects.equals(this.grantedTimestamp, grantedTimestamp); - this.grantedTimestamp = grantedTimestamp; - } - - public K getResourceId() { - return resourceId; - } - - public void setResourceId(K resourceId) { - this.updated |= !Objects.equals(this.resourceId, resourceId); - this.resourceId = resourceId; - } - - public K getScopeId() { - return scopeId; - } - - public void setScopeId(K scopeId) { - this.updated |= !Objects.equals(this.scopeId, scopeId); - this.scopeId = scopeId; - } - - public String getResourceServerId() { - return resourceServerId; - } - - public void setResourceServerId(String resourceServerId) { - this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); - this.resourceServerId = resourceServerId; - } - - public K getPolicyId() { - return policyId; - } - - public void setPolicyId(K policyId) { - this.updated |= !Objects.equals(this.policyId, policyId); - this.policyId = policyId; - } - - @Override - public boolean isUpdated() { - return updated; - } - -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPolicyEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPolicyEntity.java deleted file mode 100644 index e75afcb611..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractPolicyEntity.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.authorization.entity; - -import org.keycloak.models.map.common.AbstractEntity; -import org.keycloak.representations.idm.authorization.DecisionStrategy; -import org.keycloak.representations.idm.authorization.Logic; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.Map; -import java.util.Objects; - -public abstract class AbstractPolicyEntity implements AbstractEntity { - - public static final Comparator> COMPARE_BY_NAME = Comparator.comparing(AbstractPolicyEntity::getName); - - private final K id; - private String name; - private String description; - private String type; - private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; - private Logic logic = Logic.POSITIVE; - private final Map config = new HashMap<>(); - private String resourceServerId; - private final Set associatedPoliciesIds = new HashSet<>(); - private final Set resourceIds = new HashSet<>(); - private final Set scopeIds = new HashSet<>(); - private String owner; - private boolean updated = false; - - protected AbstractPolicyEntity(K id) { - this.id = id; - } - - public AbstractPolicyEntity() { - this.id = null; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= !Objects.equals(this.name, name); - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated |= !Objects.equals(this.description, description); - this.description = description; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.updated |= !Objects.equals(this.type, type); - this.type = type; - } - - public DecisionStrategy getDecisionStrategy() { - return decisionStrategy; - } - - public void setDecisionStrategy(DecisionStrategy decisionStrategy) { - this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy); - this.decisionStrategy = decisionStrategy; - } - - public Logic getLogic() { - return logic; - } - - public void setLogic(Logic logic) { - this.updated |= !Objects.equals(this.logic, logic); - this.logic = logic; - } - - public Map getConfig() { - return config; - } - - public String getConfigValue(String name) { - return config.get(name); - } - - public void setConfig(Map config) { - if (Objects.equals(this.config, config)) return; - - this.updated = true; - this.config.clear(); - if (config != null) { - this.config.putAll(config); - } - } - - public void removeConfig(String name) { - this.updated |= this.config.remove(name) != null; - } - - public void putConfig(String name, String value) { - this.updated |= !Objects.equals(value, this.config.put(name, value)); - } - - public String getResourceServerId() { - return resourceServerId; - } - - public void setResourceServerId(String resourceServerId) { - this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); - this.resourceServerId = resourceServerId; - } - - public Set getAssociatedPoliciesIds() { - return associatedPoliciesIds; - } - - public void addAssociatedPolicy(K policyId) { - this.updated |= this.associatedPoliciesIds.add(policyId); - } - - public void removeAssociatedPolicy(K policyId) { - this.updated |= this.associatedPoliciesIds.remove(policyId); - } - - public Set getResourceIds() { - return resourceIds; - } - - public void addResource(K resourceId) { - this.updated |= this.resourceIds.add(resourceId); - } - - public void removeResource(K resourceId) { - this.updated |= this.resourceIds.remove(resourceId); - } - - public Set getScopeIds() { - return scopeIds; - } - - public void addScope(K scopeId) { - this.updated |= this.scopeIds.add(scopeId); - } - - public void removeScope(K scopeId) { - this.updated |= this.scopeIds.remove(scopeId); - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.updated |= !Objects.equals(this.owner, owner); - this.owner = owner; - } - - @Override - public K getId() { - return id; - } - - @Override - public boolean isUpdated() { - return updated; - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceEntity.java deleted file mode 100644 index be8f19366e..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceEntity.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.authorization.entity; - -import org.keycloak.models.map.common.AbstractEntity; - -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -public abstract class AbstractResourceEntity implements AbstractEntity { - - public static final Comparator> COMPARE_BY_NAME = Comparator.comparing(AbstractResourceEntity::getName); - - private final K id; - private String name; - private String displayName; - private final Set uris = new HashSet<>(); - private String type; - private String iconUri; - private String owner; - private boolean ownerManagedAccess; - private String resourceServerId; - private final Set scopeIds = new HashSet<>(); - private final Set policyIds = new HashSet<>(); - private final Map> attributes = new HashMap<>(); - private boolean updated = false; - - protected AbstractResourceEntity(K id) { - this.id = id; - } - - public AbstractResourceEntity() { - this.id = null; - } - - @Override - public K getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= !Objects.equals(this.name, name); - this.name = name; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.updated |= !Objects.equals(this.displayName, displayName); - this.displayName = displayName; - } - - public Set getUris() { - return uris; - } - - public void setUris(Set uris) { - if (Objects.equals(this.uris, uris)) return; - - this.updated = true; - this.uris.clear(); - - if (uris != null) { - this.uris.addAll(uris); - } - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.updated |= !Objects.equals(this.type, type); - this.type = type; - } - - public String getIconUri() { - return iconUri; - } - - public void setIconUri(String iconUri) { - this.updated |= !Objects.equals(this.iconUri, iconUri); - this.iconUri = iconUri; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.updated |= !Objects.equals(this.owner, owner); - this.owner = owner; - } - - public boolean isOwnerManagedAccess() { - return ownerManagedAccess; - } - - public void setOwnerManagedAccess(boolean ownerManagedAccess) { - this.updated |= this.ownerManagedAccess != ownerManagedAccess; - this.ownerManagedAccess = ownerManagedAccess; - } - - public String getResourceServerId() { - return resourceServerId; - } - - public void setResourceServerId(String resourceServerId) { - this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); - this.resourceServerId = resourceServerId; - } - - public Set getScopeIds() { - return scopeIds; - } - - public void setScopeIds(Set scopeIds) { - if (Objects.equals(this.scopeIds, scopeIds)) return; - - this.updated = true; - this.scopeIds.clear(); - if (scopeIds != null) { - this.scopeIds.addAll(scopeIds); - } - } - - public Set getPolicyIds() { - return policyIds; - } - - public Map> getAttributes() { - return attributes; - } - - public List getAttribute(String name) { - return attributes.get(name); - } - - public String getSingleAttribute(String name) { - List attributeValues = attributes.get(name); - return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0); - } - - public void setAttribute(String name, List value) { - this.updated |= !Objects.equals(this.attributes.put(name, value), value); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - @Override - public boolean isUpdated() { - return updated; - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceServerEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceServerEntity.java deleted file mode 100644 index f8bbacb897..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractResourceServerEntity.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.authorization.entity; - -import org.keycloak.models.map.common.AbstractEntity; -import org.keycloak.representations.idm.authorization.DecisionStrategy; -import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; - -import java.util.Objects; - -public abstract class AbstractResourceServerEntity implements AbstractEntity { - - private final K id; - private boolean updated = false; - - private boolean allowRemoteResourceManagement; - private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING; - private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; - - protected AbstractResourceServerEntity(K id) { - this.id = id; - } - - public AbstractResourceServerEntity() { - this.id = null; - } - - @Override - public K getId() { - return id; - } - - public boolean isAllowRemoteResourceManagement() { - return allowRemoteResourceManagement; - } - - public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { - this.updated |= this.allowRemoteResourceManagement != allowRemoteResourceManagement; - this.allowRemoteResourceManagement = allowRemoteResourceManagement; - } - - public PolicyEnforcementMode getPolicyEnforcementMode() { - return policyEnforcementMode; - } - - public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) { - this.updated |= !Objects.equals(this.policyEnforcementMode, policyEnforcementMode); - this.policyEnforcementMode = policyEnforcementMode; - } - - public DecisionStrategy getDecisionStrategy() { - return decisionStrategy; - } - - public void setDecisionStrategy(DecisionStrategy decisionStrategy) { - this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy); - this.decisionStrategy = decisionStrategy; - } - - @Override - public boolean isUpdated() { - return updated; - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractScopeEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractScopeEntity.java deleted file mode 100644 index cfcb202ce6..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/AbstractScopeEntity.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.authorization.entity; - -import org.keycloak.models.map.common.AbstractEntity; - -import java.util.Objects; - -public abstract class AbstractScopeEntity implements AbstractEntity { - - private final K id; - private String name; - private String displayName; - private String iconUri; - private String resourceServerId; - private boolean updated = false; - - protected AbstractScopeEntity(K id) { - this.id = id; - } - - public AbstractScopeEntity() { - this.id = null; - } - - @Override - public K getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= !Objects.equals(this.name, name); - this.name = name; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.updated |= !Objects.equals(this.displayName, displayName); - this.displayName = displayName; - } - - public String getIconUri() { - return iconUri; - } - - public void setIconUri(String iconUri) { - this.updated |= !Objects.equals(this.iconUri, iconUri); - this.iconUri = iconUri; - } - - public String getResourceServerId() { - return resourceServerId; - } - - public void setResourceServerId(String resourceServerId) { - this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); - this.resourceServerId = resourceServerId; - } - - @Override - public boolean isUpdated() { - return updated; - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPermissionTicketEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPermissionTicketEntity.java index 8932d5273e..f6e383b07e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPermissionTicketEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPermissionTicketEntity.java @@ -17,21 +17,114 @@ package org.keycloak.models.map.authorization.entity; +import org.keycloak.models.map.common.AbstractEntity; + import java.util.Comparator; -import java.util.UUID; +import java.util.Objects; -public class MapPermissionTicketEntity extends AbstractPermissionTicketEntity { +public class MapPermissionTicketEntity implements AbstractEntity { - public static final Comparator> COMPARE_BY_ID = Comparator.comparing(AbstractPermissionTicketEntity::getId); - public static final Comparator> COMPARE_BY_RESOURCE_ID = Comparator.comparing(AbstractPermissionTicketEntity::getResourceId); + public static final Comparator> COMPARE_BY_RESOURCE_ID = Comparator.comparing(MapPermissionTicketEntity::getResourceId); + private final K id; + private String owner; + private String requester; + private Long createdTimestamp; + private Long grantedTimestamp; + private String resourceId; + private String scopeId; + private String resourceServerId; + private String policyId; + private boolean updated = false; - protected MapPermissionTicketEntity() { - super(); + public MapPermissionTicketEntity(K id) { + this.id = id; } - public MapPermissionTicketEntity(UUID id) { - super(id); + public MapPermissionTicketEntity() { + this.id = null; + } + + @Override + public K getId() { + return id; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.updated |= !Objects.equals(this.owner, owner); + this.owner = owner; + } + + public String getRequester() { + return requester; + } + + public void setRequester(String requester) { + this.updated |= !Objects.equals(this.requester, requester); + this.requester = requester; + } + + public Long getCreatedTimestamp() { + return createdTimestamp; + } + + public void setCreatedTimestamp(Long createdTimestamp) { + this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp); + this.createdTimestamp = createdTimestamp; + } + + public Long getGrantedTimestamp() { + return grantedTimestamp; + } + + public void setGrantedTimestamp(Long grantedTimestamp) { + this.updated |= !Objects.equals(this.grantedTimestamp, grantedTimestamp); + this.grantedTimestamp = grantedTimestamp; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.updated |= !Objects.equals(this.resourceId, resourceId); + this.resourceId = resourceId; + } + + public String getScopeId() { + return scopeId; + } + + public void setScopeId(String scopeId) { + this.updated |= !Objects.equals(this.scopeId, scopeId); + this.scopeId = scopeId; + } + + public String getResourceServerId() { + return resourceServerId; + } + + public void setResourceServerId(String resourceServerId) { + this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); + this.resourceServerId = resourceServerId; + } + + public String getPolicyId() { + return policyId; + } + + public void setPolicyId(String policyId) { + this.updated |= !Objects.equals(this.policyId, policyId); + this.policyId = policyId; + } + + @Override + public boolean isUpdated() { + return updated; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPolicyEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPolicyEntity.java index 240d8a7644..73a9c21a4b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPolicyEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapPolicyEntity.java @@ -17,15 +17,176 @@ package org.keycloak.models.map.authorization.entity; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.Logic; -public class MapPolicyEntity extends AbstractPolicyEntity { - protected MapPolicyEntity() { - super(); +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.Map; +import java.util.Objects; + +public class MapPolicyEntity implements AbstractEntity { + + public static final Comparator> COMPARE_BY_NAME = Comparator.comparing(MapPolicyEntity::getName); + + private final K id; + private String name; + private String description; + private String type; + private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; + private Logic logic = Logic.POSITIVE; + private final Map config = new HashMap<>(); + private String resourceServerId; + private final Set associatedPoliciesIds = new HashSet<>(); + private final Set resourceIds = new HashSet<>(); + private final Set scopeIds = new HashSet<>(); + private String owner; + private boolean updated = false; + + public MapPolicyEntity(K id) { + this.id = id; } - public MapPolicyEntity(UUID id) { - super(id); + public MapPolicyEntity() { + this.id = null; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= !Objects.equals(this.name, name); + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.updated |= !Objects.equals(this.description, description); + this.description = description; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.updated |= !Objects.equals(this.type, type); + this.type = type; + } + + public DecisionStrategy getDecisionStrategy() { + return decisionStrategy; + } + + public void setDecisionStrategy(DecisionStrategy decisionStrategy) { + this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy); + this.decisionStrategy = decisionStrategy; + } + + public Logic getLogic() { + return logic; + } + + public void setLogic(Logic logic) { + this.updated |= !Objects.equals(this.logic, logic); + this.logic = logic; + } + + public Map getConfig() { + return config; + } + + public String getConfigValue(String name) { + return config.get(name); + } + + public void setConfig(Map config) { + if (Objects.equals(this.config, config)) return; + + this.updated = true; + this.config.clear(); + if (config != null) { + this.config.putAll(config); + } + } + + public void removeConfig(String name) { + this.updated |= this.config.remove(name) != null; + } + + public void putConfig(String name, String value) { + this.updated |= !Objects.equals(value, this.config.put(name, value)); + } + + public String getResourceServerId() { + return resourceServerId; + } + + public void setResourceServerId(String resourceServerId) { + this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); + this.resourceServerId = resourceServerId; + } + + public Set getAssociatedPoliciesIds() { + return associatedPoliciesIds; + } + + public void addAssociatedPolicy(String policyId) { + this.updated |= this.associatedPoliciesIds.add(policyId); + } + + public void removeAssociatedPolicy(String policyId) { + this.updated |= this.associatedPoliciesIds.remove(policyId); + } + + public Set getResourceIds() { + return resourceIds; + } + + public void addResource(String resourceId) { + this.updated |= this.resourceIds.add(resourceId); + } + + public void removeResource(String resourceId) { + this.updated |= this.resourceIds.remove(resourceId); + } + + public Set getScopeIds() { + return scopeIds; + } + + public void addScope(String scopeId) { + this.updated |= this.scopeIds.add(scopeId); + } + + public void removeScope(String scopeId) { + this.updated |= this.scopeIds.remove(scopeId); + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.updated |= !Objects.equals(this.owner, owner); + this.owner = owner; + } + + @Override + public K getId() { + return id; + } + + @Override + public boolean isUpdated() { + return updated; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java index c41c65ee38..41e2c32922 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java @@ -17,18 +17,167 @@ package org.keycloak.models.map.authorization.entity; +import org.keycloak.models.map.common.AbstractEntity; + import java.util.Comparator; -import java.util.UUID; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; -public class MapResourceEntity extends AbstractResourceEntity { - public static final Comparator> COMPARE_BY_ID = Comparator.comparing(AbstractResourceEntity::getId); +public class MapResourceEntity implements AbstractEntity { + + public static final Comparator> COMPARE_BY_NAME = Comparator.comparing(MapResourceEntity::getName); - protected MapResourceEntity() { - super(); + private final K id; + private String name; + private String displayName; + private final Set uris = new HashSet<>(); + private String type; + private String iconUri; + private String owner; + private boolean ownerManagedAccess; + private String resourceServerId; + private final Set scopeIds = new HashSet<>(); + private final Set policyIds = new HashSet<>(); + private final Map> attributes = new HashMap<>(); + private boolean updated = false; + + public MapResourceEntity(K id) { + this.id = id; } - public MapResourceEntity(UUID id) { - super(id); + public MapResourceEntity() { + this.id = null; + } + + @Override + public K getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= !Objects.equals(this.name, name); + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.updated |= !Objects.equals(this.displayName, displayName); + this.displayName = displayName; + } + + public Set getUris() { + return uris; + } + + public void setUris(Set uris) { + if (Objects.equals(this.uris, uris)) return; + + this.updated = true; + this.uris.clear(); + + if (uris != null) { + this.uris.addAll(uris); + } + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.updated |= !Objects.equals(this.type, type); + this.type = type; + } + + public String getIconUri() { + return iconUri; + } + + public void setIconUri(String iconUri) { + this.updated |= !Objects.equals(this.iconUri, iconUri); + this.iconUri = iconUri; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.updated |= !Objects.equals(this.owner, owner); + this.owner = owner; + } + + public boolean isOwnerManagedAccess() { + return ownerManagedAccess; + } + + public void setOwnerManagedAccess(boolean ownerManagedAccess) { + this.updated |= this.ownerManagedAccess != ownerManagedAccess; + this.ownerManagedAccess = ownerManagedAccess; + } + + public String getResourceServerId() { + return resourceServerId; + } + + public void setResourceServerId(String resourceServerId) { + this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); + this.resourceServerId = resourceServerId; + } + + public Set getScopeIds() { + return scopeIds; + } + + public void setScopeIds(Set scopeIds) { + if (Objects.equals(this.scopeIds, scopeIds)) return; + + this.updated = true; + this.scopeIds.clear(); + if (scopeIds != null) { + this.scopeIds.addAll(scopeIds); + } + } + + public Set getPolicyIds() { + return policyIds; + } + + public Map> getAttributes() { + return attributes; + } + + public List getAttribute(String name) { + return attributes.get(name); + } + + public String getSingleAttribute(String name) { + List attributeValues = attributes.get(name); + return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0); + } + + public void setAttribute(String name, List value) { + this.updated |= !Objects.equals(this.attributes.put(name, value), value); + } + + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + @Override + public boolean isUpdated() { + return updated; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceServerEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceServerEntity.java index a78ca804c4..00776bf771 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceServerEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceServerEntity.java @@ -17,13 +17,64 @@ package org.keycloak.models.map.authorization.entity; -public class MapResourceServerEntity extends AbstractResourceServerEntity { - protected MapResourceServerEntity() { - super(); +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; + +import java.util.Objects; + +public class MapResourceServerEntity implements AbstractEntity { + + private final K id; + private boolean updated = false; + + private boolean allowRemoteResourceManagement; + private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING; + private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; + + public MapResourceServerEntity(K id) { + this.id = id; } - public MapResourceServerEntity(String id) { - super(id); + public MapResourceServerEntity() { + this.id = null; + } + + @Override + public K getId() { + return id; + } + + public boolean isAllowRemoteResourceManagement() { + return allowRemoteResourceManagement; + } + + public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) { + this.updated |= this.allowRemoteResourceManagement != allowRemoteResourceManagement; + this.allowRemoteResourceManagement = allowRemoteResourceManagement; + } + + public PolicyEnforcementMode getPolicyEnforcementMode() { + return policyEnforcementMode; + } + + public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) { + this.updated |= !Objects.equals(this.policyEnforcementMode, policyEnforcementMode); + this.policyEnforcementMode = policyEnforcementMode; + } + + public DecisionStrategy getDecisionStrategy() { + return decisionStrategy; + } + + public void setDecisionStrategy(DecisionStrategy decisionStrategy) { + this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy); + this.decisionStrategy = decisionStrategy; + } + + @Override + public boolean isUpdated() { + return updated; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapScopeEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapScopeEntity.java index 52776ae439..b32461c4b5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapScopeEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapScopeEntity.java @@ -17,15 +17,71 @@ package org.keycloak.models.map.authorization.entity; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractEntity; -public class MapScopeEntity extends AbstractScopeEntity { - protected MapScopeEntity() { - super(); +import java.util.Objects; + +public class MapScopeEntity implements AbstractEntity { + + private final K id; + private String name; + private String displayName; + private String iconUri; + private String resourceServerId; + private boolean updated = false; + + public MapScopeEntity(K id) { + this.id = id; } - public MapScopeEntity(UUID id) { - super(id); + public MapScopeEntity() { + this.id = null; + } + + @Override + public K getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= !Objects.equals(this.name, name); + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.updated |= !Objects.equals(this.displayName, displayName); + this.displayName = displayName; + } + + public String getIconUri() { + return iconUri; + } + + public void setIconUri(String iconUri) { + this.updated |= !Objects.equals(this.iconUri, iconUri); + this.iconUri = iconUri; + } + + public String getResourceServerId() { + return resourceServerId; + } + + public void setResourceServerId(String resourceServerId) { + this.updated |= !Objects.equals(this.resourceServerId, resourceServerId); + this.resourceServerId = resourceServerId; + } + + @Override + public boolean isUpdated() { + return updated; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientEntity.java b/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientEntity.java deleted file mode 100644 index 5ba50aa490..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientEntity.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright 2020 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.models.map.client; - -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.map.common.AbstractEntity; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * - * @author hmlnarik - */ -public abstract class AbstractClientEntity implements AbstractEntity { - - private K id; - private String realmId; - - private String clientId; - private String name; - private String description; - private Set redirectUris = new HashSet<>(); - private boolean enabled; - private boolean alwaysDisplayInConsole; - private String clientAuthenticatorType; - private String secret; - private String registrationToken; - private String protocol; - private Map attributes = new HashMap<>(); - private Map authFlowBindings = new HashMap<>(); - private boolean publicClient; - private boolean fullScopeAllowed; - private boolean frontchannelLogout; - private int notBefore; - private Set scope = new HashSet<>(); - private Set webOrigins = new HashSet<>(); - private Map protocolMappers = new HashMap<>(); - private Map clientScopes = new HashMap<>(); - private Set scopeMappings = new LinkedHashSet<>(); - private boolean surrogateAuthRequired; - private String managementUrl; - private String rootUrl; - private String baseUrl; - private boolean bearerOnly; - private boolean consentRequired; - private boolean standardFlowEnabled; - private boolean implicitFlowEnabled; - private boolean directAccessGrantsEnabled; - private boolean serviceAccountsEnabled; - private int nodeReRegistrationTimeout; - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected AbstractClientEntity() { - this.id = null; - this.realmId = null; - } - - public AbstractClientEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.updated |= ! Objects.equals(this.clientId, clientId); - this.clientId = clientId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated |= ! Objects.equals(this.description, description); - this.description = description; - } - - public Set getRedirectUris() { - return redirectUris; - } - - public void setRedirectUris(Set redirectUris) { - this.updated |= ! Objects.equals(this.redirectUris, redirectUris); - this.redirectUris = redirectUris; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.updated |= ! Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public boolean isAlwaysDisplayInConsole() { - return alwaysDisplayInConsole; - } - - public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) { - this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole); - this.alwaysDisplayInConsole = alwaysDisplayInConsole; - } - - public String getClientAuthenticatorType() { - return clientAuthenticatorType; - } - - public void setClientAuthenticatorType(String clientAuthenticatorType) { - this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType); - this.clientAuthenticatorType = clientAuthenticatorType; - } - - public String getSecret() { - return secret; - } - - public void setSecret(String secret) { - this.updated |= ! Objects.equals(this.secret, secret); - this.secret = secret; - } - - public String getRegistrationToken() { - return registrationToken; - } - - public void setRegistrationToken(String registrationToken) { - this.updated |= ! Objects.equals(this.registrationToken, registrationToken); - this.registrationToken = registrationToken; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.updated |= ! Objects.equals(this.protocol, protocol); - this.protocol = protocol; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.updated |= ! Objects.equals(this.attributes, attributes); - this.attributes = attributes; - } - - public Map getAuthFlowBindings() { - return authFlowBindings; - } - - public void setAuthFlowBindings(Map authFlowBindings) { - this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings); - this.authFlowBindings = authFlowBindings; - } - - public boolean isPublicClient() { - return publicClient; - } - - public void setPublicClient(boolean publicClient) { - this.updated |= ! Objects.equals(this.publicClient, publicClient); - this.publicClient = publicClient; - } - - public boolean isFullScopeAllowed() { - return fullScopeAllowed; - } - - public void setFullScopeAllowed(boolean fullScopeAllowed) { - this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed); - this.fullScopeAllowed = fullScopeAllowed; - } - - public boolean isFrontchannelLogout() { - return frontchannelLogout; - } - - public void setFrontchannelLogout(boolean frontchannelLogout) { - this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout); - this.frontchannelLogout = frontchannelLogout; - } - - public int getNotBefore() { - return notBefore; - } - - public void setNotBefore(int notBefore) { - this.updated |= ! Objects.equals(this.notBefore, notBefore); - this.notBefore = notBefore; - } - - public Set getScope() { - return scope; - } - - public void setScope(Set scope) { - this.updated |= ! Objects.equals(this.scope, scope); - this.scope.clear(); - this.scope.addAll(scope); - } - - public Set getWebOrigins() { - return webOrigins; - } - - public void setWebOrigins(Set webOrigins) { - this.updated |= ! Objects.equals(this.webOrigins, webOrigins); - this.webOrigins.clear(); - this.webOrigins.addAll(webOrigins); - } - - public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { - Objects.requireNonNull(model.getId(), "protocolMapper.id"); - updated = true; - this.protocolMappers.put(model.getId(), model); - return model; - } - - public Collection getProtocolMappers() { - return protocolMappers.values(); - } - - public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { - updated = true; - protocolMappers.put(id, mapping); - } - - public void removeProtocolMapper(String id) { - updated |= protocolMappers.remove(id) != null; - } - - public void setProtocolMappers(Collection protocolMappers) { - this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); - this.protocolMappers.clear(); - this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); - } - - public ProtocolMapperModel getProtocolMapperById(String id) { - return id == null ? null : protocolMappers.get(id); - } - - public boolean isSurrogateAuthRequired() { - return surrogateAuthRequired; - } - - public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { - this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired); - this.surrogateAuthRequired = surrogateAuthRequired; - } - - public String getManagementUrl() { - return managementUrl; - } - - public void setManagementUrl(String managementUrl) { - this.updated |= ! Objects.equals(this.managementUrl, managementUrl); - this.managementUrl = managementUrl; - } - - public String getRootUrl() { - return rootUrl; - } - - public void setRootUrl(String rootUrl) { - this.updated |= ! Objects.equals(this.rootUrl, rootUrl); - this.rootUrl = rootUrl; - } - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.updated |= ! Objects.equals(this.baseUrl, baseUrl); - this.baseUrl = baseUrl; - } - - public boolean isBearerOnly() { - return bearerOnly; - } - - public void setBearerOnly(boolean bearerOnly) { - this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly); - this.bearerOnly = bearerOnly; - } - - public boolean isConsentRequired() { - return consentRequired; - } - - public void setConsentRequired(boolean consentRequired) { - this.updated |= ! Objects.equals(this.consentRequired, consentRequired); - this.consentRequired = consentRequired; - } - - public boolean isStandardFlowEnabled() { - return standardFlowEnabled; - } - - public void setStandardFlowEnabled(boolean standardFlowEnabled) { - this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled); - this.standardFlowEnabled = standardFlowEnabled; - } - - public boolean isImplicitFlowEnabled() { - return implicitFlowEnabled; - } - - public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { - this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled); - this.implicitFlowEnabled = implicitFlowEnabled; - } - - public boolean isDirectAccessGrantsEnabled() { - return directAccessGrantsEnabled; - } - - public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { - this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled); - this.directAccessGrantsEnabled = directAccessGrantsEnabled; - } - - public boolean isServiceAccountsEnabled() { - return serviceAccountsEnabled; - } - - public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { - this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled); - this.serviceAccountsEnabled = serviceAccountsEnabled; - } - - public int getNodeReRegistrationTimeout() { - return nodeReRegistrationTimeout; - } - - public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { - this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout); - this.nodeReRegistrationTimeout = nodeReRegistrationTimeout; - } - - public void addWebOrigin(String webOrigin) { - updated = true; - this.webOrigins.add(webOrigin); - } - - public void removeWebOrigin(String webOrigin) { - updated |= this.webOrigins.remove(webOrigin); - } - - public void addRedirectUri(String redirectUri) { - this.updated |= ! this.redirectUris.contains(redirectUri); - this.redirectUris.add(redirectUri); - } - - public void removeRedirectUri(String redirectUri) { - updated |= this.redirectUris.remove(redirectUri); - } - - public void setAttribute(String name, String value) { - this.updated = true; - this.attributes.put(name, value); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - public String getAttribute(String name) { - return this.attributes.get(name); - } - - public String getAuthenticationFlowBindingOverride(String binding) { - return this.authFlowBindings.get(binding); - } - - public Map getAuthenticationFlowBindingOverrides() { - return this.authFlowBindings; - } - - public void removeAuthenticationFlowBindingOverride(String binding) { - updated |= this.authFlowBindings.remove(binding) != null; - } - - public void setAuthenticationFlowBindingOverride(String binding, String flowId) { - this.updated = true; - this.authFlowBindings.put(binding, flowId); - } - - public Collection getScopeMappings() { - return scopeMappings; - } - - public void addScopeMapping(String id) { - if (id != null) { - updated = true; - scopeMappings.add(id); - } - } - - public void deleteScopeMapping(String id) { - updated |= scopeMappings.remove(id); - } - - public void addClientScope(String id, boolean defaultScope) { - if (id != null) { - updated = true; - this.clientScopes.put(id, defaultScope); - } - } - - public void removeClientScope(String id) { - if (id != null) { - updated |= clientScopes.remove(id) != null; - } - } - - public Stream getClientScopes(boolean defaultScope) { - return this.clientScopes.entrySet().stream() - .filter(me -> Objects.equals(me.getValue(), defaultScope)) - .map(Entry::getKey); - } - - public String getRealmId() { - return this.realmId; - } - -} 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 9bb5ba5223..253763b81d 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 @@ -33,17 +33,12 @@ import java.util.stream.Stream; * * @author hmlnarik */ -public abstract class MapClientAdapter extends AbstractClientModel implements ClientModel { +public abstract class MapClientAdapter extends AbstractClientModel> implements ClientModel { - public MapClientAdapter(KeycloakSession session, RealmModel realm, MapClientEntity entity) { + public MapClientAdapter(KeycloakSession session, RealmModel realm, MapClientEntity entity) { super(session, realm, entity); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getClientId() { return entity.getClientId(); diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java index 1a9a7cb095..77278e967f 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java @@ -1,13 +1,13 @@ /* * Copyright 2020 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,20 +16,467 @@ */ package org.keycloak.models.map.client; -import java.util.UUID; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.map.common.AbstractEntity; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * * @author hmlnarik */ -public class MapClientEntity extends AbstractClientEntity { +public class MapClientEntity implements AbstractEntity { + + private K id; + private String realmId; + + private String clientId; + private String name; + private String description; + private Set redirectUris = new HashSet<>(); + private boolean enabled; + private boolean alwaysDisplayInConsole; + private String clientAuthenticatorType; + private String secret; + private String registrationToken; + private String protocol; + private Map attributes = new HashMap<>(); + private Map authFlowBindings = new HashMap<>(); + private boolean publicClient; + private boolean fullScopeAllowed; + private boolean frontchannelLogout; + private int notBefore; + private Set scope = new HashSet<>(); + private Set webOrigins = new HashSet<>(); + private Map protocolMappers = new HashMap<>(); + private Map clientScopes = new HashMap<>(); + private Set scopeMappings = new LinkedHashSet<>(); + private boolean surrogateAuthRequired; + private String managementUrl; + private String rootUrl; + private String baseUrl; + private boolean bearerOnly; + private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; + private boolean serviceAccountsEnabled; + private int nodeReRegistrationTimeout; + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; protected MapClientEntity() { - super(); + this.id = null; + this.realmId = null; } - public MapClientEntity(UUID id, String realmId) { - super(id, realmId); + public MapClientEntity(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; + } + + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.updated |= ! Objects.equals(this.clientId, clientId); + this.clientId = clientId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.updated |= ! Objects.equals(this.description, description); + this.description = description; + } + + public Set getRedirectUris() { + return redirectUris; + } + + public void setRedirectUris(Set redirectUris) { + this.updated |= ! Objects.equals(this.redirectUris, redirectUris); + this.redirectUris = redirectUris; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.updated |= ! Objects.equals(this.enabled, enabled); + this.enabled = enabled; + } + + public boolean isAlwaysDisplayInConsole() { + return alwaysDisplayInConsole; + } + + public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) { + this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole); + this.alwaysDisplayInConsole = alwaysDisplayInConsole; + } + + public String getClientAuthenticatorType() { + return clientAuthenticatorType; + } + + public void setClientAuthenticatorType(String clientAuthenticatorType) { + this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType); + this.clientAuthenticatorType = clientAuthenticatorType; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.updated |= ! Objects.equals(this.secret, secret); + this.secret = secret; + } + + public String getRegistrationToken() { + return registrationToken; + } + + public void setRegistrationToken(String registrationToken) { + this.updated |= ! Objects.equals(this.registrationToken, registrationToken); + this.registrationToken = registrationToken; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.updated |= ! Objects.equals(this.protocol, protocol); + this.protocol = protocol; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.updated |= ! Objects.equals(this.attributes, attributes); + this.attributes = attributes; + } + + public Map getAuthFlowBindings() { + return authFlowBindings; + } + + public void setAuthFlowBindings(Map authFlowBindings) { + this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings); + this.authFlowBindings = authFlowBindings; + } + + public boolean isPublicClient() { + return publicClient; + } + + public void setPublicClient(boolean publicClient) { + this.updated |= ! Objects.equals(this.publicClient, publicClient); + this.publicClient = publicClient; + } + + public boolean isFullScopeAllowed() { + return fullScopeAllowed; + } + + public void setFullScopeAllowed(boolean fullScopeAllowed) { + this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed); + this.fullScopeAllowed = fullScopeAllowed; + } + + public boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + public void setFrontchannelLogout(boolean frontchannelLogout) { + this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout); + this.frontchannelLogout = frontchannelLogout; + } + + public int getNotBefore() { + return notBefore; + } + + public void setNotBefore(int notBefore) { + this.updated |= ! Objects.equals(this.notBefore, notBefore); + this.notBefore = notBefore; + } + + public Set getScope() { + return scope; + } + + public void setScope(Set scope) { + this.updated |= ! Objects.equals(this.scope, scope); + this.scope.clear(); + this.scope.addAll(scope); + } + + public Set getWebOrigins() { + return webOrigins; + } + + public void setWebOrigins(Set webOrigins) { + this.updated |= ! Objects.equals(this.webOrigins, webOrigins); + this.webOrigins.clear(); + this.webOrigins.addAll(webOrigins); + } + + public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { + Objects.requireNonNull(model.getId(), "protocolMapper.id"); + updated = true; + this.protocolMappers.put(model.getId(), model); + return model; + } + + public Collection getProtocolMappers() { + return protocolMappers.values(); + } + + public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { + updated = true; + protocolMappers.put(id, mapping); + } + + public void removeProtocolMapper(String id) { + updated |= protocolMappers.remove(id) != null; + } + + public void setProtocolMappers(Collection protocolMappers) { + this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); + this.protocolMappers.clear(); + this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); + } + + public ProtocolMapperModel getProtocolMapperById(String id) { + return id == null ? null : protocolMappers.get(id); + } + + public boolean isSurrogateAuthRequired() { + return surrogateAuthRequired; + } + + public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { + this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired); + this.surrogateAuthRequired = surrogateAuthRequired; + } + + public String getManagementUrl() { + return managementUrl; + } + + public void setManagementUrl(String managementUrl) { + this.updated |= ! Objects.equals(this.managementUrl, managementUrl); + this.managementUrl = managementUrl; + } + + public String getRootUrl() { + return rootUrl; + } + + public void setRootUrl(String rootUrl) { + this.updated |= ! Objects.equals(this.rootUrl, rootUrl); + this.rootUrl = rootUrl; + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.updated |= ! Objects.equals(this.baseUrl, baseUrl); + this.baseUrl = baseUrl; + } + + public boolean isBearerOnly() { + return bearerOnly; + } + + public void setBearerOnly(boolean bearerOnly) { + this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly); + this.bearerOnly = bearerOnly; + } + + public boolean isConsentRequired() { + return consentRequired; + } + + public void setConsentRequired(boolean consentRequired) { + this.updated |= ! Objects.equals(this.consentRequired, consentRequired); + this.consentRequired = consentRequired; + } + + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled); + this.standardFlowEnabled = standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled); + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled); + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + + public boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled); + this.serviceAccountsEnabled = serviceAccountsEnabled; + } + + public int getNodeReRegistrationTimeout() { + return nodeReRegistrationTimeout; + } + + public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { + this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout); + this.nodeReRegistrationTimeout = nodeReRegistrationTimeout; + } + + public void addWebOrigin(String webOrigin) { + updated = true; + this.webOrigins.add(webOrigin); + } + + public void removeWebOrigin(String webOrigin) { + updated |= this.webOrigins.remove(webOrigin); + } + + public void addRedirectUri(String redirectUri) { + this.updated |= ! this.redirectUris.contains(redirectUri); + this.redirectUris.add(redirectUri); + } + + public void removeRedirectUri(String redirectUri) { + updated |= this.redirectUris.remove(redirectUri); + } + + public void setAttribute(String name, String value) { + this.updated = true; + this.attributes.put(name, value); + } + + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + public String getAttribute(String name) { + return this.attributes.get(name); + } + + public String getAuthenticationFlowBindingOverride(String binding) { + return this.authFlowBindings.get(binding); + } + + public Map getAuthenticationFlowBindingOverrides() { + return this.authFlowBindings; + } + + public void removeAuthenticationFlowBindingOverride(String binding) { + updated |= this.authFlowBindings.remove(binding) != null; + } + + public void setAuthenticationFlowBindingOverride(String binding, String flowId) { + this.updated = true; + this.authFlowBindings.put(binding, flowId); + } + + public Collection getScopeMappings() { + return scopeMappings; + } + + public void addScopeMapping(String id) { + if (id != null) { + updated = true; + scopeMappings.add(id); + } + } + + public void deleteScopeMapping(String id) { + updated |= scopeMappings.remove(id); + } + + public void addClientScope(String id, boolean defaultScope) { + if (id != null) { + updated = true; + this.clientScopes.put(id, defaultScope); + } + } + + public void removeClientScope(String id) { + if (id != null) { + updated |= clientScopes.remove(id) != null; + } + } + + public Stream getClientScopes(boolean defaultScope) { + return this.clientScopes.entrySet().stream() + .filter(me -> Objects.equals(me.getValue(), defaultScope)) + .map(Entry::getKey); + } + + public String getRealmId() { + return this.realmId; } } diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java index d561b52879..436f126d67 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java @@ -30,11 +30,9 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.common.Serialization; import java.util.Comparator; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; @@ -50,18 +48,17 @@ import org.keycloak.models.ClientScopeModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapClientProvider implements ClientProvider { +public class MapClientProvider implements ClientProvider { private static final Logger LOG = Logger.getLogger(MapClientProvider.class); - private static final Predicate ALWAYS_FALSE = c -> { return false; }; private final KeycloakSession session; - final MapKeycloakTransaction tx; - private final MapStorage clientStore; - private final ConcurrentMap> clientRegisteredNodesStore; + final MapKeycloakTransaction, ClientModel> tx; + private final MapStorage, ClientModel> clientStore; + private final ConcurrentMap> clientRegisteredNodesStore; private static final Comparator COMPARE_BY_CLIENT_ID = Comparator.comparing(MapClientEntity::getClientId); - public MapClientProvider(KeycloakSession session, MapStorage clientStore, ConcurrentMap> clientRegisteredNodesStore) { + public MapClientProvider(KeycloakSession session, MapStorage, ClientModel> clientStore, ConcurrentMap> clientRegisteredNodesStore) { this.session = session; this.clientStore = clientStore; this.clientRegisteredNodesStore = clientRegisteredNodesStore; @@ -83,16 +80,21 @@ public class MapClientProvider implements ClientProvider { }; } - private MapClientEntity registerEntityForChanges(MapClientEntity origEntity) { - final MapClientEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapClientEntity::isUpdated); + private MapClientEntity registerEntityForChanges(MapClientEntity origEntity) { + final MapClientEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapClientEntity::isUpdated); return res; } - private Function entityToAdapterFunc(RealmModel realm) { + private Function, ClientModel> entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapClientAdapter(session, realm, registerEntityForChanges(origEntity)) { + return origEntity -> new MapClientAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return clientStore.getKeyConvertor().keyToString(entity.getId()); + } + @Override public void updateClient() { LOG.tracef("updateClient(%s)%s", realm, origEntity.getId(), getShortStackTrace()); @@ -119,9 +121,9 @@ public class MapClientProvider implements ClientProvider { }; } - private Predicate entityRealmFilter(RealmModel realm) { + private Predicate> entityRealmFilter(RealmModel realm) { if (realm == null || realm.getId() == null) { - return MapClientProvider.ALWAYS_FALSE; + return c -> false; } String realmId = realm.getId(); return entity -> Objects.equals(realmId, entity.getRealmId()); @@ -145,7 +147,7 @@ public class MapClientProvider implements ClientProvider { @Override public ClientModel addClient(RealmModel realm, String id, String clientId) { - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? clientStore.getKeyConvertor().yieldNewUniqueKey() : clientStore.getKeyConvertor().fromString(id); if (clientId == null) { clientId = entityId.toString(); @@ -153,7 +155,7 @@ public class MapClientProvider implements ClientProvider { LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace()); - MapClientEntity entity = new MapClientEntity(entityId, realm.getId()); + MapClientEntity entity = new MapClientEntity<>(entityId, realm.getId()); entity.setClientId(clientId); entity.setEnabled(true); entity.setStandardFlowEnabled(true); @@ -213,7 +215,7 @@ public class MapClientProvider implements ClientProvider { }); // TODO: ^^^^^^^ Up to here - tx.delete(UUID.fromString(id)); + tx.delete(clientStore.getKeyConvertor().fromString(id)); return true; } @@ -234,7 +236,7 @@ public class MapClientProvider implements ClientProvider { LOG.tracef("getClientById(%s, %s)%s", realm, id, getShortStackTrace()); - MapClientEntity entity = tx.read(UUID.fromString(id)); + MapClientEntity entity = tx.read(clientStore.getKeyConvertor().fromStringSafe(id)); return (entity == null || ! entityRealmFilter(realm).test(entity)) ? null : entityToAdapterFunc(realm).apply(entity); @@ -268,7 +270,7 @@ public class MapClientProvider implements ClientProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.CLIENT_ID, Operator.ILIKE, "%" + clientId + "%"); - Stream s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.getUpdatedNotRemoved(mcb) .sorted(COMPARE_BY_CLIENT_ID); return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm)); @@ -276,7 +278,8 @@ public class MapClientProvider implements ClientProvider { @Override public void addClientScopes(RealmModel realm, ClientModel client, Set clientScopes, boolean defaultScope) { - MapClientEntity entity = tx.read(UUID.fromString(client.getId())); + final String id = client.getId(); + MapClientEntity entity = tx.read(clientStore.getKeyConvertor().fromString(id)); if (entity == null) return; @@ -295,7 +298,8 @@ public class MapClientProvider implements ClientProvider { @Override public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) { - MapClientEntity entity = tx.read(UUID.fromString(client.getId())); + final String id = client.getId(); + MapClientEntity entity = tx.read(clientStore.getKeyConvertor().fromString(id)); if (entity == null) return; @@ -306,7 +310,8 @@ public class MapClientProvider implements ClientProvider { @Override public Map getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) { - MapClientEntity entity = tx.read(UUID.fromString(client.getId())); + final String id = client.getId(); + MapClientEntity entity = tx.read(clientStore.getKeyConvertor().fromString(id)); if (entity == null) return null; @@ -326,7 +331,7 @@ public class MapClientProvider implements ClientProvider { ModelCriteriaBuilder mcb = clientStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.SCOPE_MAPPING_ROLE, Operator.EQ, role.getId()); - try (Stream toRemove = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> toRemove = tx.getUpdatedNotRemoved(mcb)) { toRemove .map(clientEntity -> session.clients().getClientById(realm, clientEntity.getId().toString())) .filter(Objects::nonNull) diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java index bde0364fdc..60fa6a47f0 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java @@ -26,40 +26,34 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel.RoleRemovedEvent; import org.keycloak.models.RoleModel; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import org.keycloak.models.map.storage.MapStorageProvider; -import org.keycloak.models.map.storage.MapStorage; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; -import org.keycloak.models.map.storage.MapStorageProviderFactory; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * * @author hmlnarik */ -public class MapClientProviderFactory extends AbstractMapProviderFactory implements ClientProviderFactory, ProviderEventListener { +public class MapClientProviderFactory extends AbstractMapProviderFactory, ClientModel> implements ClientProviderFactory, ProviderEventListener { - private final ConcurrentHashMap> REGISTERED_NODES_STORE = new ConcurrentHashMap<>(); - - private MapStorage store; + private final ConcurrentHashMap> REGISTERED_NODES_STORE = new ConcurrentHashMap<>(); private Runnable onClose; + public MapClientProviderFactory() { + super(MapClientEntity.class, ClientModel.class); + } + @Override public void postInit(KeycloakSessionFactory factory) { - MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("clients", UUID.class, MapClientEntity.class, ClientModel.class); - factory.register(this); onClose = () -> factory.unregister(this); } - @Override public MapClientProvider create(KeycloakSession session) { - return new MapClientProvider(session, store, REGISTERED_NODES_STORE); + return new MapClientProvider<>(session, getStorage(session), REGISTERED_NODES_STORE); } @Override @@ -68,6 +62,11 @@ public class MapClientProviderFactory extends AbstractMapProviderFactory implements AbstractEntity { - - private final K id; - private final String realmId; - - private String name; - private String protocol; - private String description; - - private final Set scopeMappings = new LinkedHashSet<>(); - private final Map protocolMappers = new HashMap<>(); - private final Map attributes = new HashMap<>(); - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected AbstractClientScopeEntity() { - this.id = null; - this.realmId = null; - } - - public AbstractClientScopeEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated |= ! Objects.equals(this.description, description); - this.description = description; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.updated |= ! Objects.equals(this.protocol, protocol); - this.protocol = protocol; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.updated |= ! Objects.equals(this.attributes, attributes); - this.attributes.clear(); - this.attributes.putAll(attributes); - } - - public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { - Objects.requireNonNull(model.getId(), "protocolMapper.id"); - updated = true; - this.protocolMappers.put(model.getId(), model); - return model; - } - - public Stream getProtocolMappers() { - return protocolMappers.values().stream(); - } - - public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { - updated = true; - protocolMappers.put(id, mapping); - } - - public void removeProtocolMapper(String id) { - updated |= protocolMappers.remove(id) != null; - } - - public void setProtocolMappers(Collection protocolMappers) { - this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); - this.protocolMappers.clear(); - this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); - } - - public ProtocolMapperModel getProtocolMapperById(String id) { - return id == null ? null : protocolMappers.get(id); - } - - public void setAttribute(String name, String value) { - this.updated = true; - this.attributes.put(name, value); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - public String getAttribute(String name) { - return this.attributes.get(name); - } - - public String getRealmId() { - return this.realmId; - } - - public Stream getScopeMappings() { - return scopeMappings.stream(); - } - - public void addScopeMapping(String id) { - if (id != null) { - updated = true; - scopeMappings.add(id); - } - } - - public void deleteScopeMapping(String id) { - updated |= scopeMappings.remove(id); - } -} 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 eae5fd8cdd..69596b4c57 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 @@ -28,17 +28,12 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.RoleUtils; -public class MapClientScopeAdapter extends AbstractClientScopeModel implements ClientScopeModel { +public abstract class MapClientScopeAdapter extends AbstractClientScopeModel> implements ClientScopeModel { - public MapClientScopeAdapter(KeycloakSession session, RealmModel realm, MapClientScopeEntity entity) { + public MapClientScopeAdapter(KeycloakSession session, RealmModel realm, MapClientScopeEntity entity) { super(session, realm, entity); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getName() { return entity.getName(); diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java index 99dbb6ebf4..ab09aa451b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java @@ -1,13 +1,13 @@ /* * Copyright 2021 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,16 +16,155 @@ */ package org.keycloak.models.map.clientscope; -import java.util.UUID; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.map.common.AbstractEntity; -public class MapClientScopeEntity extends AbstractClientScopeEntity { +public class MapClientScopeEntity implements AbstractEntity { - private MapClientScopeEntity() { - super(); + private final K id; + private final String realmId; + + private String name; + private String protocol; + private String description; + + private final Set scopeMappings = new LinkedHashSet<>(); + private final Map protocolMappers = new HashMap<>(); + private final Map attributes = new HashMap<>(); + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; + + protected MapClientScopeEntity() { + this.id = null; + this.realmId = null; } - public MapClientScopeEntity(UUID id, String realmId) { - super(id, realmId); + public MapClientScopeEntity(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; } + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.updated |= ! Objects.equals(this.description, description); + this.description = description; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.updated |= ! Objects.equals(this.protocol, protocol); + this.protocol = protocol; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.updated |= ! Objects.equals(this.attributes, attributes); + this.attributes.clear(); + this.attributes.putAll(attributes); + } + + public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { + Objects.requireNonNull(model.getId(), "protocolMapper.id"); + updated = true; + this.protocolMappers.put(model.getId(), model); + return model; + } + + public Stream getProtocolMappers() { + return protocolMappers.values().stream(); + } + + public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { + updated = true; + protocolMappers.put(id, mapping); + } + + public void removeProtocolMapper(String id) { + updated |= protocolMappers.remove(id) != null; + } + + public void setProtocolMappers(Collection protocolMappers) { + this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); + this.protocolMappers.clear(); + this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); + } + + public ProtocolMapperModel getProtocolMapperById(String id) { + return id == null ? null : protocolMappers.get(id); + } + + public void setAttribute(String name, String value) { + this.updated = true; + this.attributes.put(name, value); + } + + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + public String getAttribute(String name) { + return this.attributes.get(name); + } + + public String getRealmId() { + return this.realmId; + } + + public Stream getScopeMappings() { + return scopeMappings.stream(); + } + + public void addScopeMapping(String id) { + if (id != null) { + updated = true; + scopeMappings.add(id); + } + } + + public void deleteScopeMapping(String id) { + updated |= scopeMappings.remove(id); + } } diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java index d9647dbdad..157c8d528a 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java @@ -19,7 +19,6 @@ package org.keycloak.models.map.clientscope; import java.util.Comparator; import java.util.Objects; -import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -40,38 +39,42 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapClientScopeProvider implements ClientScopeProvider { +public class MapClientScopeProvider implements ClientScopeProvider { private static final Logger LOG = Logger.getLogger(MapClientScopeProvider.class); - private static final Predicate ALWAYS_FALSE = c -> { return false; }; private final KeycloakSession session; - private final MapKeycloakTransaction tx; - private final MapStorage clientScopeStore; + private final MapKeycloakTransaction, ClientScopeModel> tx; + private final MapStorage, ClientScopeModel> clientScopeStore; private static final Comparator COMPARE_BY_NAME = Comparator.comparing(MapClientScopeEntity::getName); - public MapClientScopeProvider(KeycloakSession session, MapStorage clientScopeStore) { + public MapClientScopeProvider(KeycloakSession session, MapStorage, ClientScopeModel> clientScopeStore) { this.session = session; this.clientScopeStore = clientScopeStore; this.tx = clientScopeStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private MapClientScopeEntity registerEntityForChanges(MapClientScopeEntity origEntity) { - final MapClientScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - tx.updateIfChanged(origEntity.getId(), res, MapClientScopeEntity::isUpdated); + private MapClientScopeEntity registerEntityForChanges(MapClientScopeEntity origEntity) { + final MapClientScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + tx.updateIfChanged(origEntity.getId(), res, MapClientScopeEntity::isUpdated); return res; } - private Function entityToAdapterFunc(RealmModel realm) { + private Function, ClientScopeModel> entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapClientScopeAdapter(session, realm, registerEntityForChanges(origEntity)); + return origEntity -> new MapClientScopeAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return clientScopeStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } - private Predicate entityRealmFilter(RealmModel realm) { + private Predicate> entityRealmFilter(RealmModel realm) { if (realm == null || realm.getId() == null) { - return MapClientScopeProvider.ALWAYS_FALSE; + return c -> false; } String realmId = realm.getId(); return entity -> Objects.equals(realmId, entity.getRealmId()); @@ -98,11 +101,11 @@ public class MapClientScopeProvider implements ClientScopeProvider { throw new ModelDuplicateException("Client scope with name '" + name + "' in realm " + realm.getName()); } - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? clientScopeStore.getKeyConvertor().yieldNewUniqueKey() : clientScopeStore.getKeyConvertor().fromString(id); LOG.tracef("addClientScope(%s, %s, %s)%s", realm, id, name, getShortStackTrace()); - MapClientScopeEntity entity = new MapClientScopeEntity(entityId, realm.getId()); + MapClientScopeEntity entity = new MapClientScopeEntity<>(entityId, realm.getId()); entity.setName(KeycloakModelUtils.convertClientScopeName(name)); if (tx.read(entity.getId()) != null) { throw new ModelDuplicateException("Client scope exists: " + id); @@ -137,7 +140,7 @@ public class MapClientScopeProvider implements ClientScopeProvider { } }); - tx.delete(UUID.fromString(id)); + tx.delete(clientScopeStore.getKeyConvertor().fromString(id)); return true; } @@ -159,14 +162,14 @@ public class MapClientScopeProvider implements ClientScopeProvider { LOG.tracef("getClientScopeById(%s, %s)%s", realm, id, getShortStackTrace()); - UUID uuid; + K uuid; try { - uuid = UUID.fromString(id); + uuid = clientScopeStore.getKeyConvertor().fromStringSafe(id); } catch (IllegalArgumentException ex) { return null; } - MapClientScopeEntity entity = tx.read(uuid); + MapClientScopeEntity entity = tx.read(uuid); return (entity == null || ! entityRealmFilter(realm).test(entity)) ? null : entityToAdapterFunc(realm).apply(entity); diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java index a5858cc8a1..6344db4d36 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java @@ -16,28 +16,25 @@ */ package org.keycloak.models.map.clientscope; -import java.util.UUID; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeProvider; import org.keycloak.models.ClientScopeProviderFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProvider; -public class MapClientScopeProviderFactory extends AbstractMapProviderFactory implements ClientScopeProviderFactory { +public class MapClientScopeProviderFactory extends AbstractMapProviderFactory, ClientScopeModel> implements ClientScopeProviderFactory { - private MapStorage store; - - @Override - public void postInit(KeycloakSessionFactory factory) { - MapStorageProvider sp = (MapStorageProvider) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("clientscope", UUID.class, MapClientScopeEntity.class, ClientScopeModel.class); + public MapClientScopeProviderFactory() { + super(MapClientScopeEntity.class, ClientScopeModel.class); } @Override public ClientScopeProvider create(KeycloakSession session) { - return new MapClientScopeProvider(session, store); + return new MapClientScopeProvider<>(session, getStorage(session)); + } + + @Override + public String getHelpText() { + return "Client scope provider"; } } diff --git a/model/map/src/main/java/org/keycloak/models/map/common/AbstractMapProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/common/AbstractMapProviderFactory.java index b28d2ee142..565dd4d852 100644 --- a/model/map/src/main/java/org/keycloak/models/map/common/AbstractMapProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/common/AbstractMapProviderFactory.java @@ -17,23 +17,52 @@ package org.keycloak.models.map.common; import org.keycloak.Config.Scope; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.map.storage.MapStorage; +import org.keycloak.models.map.storage.MapStorageProvider; +import org.keycloak.models.map.storage.MapStorageProviderFactory; +import org.keycloak.models.map.storage.MapStorageSpi; +import org.keycloak.component.AmphibianProviderFactory; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; import org.jboss.logging.Logger; +import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory; /** * * @author hmlnarik */ -public abstract class AbstractMapProviderFactory implements ProviderFactory { +public abstract class AbstractMapProviderFactory, M> implements AmphibianProviderFactory { public static final String PROVIDER_ID = "map"; + public static final String CONFIG_STORAGE = "storage"; + protected final Logger LOG = Logger.getLogger(getClass()); + protected final Class modelType; + + protected final Class entityType; + + private Scope storageConfigScope; + + @SuppressWarnings("unchecked") + protected AbstractMapProviderFactory(Class entityType, Class modelType) { + this.modelType = modelType; + this.entityType = (Class) entityType; + } + @Override - public void init(Scope config) { + public String getId() { + return PROVIDER_ID; + } + + protected MapStorage getStorage(KeycloakSession session) { + MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(), + MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME); + final MapStorageProvider factory = storageProviderFactory.create(session); + + return factory.getStorage(entityType, modelType); } @Override @@ -41,11 +70,8 @@ public abstract class AbstractMapProviderFactory implements } @Override - public void close() { - } - - @Override - public String getId() { - return PROVIDER_ID; + public void init(Scope config) { + // Implementation of the map storage SPI + this.storageConfigScope = config.scope(CONFIG_STORAGE); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java b/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java index 73460ccd4a..b2848c9486 100644 --- a/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java +++ b/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java @@ -19,6 +19,9 @@ package org.keycloak.models.map.common; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; @@ -49,7 +52,12 @@ public class Serialization { public static final ConcurrentHashMap, ObjectReader> READERS = new ConcurrentHashMap<>(); public static final ConcurrentHashMap, ObjectWriter> WRITERS = new ConcurrentHashMap<>(); - abstract class IgnoreUpdatedMixIn { @JsonIgnore public abstract boolean isUpdated(); } + abstract class IgnoreUpdatedMixIn { + @JsonIgnore public abstract boolean isUpdated(); + + @JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY) + abstract Object getId(); + } static { JavaType type = TypeFactory.unknownType(); diff --git a/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupEntity.java b/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupEntity.java deleted file mode 100644 index 0b68af0320..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupEntity.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2020 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.map.group; - -import org.keycloak.models.map.common.AbstractEntity; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * - * @author mhajas - */ -public abstract class AbstractGroupEntity implements AbstractEntity { - - private final K id; - private final String realmId; - - private String name; - private String parentId; - private Map> attributes = new HashMap<>(); - private Set grantedRoles = new HashSet<>(); - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected AbstractGroupEntity() { - this.id = null; - this.realmId = null; - } - - public AbstractGroupEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.updated |= !Objects.equals(this.parentId, parentId); - this.parentId = parentId; - } - - public Map> getAttributes() { - return attributes; - } - - public void setAttributes(Map> attributes) { - this.updated |= ! Objects.equals(this.attributes, attributes); - this.attributes = attributes; - } - - public void setAttribute(String name, List value) { - this.updated |= !this.attributes.containsKey(name) || !this.attributes.get(name).equals(value); - this.attributes.put(name, value); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - public List getAttribute(String name) { - return this.attributes.get(name); - } - - public String getRealmId() { - return this.realmId; - } - - public Set getGrantedRoles() { - return grantedRoles; - } - - public void setGrantedRoles(Set grantedRoles) { - this.updated |= !Objects.equals(this.grantedRoles, grantedRoles); - this.grantedRoles = grantedRoles; - } - - public void removeRole(String role) { - this.updated |= this.grantedRoles.contains(role); - this.grantedRoles.remove(role); - } - - public void addGrantedRole(String role) { - this.updated |= !this.grantedRoles.contains(role); - this.grantedRoles.add(role); - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java index df776bd0c4..200fd338cd 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java @@ -29,16 +29,11 @@ import java.util.Map; import java.util.stream.Stream; -public class MapGroupAdapter extends AbstractGroupModel { - public MapGroupAdapter(KeycloakSession session, RealmModel realm, MapGroupEntity entity) { +public abstract class MapGroupAdapter extends AbstractGroupModel> { + public MapGroupAdapter(KeycloakSession session, RealmModel realm, MapGroupEntity entity) { super(session, realm, entity); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public String getName() { return entity.getName(); diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java index 73c789ed93..e164dfb4e2 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java @@ -1,13 +1,13 @@ /* * Copyright 2020 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,15 +17,118 @@ package org.keycloak.models.map.group; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractEntity; -public class MapGroupEntity extends AbstractGroupEntity { +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * + * @author mhajas + */ +public class MapGroupEntity implements AbstractEntity { + + private final K id; + private final String realmId; + + private String name; + private String parentId; + private Map> attributes = new HashMap<>(); + private Set grantedRoles = new HashSet<>(); + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; protected MapGroupEntity() { - super(); + this.id = null; + this.realmId = null; } - public MapGroupEntity(UUID id, String realmId) { - super(id, realmId); + public MapGroupEntity(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; + } + + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.updated |= !Objects.equals(this.parentId, parentId); + this.parentId = parentId; + } + + public Map> getAttributes() { + return attributes; + } + + public void setAttributes(Map> attributes) { + this.updated |= ! Objects.equals(this.attributes, attributes); + this.attributes = attributes; + } + + public void setAttribute(String name, List value) { + this.updated |= !this.attributes.containsKey(name) || !this.attributes.get(name).equals(value); + this.attributes.put(name, value); + } + + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + public List getAttribute(String name) { + return this.attributes.get(name); + } + + public String getRealmId() { + return this.realmId; + } + + public Set getGrantedRoles() { + return grantedRoles; + } + + public void setGrantedRoles(Set grantedRoles) { + this.updated |= !Objects.equals(this.grantedRoles, grantedRoles); + this.grantedRoles = grantedRoles; + } + + public void removeRole(String role) { + this.updated |= this.grantedRoles.contains(role); + this.grantedRoles.remove(role); + } + + public void addGrantedRole(String role) { + this.updated |= !this.grantedRoles.contains(role); + this.grantedRoles.add(role); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java index 3269855b8a..52a3012189 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java @@ -33,7 +33,6 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import java.util.Comparator; import java.util.Objects; -import java.util.UUID; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Stream; @@ -41,29 +40,34 @@ import java.util.stream.Stream; import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.utils.StreamsUtil.paginatedStream; -public class MapGroupProvider implements GroupProvider { +public class MapGroupProvider implements GroupProvider { private static final Logger LOG = Logger.getLogger(MapGroupProvider.class); private final KeycloakSession session; - final MapKeycloakTransaction tx; - private final MapStorage groupStore; + final MapKeycloakTransaction, GroupModel> tx; + private final MapStorage, GroupModel> groupStore; - public MapGroupProvider(KeycloakSession session, MapStorage groupStore) { + public MapGroupProvider(KeycloakSession session, MapStorage, GroupModel> groupStore) { this.session = session; this.groupStore = groupStore; this.tx = groupStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private MapGroupEntity registerEntityForChanges(MapGroupEntity origEntity) { - final MapGroupEntity res = Serialization.from(origEntity); - tx.updateIfChanged(origEntity.getId(), res, MapGroupEntity::isUpdated); + private MapGroupEntity registerEntityForChanges(MapGroupEntity origEntity) { + final MapGroupEntity res = Serialization.from(origEntity); + tx.updateIfChanged(origEntity.getId(), res, MapGroupEntity::isUpdated); return res; } - private Function entityToAdapterFunc(RealmModel realm) { + private Function, GroupModel> entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapGroupAdapter(session, realm, registerEntityForChanges(origEntity)); + return origEntity -> new MapGroupAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return groupStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } @Override @@ -74,15 +78,14 @@ public class MapGroupProvider implements GroupProvider { LOG.tracef("getGroupById(%s, %s)%s", realm, id, getShortStackTrace()); - - UUID uid; + K uid; try { - uid = UUID.fromString(id); + uid = groupStore.getKeyConvertor().fromStringSafe(id); } catch (IllegalArgumentException ex) { return null; } - MapGroupEntity entity = tx.read(uid); + MapGroupEntity entity = tx.read(uid); String realmId = realm.getId(); return (entity == null || ! Objects.equals(realmId, entity.getRealmId())) ? null @@ -112,7 +115,7 @@ public class MapGroupProvider implements GroupProvider { @Override public Stream getGroupsStream(RealmModel realm, Stream ids, String search, Integer first, Integer max) { ModelCriteriaBuilder mcb = groupStore.createCriteriaBuilder() - .compare(SearchableFields.ID, Operator.IN, ids.map(UUID::fromString)) + .compare(SearchableFields.ID, Operator.IN, ids.map(groupStore.getKeyConvertor()::fromString)) .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); if (search != null) { @@ -188,7 +191,7 @@ public class MapGroupProvider implements GroupProvider { @Override public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) { LOG.tracef("createGroup(%s, %s, %s, %s)%s", realm, id, name, toParent, getShortStackTrace()); - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? groupStore.getKeyConvertor().yieldNewUniqueKey() : groupStore.getKeyConvertor().fromString(id); // Check Db constraint: uniqueConstraints = { @UniqueConstraint(columnNames = {"REALM_ID", "PARENT_GROUP", "NAME"})} String parentId = toParent == null ? null : toParent.getId(); @@ -201,7 +204,7 @@ public class MapGroupProvider implements GroupProvider { throw new ModelDuplicateException("Group with name '" + name + "' in realm " + realm.getName() + " already exists for requested parent" ); } - MapGroupEntity entity = new MapGroupEntity(entityId, realm.getId()); + MapGroupEntity entity = new MapGroupEntity(entityId, realm.getId()); entity.setName(name); entity.setParentId(toParent == null ? null : toParent.getId()); if (tx.read(entity.getId()) != null) { @@ -243,7 +246,7 @@ public class MapGroupProvider implements GroupProvider { // TODO: ^^^^^^^ Up to here - tx.delete(UUID.fromString(group.getId())); + tx.delete(groupStore.getKeyConvertor().fromString(group.getId())); return true; } @@ -264,7 +267,7 @@ public class MapGroupProvider implements GroupProvider { .compare(SearchableFields.PARENT_ID, Operator.EQ, parentId) .compare(SearchableFields.NAME, Operator.EQ, group.getName()); - try (Stream possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { if (possibleSiblings.findAny().isPresent()) { throw new ModelDuplicateException("Parent already contains subgroup named '" + group.getName() + "'"); } @@ -286,7 +289,7 @@ public class MapGroupProvider implements GroupProvider { .compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null) .compare(SearchableFields.NAME, Operator.EQ, subGroup.getName()); - try (Stream possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { if (possibleSiblings.findAny().isPresent()) { throw new ModelDuplicateException("There is already a top level group named '" + subGroup.getName() + "'"); } @@ -300,7 +303,7 @@ public class MapGroupProvider implements GroupProvider { ModelCriteriaBuilder mcb = groupStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId()); - try (Stream toRemove = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> toRemove = tx.getUpdatedNotRemoved(mcb)) { toRemove .map(groupEntity -> session.groups().getGroupById(realm, groupEntity.getId().toString())) .forEach(groupModel -> groupModel.deleteRoleMapping(role)); diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java index ef5283f4ee..fa6b873934 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.keycloak.models.map.group; import org.keycloak.models.ClientModel; @@ -27,38 +26,32 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel.RoleRemovedEvent; import org.keycloak.models.RoleModel; -import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProvider; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; -import org.keycloak.models.map.storage.MapStorageProviderFactory; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractMapProviderFactory; /** * * @author mhajas */ -public class MapGroupProviderFactory extends AbstractMapProviderFactory implements GroupProviderFactory, ProviderEventListener { - - private MapStorage store; +public class MapGroupProviderFactory extends AbstractMapProviderFactory, GroupModel> implements GroupProviderFactory, ProviderEventListener { private Runnable onClose; + public MapGroupProviderFactory() { + super(MapGroupEntity.class, GroupModel.class); + } + @Override public void postInit(KeycloakSessionFactory factory) { - MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("groups", UUID.class, MapGroupEntity.class, GroupModel.class); - factory.register(this); onClose = () -> factory.unregister(this); } - @Override public MapGroupProvider create(KeycloakSession session) { - return new MapGroupProvider(session, store); + return new MapGroupProvider<>(session, getStorage(session)); } @Override @@ -67,6 +60,11 @@ public class MapGroupProviderFactory extends AbstractMapProviderFactoryMartin Kanis - */ -public abstract class AbstractUserLoginFailureEntity implements AbstractEntity { - private K id; - private String realmId; - private String userId; - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - private int failedLoginNotBefore; - private int numFailures; - private long lastFailure; - private String lastIPFailure; - - public AbstractUserLoginFailureEntity() { - this.id = null; - this.realmId = null; - this.userId = null; - } - - public AbstractUserLoginFailureEntity(K id, String realmId, String userId) { - this.id = id; - this.realmId = realmId; - this.userId = userId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getRealmId() { - return realmId; - } - - public void setRealmId(String realmId) { - this.updated |= !Objects.equals(this.realmId, realmId); - this.realmId = realmId; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.updated |= !Objects.equals(this.userId, userId); - this.userId = userId; - } - - public int getFailedLoginNotBefore() { - return failedLoginNotBefore; - } - - public void setFailedLoginNotBefore(int failedLoginNotBefore) { - this.updated |= this.failedLoginNotBefore != failedLoginNotBefore; - this.failedLoginNotBefore = failedLoginNotBefore; - } - - public int getNumFailures() { - return numFailures; - } - - public void setNumFailures(int numFailures) { - this.updated |= this.numFailures != numFailures; - this.numFailures = numFailures; - } - - public long getLastFailure() { - return lastFailure; - } - - public void setLastFailure(long lastFailure) { - this.updated |= this.lastFailure != lastFailure; - this.lastFailure = lastFailure; - } - - public String getLastIPFailure() { - return lastIPFailure; - } - - public void setLastIPFailure(String lastIPFailure) { - this.updated |= !Objects.equals(this.lastIPFailure, lastIPFailure); - this.lastIPFailure = lastIPFailure; - } - - public void clearFailures() { - this.updated |= this.failedLoginNotBefore != 0 || this.numFailures != 0 || - this.lastFailure != 0l || this.lastIPFailure != null; - this.failedLoginNotBefore = this.numFailures = 0; - this.lastFailure = 0l; - this.lastIPFailure = null; - } - - @Override - public String toString() { - return String.format("%s@%08x", getId(), hashCode()); - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureAdapter.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureAdapter.java index e87a71cfe4..53cdcb2231 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureAdapter.java @@ -22,8 +22,8 @@ import org.keycloak.models.RealmModel; /** * @author Martin Kanis */ -public class MapUserLoginFailureAdapter extends AbstractUserLoginFailureModel { - public MapUserLoginFailureAdapter(KeycloakSession session, RealmModel realm, MapUserLoginFailureEntity entity) { +public abstract class MapUserLoginFailureAdapter extends AbstractUserLoginFailureModel> { + public MapUserLoginFailureAdapter(KeycloakSession session, RealmModel realm, MapUserLoginFailureEntity entity) { super(session, realm, entity); } diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java index 12656ed6c5..50ba18de04 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java @@ -16,17 +16,114 @@ */ package org.keycloak.models.map.loginFailure; -import java.util.UUID; +import org.keycloak.models.map.common.AbstractEntity; + +import java.util.Objects; /** * @author Martin Kanis */ -public class MapUserLoginFailureEntity extends AbstractUserLoginFailureEntity { - protected MapUserLoginFailureEntity() { - super(); +public class MapUserLoginFailureEntity implements AbstractEntity { + private K id; + private String realmId; + private String userId; + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; + + private int failedLoginNotBefore; + private int numFailures; + private long lastFailure; + private String lastIPFailure; + + public MapUserLoginFailureEntity() { + this.id = null; + this.realmId = null; + this.userId = null; } - public MapUserLoginFailureEntity(UUID id, String realmId, String userId) { - super(id, realmId, userId); + public MapUserLoginFailureEntity(K id, String realmId, String userId) { + this.id = id; + this.realmId = realmId; + this.userId = userId; + } + + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + public String getRealmId() { + return realmId; + } + + public void setRealmId(String realmId) { + this.updated |= !Objects.equals(this.realmId, realmId); + this.realmId = realmId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.updated |= !Objects.equals(this.userId, userId); + this.userId = userId; + } + + public int getFailedLoginNotBefore() { + return failedLoginNotBefore; + } + + public void setFailedLoginNotBefore(int failedLoginNotBefore) { + this.updated |= this.failedLoginNotBefore != failedLoginNotBefore; + this.failedLoginNotBefore = failedLoginNotBefore; + } + + public int getNumFailures() { + return numFailures; + } + + public void setNumFailures(int numFailures) { + this.updated |= this.numFailures != numFailures; + this.numFailures = numFailures; + } + + public long getLastFailure() { + return lastFailure; + } + + public void setLastFailure(long lastFailure) { + this.updated |= this.lastFailure != lastFailure; + this.lastFailure = lastFailure; + } + + public String getLastIPFailure() { + return lastIPFailure; + } + + public void setLastIPFailure(String lastIPFailure) { + this.updated |= !Objects.equals(this.lastIPFailure, lastIPFailure); + this.lastIPFailure = lastIPFailure; + } + + public void clearFailures() { + this.updated |= this.failedLoginNotBefore != 0 || this.numFailures != 0 || + this.lastFailure != 0l || this.lastIPFailure != null; + this.failedLoginNotBefore = this.numFailures = 0; + this.lastFailure = 0l; + this.lastIPFailure = null; + } + + @Override + public String toString() { + return String.format("%s@%08x", getId(), hashCode()); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java index bcf8ed592a..d4c6656516 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java @@ -26,7 +26,6 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.ModelCriteriaBuilder; -import java.util.UUID; import java.util.function.Function; import static org.keycloak.common.util.StackUtil.getShortStackTrace; @@ -34,14 +33,14 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace; /** * @author Martin Kanis */ -public class MapUserLoginFailureProvider implements UserLoginFailureProvider { +public class MapUserLoginFailureProvider implements UserLoginFailureProvider { private static final Logger LOG = Logger.getLogger(MapUserLoginFailureProvider.class); private final KeycloakSession session; - protected final MapKeycloakTransaction userLoginFailureTx; - private final MapStorage userLoginFailureStore; + protected final MapKeycloakTransaction, UserLoginFailureModel> userLoginFailureTx; + private final MapStorage, UserLoginFailureModel> userLoginFailureStore; - public MapUserLoginFailureProvider(KeycloakSession session, MapStorage userLoginFailureStore) { + public MapUserLoginFailureProvider(KeycloakSession session, MapStorage, UserLoginFailureModel> userLoginFailureStore) { this.session = session; this.userLoginFailureStore = userLoginFailureStore; @@ -49,14 +48,19 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider { session.getTransactionManager().enlistAfterCompletion(userLoginFailureTx); } - private Function userLoginFailureEntityToAdapterFunc(RealmModel realm) { + private Function, UserLoginFailureModel> userLoginFailureEntityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapUserLoginFailureAdapter(session, realm, registerEntityForChanges(origEntity)); + return origEntity -> new MapUserLoginFailureAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return userLoginFailureStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } - private MapUserLoginFailureEntity registerEntityForChanges(MapUserLoginFailureEntity origEntity) { - MapUserLoginFailureEntity res = userLoginFailureTx.read(origEntity.getId(), id -> Serialization.from(origEntity)); - userLoginFailureTx.updateIfChanged(origEntity.getId(), res, MapUserLoginFailureEntity::isUpdated); + private MapUserLoginFailureEntity registerEntityForChanges(MapUserLoginFailureEntity origEntity) { + MapUserLoginFailureEntity res = userLoginFailureTx.read(origEntity.getId(), id -> Serialization.from(origEntity)); + userLoginFailureTx.updateIfChanged(origEntity.getId(), res, MapUserLoginFailureEntity::isUpdated); return res; } @@ -82,10 +86,10 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider { LOG.tracef("addUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace()); - MapUserLoginFailureEntity userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null); + MapUserLoginFailureEntity userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null); if (userLoginFailureEntity == null) { - userLoginFailureEntity = new MapUserLoginFailureEntity(UUID.randomUUID(), realm.getId(), userId); + userLoginFailureEntity = new MapUserLoginFailureEntity<>(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), realm.getId(), userId); userLoginFailureTx.create(userLoginFailureEntity.getId(), userLoginFailureEntity); } @@ -101,7 +105,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider { LOG.tracef("removeUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace()); - userLoginFailureTx.delete(UUID.randomUUID(), mcb); + userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb); } @Override @@ -111,7 +115,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider { LOG.tracef("removeAllUserLoginFailures(%s)%s", realm, getShortStackTrace()); - userLoginFailureTx.delete(UUID.randomUUID(), mcb); + userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java index 6e216c3ee5..e118d4b95d 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java @@ -23,46 +23,58 @@ import org.keycloak.models.UserLoginFailureProvider; import org.keycloak.models.UserLoginFailureProviderFactory; import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; +import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProvider; - -import java.util.UUID; +import org.keycloak.provider.ProviderEvent; +import org.keycloak.provider.ProviderEventListener; /** * @author Martin Kanis */ -public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFactory - implements UserLoginFailureProviderFactory { +public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFactory, UserLoginFailureModel> + implements UserLoginFailureProviderFactory, ProviderEventListener { - private MapStorage userLoginFailureStore; + private Runnable onClose; + + public MapUserLoginFailureProviderFactory() { + super(MapUserLoginFailureEntity.class, UserLoginFailureModel.class); + } @Override public void postInit(KeycloakSessionFactory factory) { - MapStorageProvider sp = (MapStorageProvider) factory.getProviderFactory(MapStorageProvider.class); - userLoginFailureStore = sp.getStorage("userLoginFailures", UUID.class, MapUserLoginFailureEntity.class, UserLoginFailureModel.class); - - factory.register(event -> { - if (event instanceof UserModel.UserRemovedEvent) { - UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event; - - MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(userRemovedEvent.getKeycloakSession()); - provider.removeUserLoginFailure(userRemovedEvent.getRealm(), userRemovedEvent.getUser().getId()); - } - }); - - factory.register(event -> { - if (event instanceof RealmModel.RealmRemovedEvent) { - RealmModel.RealmRemovedEvent realmRemovedEvent = (RealmModel.RealmRemovedEvent) event; - - MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(realmRemovedEvent.getKeycloakSession()); - provider.removeAllUserLoginFailures(realmRemovedEvent.getRealm()); - } - }); + factory.register(this); + onClose = () -> factory.unregister(this); } @Override - public MapUserLoginFailureProvider create(KeycloakSession session) { - return new MapUserLoginFailureProvider(session, userLoginFailureStore); + public void close() { + super.close(); + onClose.run(); } + + @Override + public MapUserLoginFailureProvider create(KeycloakSession session) { + return new MapUserLoginFailureProvider<>(session, getStorage(session)); + } + + @Override + public String getHelpText() { + return "User login failure provider"; + } + + @Override + public void onEvent(ProviderEvent event) { + if (event instanceof UserModel.UserRemovedEvent) { + UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event; + + MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(userRemovedEvent.getKeycloakSession()); + provider.removeUserLoginFailure(userRemovedEvent.getRealm(), userRemovedEvent.getUser().getId()); + } else if (event instanceof RealmModel.RealmRemovedEvent) { + RealmModel.RealmRemovedEvent realmRemovedEvent = (RealmModel.RealmRemovedEvent) event; + + MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(realmRemovedEvent.getKeycloakSession()); + provider.removeAllUserLoginFailures(realmRemovedEvent.getRealm()); + } + } + } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/AbstractRealmEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/AbstractRealmEntity.java deleted file mode 100644 index 26614eaff9..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/realm/AbstractRealmEntity.java +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.models.map.realm; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.keycloak.common.util.Time; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.OTPPolicy; -import org.keycloak.models.map.common.AbstractEntity; -import org.keycloak.models.map.realm.entity.MapAuthenticationExecutionEntity; -import org.keycloak.models.map.realm.entity.MapAuthenticationFlowEntity; -import org.keycloak.models.map.realm.entity.MapAuthenticatorConfigEntity; -import org.keycloak.models.map.realm.entity.MapClientInitialAccessEntity; -import org.keycloak.models.map.realm.entity.MapComponentEntity; -import org.keycloak.models.map.realm.entity.MapIdentityProviderEntity; -import org.keycloak.models.map.realm.entity.MapIdentityProviderMapperEntity; -import org.keycloak.models.map.realm.entity.MapOTPPolicyEntity; -import org.keycloak.models.map.realm.entity.MapRequiredActionProviderEntity; -import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity; -import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity; - -public abstract class AbstractRealmEntity implements AbstractEntity { - - private final K id; - private String name; - - private Boolean enabled = false; - private Boolean registrationAllowed = false; - private Boolean registrationEmailAsUsername = false; - private Boolean verifyEmail = false; - private Boolean resetPasswordAllowed = false; - private Boolean loginWithEmailAllowed = false; - private Boolean duplicateEmailsAllowed = false; - private Boolean rememberMe = false; - private Boolean editUsernameAllowed = false; - private Boolean revokeRefreshToken = false; - private Boolean adminEventsEnabled = false; - private Boolean adminEventsDetailsEnabled = false; - private Boolean internationalizationEnabled = false; - private Boolean allowUserManagedAccess = false; - private Boolean offlineSessionMaxLifespanEnabled = false; - private Boolean eventsEnabled = false; - private Integer refreshTokenMaxReuse = 0; - private Integer ssoSessionIdleTimeout = 0; - private Integer ssoSessionMaxLifespan = 0; - private Integer ssoSessionIdleTimeoutRememberMe = 0; - private Integer ssoSessionMaxLifespanRememberMe = 0; - private Integer offlineSessionIdleTimeout = 0; - private Integer accessTokenLifespan = 0; - private Integer accessTokenLifespanForImplicitFlow = 0; - private Integer accessCodeLifespan = 0; - private Integer accessCodeLifespanUserAction = 0; - private Integer accessCodeLifespanLogin = 0; - private Integer notBefore = 0; - private Integer clientSessionIdleTimeout = 0; - private Integer clientSessionMaxLifespan = 0; - private Integer clientOfflineSessionIdleTimeout = 0; - private Integer clientOfflineSessionMaxLifespan = 0; - private Integer actionTokenGeneratedByAdminLifespan = 0; - private Integer offlineSessionMaxLifespan = 0; - private Long eventsExpiration = 0l; - private String displayName; - private String displayNameHtml; - private String passwordPolicy; - private String sslRequired; - private String loginTheme; - private String accountTheme; - private String adminTheme; - private String emailTheme; - private String masterAdminClient; - private String defaultRoleId; - private String defaultLocale; - private String browserFlow; - private String registrationFlow; - private String directGrantFlow; - private String resetCredentialsFlow; - private String clientAuthenticationFlow; - private String dockerAuthenticationFlow; - private MapOTPPolicyEntity otpPolicy = MapOTPPolicyEntity.fromModel(OTPPolicy.DEFAULT_POLICY); - private MapWebAuthnPolicyEntity webAuthnPolicy = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy(); - private MapWebAuthnPolicyEntity webAuthnPolicyPasswordless = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy(); - - - private Set eventsListeners = new HashSet<>(); - private Set enabledEventTypes = new HashSet<>(); - private Set supportedLocales = new HashSet<>(); - private Map browserSecurityHeaders = new HashMap<>(); - private Map smtpConfig = new HashMap<>(); - - private final Set defaultGroupIds = new HashSet<>(); - private final Set defaultClientScopes = new HashSet<>(); - private final Set optionalClientScopes = new HashSet<>(); - private final Map attributes = new HashMap<>(); - private final Map> localizationTexts = new HashMap<>(); - private final Map clientInitialAccesses = new HashMap<>(); - private final Map components = new HashMap<>(); - private final Map authenticationFlows = new HashMap<>(); - private final Map authenticationExecutions = new HashMap<>(); - private final Map requiredCredentials = new HashMap<>(); - private final Map authenticatorConfigs = new HashMap<>(); - private final Map identityProviders = new HashMap<>(); - private final Map identityProviderMappers = new HashMap<>(); - private final Map requiredActionProviders = new HashMap<>(); - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected AbstractRealmEntity() { - this.id = null; - } - - public AbstractRealmEntity(K id) { - Objects.requireNonNull(id, "id"); - - this.id = id; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated - || authenticationExecutions.values().stream().anyMatch(MapAuthenticationExecutionEntity::isUpdated) - || authenticationFlows.values().stream().anyMatch(MapAuthenticationFlowEntity::isUpdated) - || authenticatorConfigs.values().stream().anyMatch(MapAuthenticatorConfigEntity::isUpdated) - || clientInitialAccesses.values().stream().anyMatch(MapClientInitialAccessEntity::isUpdated) - || components.values().stream().anyMatch(MapComponentEntity::isUpdated) - || identityProviders.values().stream().anyMatch(MapIdentityProviderEntity::isUpdated) - || identityProviderMappers.values().stream().anyMatch(MapIdentityProviderMapperEntity::isUpdated) - || requiredActionProviders.values().stream().anyMatch(MapRequiredActionProviderEntity::isUpdated) - || requiredCredentials.values().stream().anyMatch(MapRequiredCredentialEntity::isUpdated) - || otpPolicy.isUpdated() - || webAuthnPolicy.isUpdated() - || webAuthnPolicyPasswordless.isUpdated(); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.updated |= ! Objects.equals(this.displayName, displayName); - this.displayName = displayName; - } - - public String getDisplayNameHtml() { - return displayNameHtml; - } - - public void setDisplayNameHtml(String displayNameHtml) { - this.updated |= ! Objects.equals(this.displayNameHtml, displayNameHtml); - this.displayNameHtml = displayNameHtml; - } - - public Boolean isEnabled() { - return enabled; - } - - public void setEnabled(Boolean enabled) { - this.updated |= ! Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public Boolean isRegistrationAllowed() { - return registrationAllowed; - } - - public void setRegistrationAllowed(Boolean registrationAllowed) { - this.updated |= ! Objects.equals(this.registrationAllowed, registrationAllowed); - this.registrationAllowed = registrationAllowed; - } - - public Boolean isRegistrationEmailAsUsername() { - return registrationEmailAsUsername; - } - - public void setRegistrationEmailAsUsername(Boolean registrationEmailAsUsername) { - this.updated |= ! Objects.equals(this.registrationEmailAsUsername, registrationEmailAsUsername); - this.registrationEmailAsUsername = registrationEmailAsUsername; - } - - public Boolean isVerifyEmail() { - return verifyEmail; - } - - public void setVerifyEmail(Boolean verifyEmail) { - this.updated |= ! Objects.equals(this.verifyEmail, verifyEmail); - this.verifyEmail = verifyEmail; - } - - - public Boolean isResetPasswordAllowed() { - return resetPasswordAllowed; - } - - public void setResetPasswordAllowed(Boolean resetPasswordAllowed) { - this.updated |= ! Objects.equals(this.resetPasswordAllowed, resetPasswordAllowed); - this.resetPasswordAllowed = resetPasswordAllowed; - } - - public Boolean isLoginWithEmailAllowed() { - return loginWithEmailAllowed; - } - - public void setLoginWithEmailAllowed(Boolean loginWithEmailAllowed) { - this.updated |= ! Objects.equals(this.loginWithEmailAllowed, loginWithEmailAllowed); - this.loginWithEmailAllowed = loginWithEmailAllowed; - } - - public Boolean isDuplicateEmailsAllowed() { - return duplicateEmailsAllowed; - } - - public void setDuplicateEmailsAllowed(Boolean duplicateEmailsAllowed) { - this.updated |= ! Objects.equals(this.duplicateEmailsAllowed, duplicateEmailsAllowed); - this.duplicateEmailsAllowed = duplicateEmailsAllowed; - } - - public Boolean isRememberMe() { - return rememberMe; - } - - public void setRememberMe(Boolean rememberMe) { - this.updated |= ! Objects.equals(this.rememberMe, rememberMe); - this.rememberMe = rememberMe; - } - - public Boolean isEditUsernameAllowed() { - return editUsernameAllowed; - } - - public void setEditUsernameAllowed(Boolean editUsernameAllowed) { - this.updated |= ! Objects.equals(this.editUsernameAllowed, editUsernameAllowed); - this.editUsernameAllowed = editUsernameAllowed; - } - - public Boolean isRevokeRefreshToken() { - return revokeRefreshToken; - } - - public void setRevokeRefreshToken(Boolean revokeRefreshToken) { - this.updated |= ! Objects.equals(this.revokeRefreshToken, revokeRefreshToken); - this.revokeRefreshToken = revokeRefreshToken; - } - - public Boolean isAdminEventsEnabled() { - return adminEventsEnabled; - } - - public void setAdminEventsEnabled(Boolean adminEventsEnabled) { - this.updated |= ! Objects.equals(this.adminEventsEnabled, adminEventsEnabled); - this.adminEventsEnabled = adminEventsEnabled; - } - - public Boolean isAdminEventsDetailsEnabled() { - return adminEventsDetailsEnabled; - } - - public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) { - this.updated |= ! Objects.equals(this.adminEventsDetailsEnabled, adminEventsDetailsEnabled); - this.adminEventsDetailsEnabled = adminEventsDetailsEnabled; - } - - public Boolean isInternationalizationEnabled() { - return internationalizationEnabled; - } - - public void setInternationalizationEnabled(Boolean internationalizationEnabled) { - this.updated |= ! Objects.equals(this.internationalizationEnabled, internationalizationEnabled); - this.internationalizationEnabled = internationalizationEnabled; - } - - public Boolean isAllowUserManagedAccess() { - return allowUserManagedAccess; - } - - public void setAllowUserManagedAccess(Boolean allowUserManagedAccess) { - this.updated |= ! Objects.equals(this.allowUserManagedAccess, allowUserManagedAccess); - this.allowUserManagedAccess = allowUserManagedAccess; - } - - public Boolean isOfflineSessionMaxLifespanEnabled() { - return offlineSessionMaxLifespanEnabled; - } - - public void setOfflineSessionMaxLifespanEnabled(Boolean offlineSessionMaxLifespanEnabled) { - this.updated |= ! Objects.equals(this.offlineSessionMaxLifespanEnabled, offlineSessionMaxLifespanEnabled); - this.offlineSessionMaxLifespanEnabled = offlineSessionMaxLifespanEnabled; - } - - public Boolean isEventsEnabled() { - return eventsEnabled; - } - - public void setEventsEnabled(Boolean eventsEnabled) { - this.updated |= ! Objects.equals(this.eventsEnabled, eventsEnabled); - this.eventsEnabled = eventsEnabled; - } - - public Integer getRefreshTokenMaxReuse() { - return refreshTokenMaxReuse; - } - - public void setRefreshTokenMaxReuse(Integer refreshTokenMaxReuse) { - this.updated |= ! Objects.equals(this.refreshTokenMaxReuse, refreshTokenMaxReuse); - this.refreshTokenMaxReuse = refreshTokenMaxReuse; - } - - public Integer getSsoSessionIdleTimeout() { - return ssoSessionIdleTimeout; - } - - public void setSsoSessionIdleTimeout(Integer ssoSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.ssoSessionIdleTimeout, ssoSessionIdleTimeout); - this.ssoSessionIdleTimeout = ssoSessionIdleTimeout; - } - - public Integer getSsoSessionMaxLifespan() { - return ssoSessionMaxLifespan; - } - - public void setSsoSessionMaxLifespan(Integer ssoSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.ssoSessionMaxLifespan, ssoSessionMaxLifespan); - this.ssoSessionMaxLifespan = ssoSessionMaxLifespan; - } - - public Integer getSsoSessionIdleTimeoutRememberMe() { - return ssoSessionIdleTimeoutRememberMe; - } - - public void setSsoSessionIdleTimeoutRememberMe(Integer ssoSessionIdleTimeoutRememberMe) { - this.updated |= ! Objects.equals(this.ssoSessionIdleTimeoutRememberMe, ssoSessionIdleTimeoutRememberMe); - this.ssoSessionIdleTimeoutRememberMe = ssoSessionIdleTimeoutRememberMe; - } - - public Integer getSsoSessionMaxLifespanRememberMe() { - return ssoSessionMaxLifespanRememberMe; - } - - public void setSsoSessionMaxLifespanRememberMe(Integer ssoSessionMaxLifespanRememberMe) { - this.updated |= ! Objects.equals(this.ssoSessionMaxLifespanRememberMe, ssoSessionMaxLifespanRememberMe); - this.ssoSessionMaxLifespanRememberMe = ssoSessionMaxLifespanRememberMe; - } - - public Integer getOfflineSessionIdleTimeout() { - return offlineSessionIdleTimeout; - } - - public void setOfflineSessionIdleTimeout(Integer offlineSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.offlineSessionIdleTimeout, offlineSessionIdleTimeout); - this.offlineSessionIdleTimeout = offlineSessionIdleTimeout; - } - - public Integer getAccessTokenLifespan() { - return accessTokenLifespan; - } - - public void setAccessTokenLifespan(Integer accessTokenLifespan) { - this.updated |= ! Objects.equals(this.accessTokenLifespan, accessTokenLifespan); - this.accessTokenLifespan = accessTokenLifespan; - } - - public Integer getAccessTokenLifespanForImplicitFlow() { - return accessTokenLifespanForImplicitFlow; - } - - public void setAccessTokenLifespanForImplicitFlow(Integer accessTokenLifespanForImplicitFlow) { - this.updated |= ! Objects.equals(this.accessTokenLifespanForImplicitFlow, accessTokenLifespanForImplicitFlow); - this.accessTokenLifespanForImplicitFlow = accessTokenLifespanForImplicitFlow; - } - - public Integer getAccessCodeLifespan() { - return accessCodeLifespan; - } - - public void setAccessCodeLifespan(Integer accessCodeLifespan) { - this.updated |= ! Objects.equals(this.accessCodeLifespan, accessCodeLifespan); - this.accessCodeLifespan = accessCodeLifespan; - } - - public Integer getAccessCodeLifespanUserAction() { - return accessCodeLifespanUserAction; - } - - public void setAccessCodeLifespanUserAction(Integer accessCodeLifespanUserAction) { - this.updated |= ! Objects.equals(this.accessCodeLifespanUserAction, accessCodeLifespanUserAction); - this.accessCodeLifespanUserAction = accessCodeLifespanUserAction; - } - - public Integer getAccessCodeLifespanLogin() { - return accessCodeLifespanLogin; - } - - public void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin) { - this.updated |= ! Objects.equals(this.accessCodeLifespanLogin, accessCodeLifespanLogin); - this.accessCodeLifespanLogin = accessCodeLifespanLogin; - } - - public Integer getNotBefore() { - return notBefore; - } - - public void setNotBefore(Integer notBefore) { - this.updated |= ! Objects.equals(this.notBefore, notBefore); - this.notBefore = notBefore; - } - - public Integer getClientSessionIdleTimeout() { - return clientSessionIdleTimeout; - } - - public void setClientSessionIdleTimeout(Integer clientSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.clientSessionIdleTimeout, clientSessionIdleTimeout); - this.clientSessionIdleTimeout = clientSessionIdleTimeout; - } - - public Integer getClientSessionMaxLifespan() { - return clientSessionMaxLifespan; - } - - public void setClientSessionMaxLifespan(Integer clientSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.clientSessionMaxLifespan, clientSessionMaxLifespan); - this.clientSessionMaxLifespan = clientSessionMaxLifespan; - } - - public Integer getClientOfflineSessionIdleTimeout() { - return clientOfflineSessionIdleTimeout; - } - - public void setClientOfflineSessionIdleTimeout(Integer clientOfflineSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.clientOfflineSessionIdleTimeout, clientOfflineSessionIdleTimeout); - this.clientOfflineSessionIdleTimeout = clientOfflineSessionIdleTimeout; - } - - public Integer getClientOfflineSessionMaxLifespan() { - return clientOfflineSessionMaxLifespan; - } - - public void setClientOfflineSessionMaxLifespan(Integer clientOfflineSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.clientOfflineSessionMaxLifespan, clientOfflineSessionMaxLifespan); - this.clientOfflineSessionMaxLifespan = clientOfflineSessionMaxLifespan; - } - - public Integer getActionTokenGeneratedByAdminLifespan() { - return actionTokenGeneratedByAdminLifespan; - } - - public void setActionTokenGeneratedByAdminLifespan(Integer actionTokenGeneratedByAdminLifespan) { - this.updated |= ! Objects.equals(this.actionTokenGeneratedByAdminLifespan, actionTokenGeneratedByAdminLifespan); - this.actionTokenGeneratedByAdminLifespan = actionTokenGeneratedByAdminLifespan; - } - - public Integer getOfflineSessionMaxLifespan() { - return offlineSessionMaxLifespan; - } - - public void setOfflineSessionMaxLifespan(Integer offlineSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.offlineSessionMaxLifespan, offlineSessionMaxLifespan); - this.offlineSessionMaxLifespan = offlineSessionMaxLifespan; - } - - public Long getEventsExpiration() { - return eventsExpiration; - } - - public void setEventsExpiration(Long eventsExpiration) { - this.updated |= ! Objects.equals(this.eventsExpiration, eventsExpiration); - this.eventsExpiration = eventsExpiration; - } - - public String getPasswordPolicy() { - return passwordPolicy; - } - - public void setPasswordPolicy(String passwordPolicy) { - this.updated |= ! Objects.equals(this.passwordPolicy, passwordPolicy); - this.passwordPolicy = passwordPolicy; - } - - public String getSslRequired() { - return sslRequired; - } - - public void setSslRequired(String sslRequired) { - this.updated |= ! Objects.equals(this.sslRequired, sslRequired); - this.sslRequired = sslRequired; - } - - public String getLoginTheme() { - return loginTheme; - } - - public void setLoginTheme(String loginTheme) { - this.updated |= ! Objects.equals(this.loginTheme, loginTheme); - this.loginTheme = loginTheme; - } - - public String getAccountTheme() { - return accountTheme; - } - - public void setAccountTheme(String accountTheme) { - this.updated |= ! Objects.equals(this.accountTheme, accountTheme); - this.accountTheme = accountTheme; - } - - public String getAdminTheme() { - return adminTheme; - } - - public void setAdminTheme(String adminTheme) { - this.updated |= ! Objects.equals(this.adminTheme, adminTheme); - this.adminTheme = adminTheme; - } - - public String getEmailTheme() { - return emailTheme; - } - - public void setEmailTheme(String emailTheme) { - this.updated |= ! Objects.equals(this.emailTheme, emailTheme); - this.emailTheme = emailTheme; - } - - public String getMasterAdminClient() { - return masterAdminClient; - } - - public void setMasterAdminClient(String masterAdminClient) { - this.updated |= ! Objects.equals(this.masterAdminClient, masterAdminClient); - this.masterAdminClient = masterAdminClient; - } - - public String getDefaultRoleId() { - return defaultRoleId; - } - - public void setDefaultRoleId(String defaultRoleId) { - this.updated |= ! Objects.equals(this.defaultRoleId, defaultRoleId); - this.defaultRoleId = defaultRoleId; - } - - public String getDefaultLocale() { - return defaultLocale; - } - - public void setDefaultLocale(String defaultLocale) { - this.updated |= ! Objects.equals(this.defaultLocale, defaultLocale); - this.defaultLocale = defaultLocale; - } - - public String getBrowserFlow() { - return browserFlow; - } - - public void setBrowserFlow(String browserFlow) { - this.updated |= ! Objects.equals(this.browserFlow, browserFlow); - this.browserFlow = browserFlow; - } - - public String getRegistrationFlow() { - return registrationFlow; - } - - public void setRegistrationFlow(String registrationFlow) { - this.updated |= ! Objects.equals(this.registrationFlow, registrationFlow); - this.registrationFlow = registrationFlow; - } - - public String getDirectGrantFlow() { - return directGrantFlow; - } - - public void setDirectGrantFlow(String directGrantFlow) { - this.updated |= ! Objects.equals(this.directGrantFlow, directGrantFlow); - this.directGrantFlow = directGrantFlow; - } - - public String getResetCredentialsFlow() { - return resetCredentialsFlow; - } - - public void setResetCredentialsFlow(String resetCredentialsFlow) { - this.updated |= ! Objects.equals(this.resetCredentialsFlow, resetCredentialsFlow); - this.resetCredentialsFlow = resetCredentialsFlow; - } - - public String getClientAuthenticationFlow() { - return clientAuthenticationFlow; - } - - public void setClientAuthenticationFlow(String clientAuthenticationFlow) { - this.updated |= ! Objects.equals(this.clientAuthenticationFlow, clientAuthenticationFlow); - this.clientAuthenticationFlow = clientAuthenticationFlow; - } - - public String getDockerAuthenticationFlow() { - return dockerAuthenticationFlow; - } - - public void setDockerAuthenticationFlow(String dockerAuthenticationFlow) { - this.updated |= ! Objects.equals(this.dockerAuthenticationFlow, dockerAuthenticationFlow); - this.dockerAuthenticationFlow = dockerAuthenticationFlow; - } - - public MapOTPPolicyEntity getOTPPolicy() { - return otpPolicy; - } - - public void setOTPPolicy(MapOTPPolicyEntity otpPolicy) { - this.updated |= ! Objects.equals(this.otpPolicy, otpPolicy); - this.otpPolicy = otpPolicy; - } - - public MapWebAuthnPolicyEntity getWebAuthnPolicy() { - return webAuthnPolicy; - } - - public void setWebAuthnPolicy(MapWebAuthnPolicyEntity webAuthnPolicy) { - this.updated |= ! Objects.equals(this.webAuthnPolicy, webAuthnPolicy); - this.webAuthnPolicy = webAuthnPolicy; - } - - public MapWebAuthnPolicyEntity getWebAuthnPolicyPasswordless() { - return webAuthnPolicyPasswordless; - } - - public void setWebAuthnPolicyPasswordless(MapWebAuthnPolicyEntity webAuthnPolicyPasswordless) { - this.updated |= ! Objects.equals(this.webAuthnPolicyPasswordless, webAuthnPolicyPasswordless); - this.webAuthnPolicyPasswordless = webAuthnPolicyPasswordless; - } - - public void setAttribute(String name, String value) { - this.updated |= !Objects.equals(this.attributes.put(name, value), value); - } - - public void removeAttribute(String name) { - this.updated |= attributes.remove(name) != null; - } - - public String getAttribute(String name) { - return attributes.get(name); - } - - public Map getAttributes() { - return attributes; - } - - public void addDefaultClientScope(String scopeId) { - this.updated |= this.defaultClientScopes.add(scopeId); - } - - public Stream getDefaultClientScopeIds() { - return defaultClientScopes.stream(); - } - - public void addOptionalClientScope(String scopeId) { - this.updated |= this.optionalClientScopes.add(scopeId); - } - - public Stream getOptionalClientScopeIds() { - return optionalClientScopes.stream(); - } - - public void removeDefaultOrOptionalClientScope(String scopeId) { - if (this.defaultClientScopes.remove(scopeId)) { - this.updated = true; - return ; - } - this.updated |= this.optionalClientScopes.remove(scopeId); - } - - public Stream getDefaultGroupIds() { - return defaultGroupIds.stream(); - } - - public void addDefaultGroup(String groupId) { - this.updated |= this.defaultGroupIds.add(groupId); - } - - public void removeDefaultGroup(String groupId) { - this.updated |= this.defaultGroupIds.remove(groupId); - } - - public Set getEventsListeners() { - return eventsListeners; - } - - public void setEventsListeners(Set eventsListeners) { - if (eventsListeners == null) return; - this.updated |= ! Objects.equals(eventsListeners, this.eventsListeners); - this.eventsListeners = eventsListeners; - } - - public Set getEnabledEventTypes() { - return enabledEventTypes; - } - - public void setEnabledEventTypes(Set enabledEventTypes) { - if (enabledEventTypes == null) return; - this.updated |= ! Objects.equals(enabledEventTypes, this.enabledEventTypes); - this.enabledEventTypes = enabledEventTypes; - } - - public Set getSupportedLocales() { - return supportedLocales; - } - - public void setSupportedLocales(Set supportedLocales) { - if (supportedLocales == null) return; - this.updated |= ! Objects.equals(supportedLocales, this.supportedLocales); - this.supportedLocales = supportedLocales; - } - - public Map> getLocalizationTexts() { - return localizationTexts; - } - - public Map getLocalizationText(String locale) { - if (localizationTexts.containsKey(locale)) { - return localizationTexts.get(locale); - } - return Collections.emptyMap(); - } - - public void addLocalizationTexts(String locale, Map texts) { - if (! localizationTexts.containsKey(locale)) { - updated = true; - localizationTexts.put(locale, texts); - } - } - - public void updateLocalizationTexts(String locale, Map texts) { - this.updated |= localizationTexts.replace(locale, texts) != null; - } - - public boolean removeLocalizationTexts(String locale) { - boolean removed = localizationTexts.remove(locale) != null; - updated |= removed; - return removed; - } - - public Map getBrowserSecurityHeaders() { - return browserSecurityHeaders; - } - - public void setBrowserSecurityHeaders(Map headers) { - if (headers == null) return; - this.updated |= ! Objects.equals(this.browserSecurityHeaders, headers); - this.browserSecurityHeaders = headers; - } - - public Map getSmtpConfig() { - return smtpConfig; - } - - public void setSmtpConfig(Map smtpConfig) { - if (smtpConfig == null) return; - this.updated |= ! Objects.equals(this.smtpConfig, smtpConfig); - this.smtpConfig = smtpConfig; - } - - public Stream getRequiredCredentials() { - return requiredCredentials.values().stream(); - } - - public void addRequiredCredential(MapRequiredCredentialEntity requiredCredential) { - if (requiredCredentials.containsKey(requiredCredential.getType())) { - throw new ModelDuplicateException("An RequiredCredential with given type already exists"); - } - this.updated = true; - requiredCredentials.put(requiredCredential.getType(), requiredCredential); - } - - public void updateRequiredCredential(MapRequiredCredentialEntity requiredCredential) { - this.updated |= requiredCredentials.replace(requiredCredential.getType(), requiredCredential) != null; - } - - public Stream getComponents() { - return components.values().stream(); - } - - public MapComponentEntity getComponent(String id) { - return components.get(id); - } - - public void addComponent(MapComponentEntity component) { - if (components.containsKey(component.getId())) { - throw new ModelDuplicateException("A Component with given id already exists"); - } - this.updated = true; - components.put(component.getId(), component); - } - - public void updateComponent(MapComponentEntity component) { - this.updated |= components.replace(component.getId(), component) != null; - } - - public boolean removeComponent(String id) { - boolean removed = this.components.remove(id) != null; - this.updated |= removed; - return removed; - } - - public Stream getAuthenticationFlows() { - return authenticationFlows.values().stream(); - } - - public MapAuthenticationFlowEntity getAuthenticationFlow(String flowId) { - return authenticationFlows.get(flowId); - } - - public void addAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { - if (authenticationFlows.containsKey(authenticationFlow.getId())) { - throw new ModelDuplicateException("An AuthenticationFlow with given id already exists"); - } - this.updated = true; - authenticationFlows.put(authenticationFlow.getId(), authenticationFlow); - } - - public boolean removeAuthenticationFlow(String flowId) { - boolean removed = this.authenticationFlows.remove(flowId) != null; - updated |= removed; - return removed; - } - - public void updateAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { - this.updated |= authenticationFlows.replace(authenticationFlow.getId(), authenticationFlow) != null; - } - - public void addAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { - if (authenticationExecutions.containsKey(authenticationExecution.getId())) { - throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); - } - - this.updated = true; - authenticationExecutions.put(authenticationExecution.getId(), authenticationExecution); - } - - public void updateAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { - this.updated |= authenticationExecutions.replace(authenticationExecution.getId(), authenticationExecution) != null; - } - - public boolean removeAuthenticatonExecution(String id) { - boolean removed = this.authenticationExecutions.remove(id) != null; - updated |= removed; - return removed; - } - - public MapAuthenticationExecutionEntity getAuthenticationExecution(String id) { - return authenticationExecutions.get(id); - } - - public Stream getAuthenticationExecutions() { - return authenticationExecutions.values().stream(); - } - - public Stream getAuthenticatorConfigs() { - return authenticatorConfigs.values().stream(); - } - - public void addAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { - this.updated |= ! Objects.equals(authenticatorConfigs.put(authenticatorConfig.getId(), authenticatorConfig), authenticatorConfig); - } - - public void updateAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { - this.updated |= authenticatorConfigs.replace(authenticatorConfig.getId(), authenticatorConfig) != null; - } - - public boolean removeAuthenticatorConfig(String id) { - boolean removed = this.authenticatorConfigs.remove(id) != null; - updated |= removed; - return removed; - } - - public MapAuthenticatorConfigEntity getAuthenticatorConfig(String id) { - return authenticatorConfigs.get(id); - } - - public Stream getRequiredActionProviders() { - return requiredActionProviders.values().stream(); - } - - public void addRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { - if (requiredActionProviders.containsKey(requiredActionProvider.getId())) { - throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); - } - - this.updated = true; - requiredActionProviders.put(requiredActionProvider.getId(), requiredActionProvider); - } - - public void updateRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { - this.updated |= requiredActionProviders.replace(requiredActionProvider.getId(), requiredActionProvider) != null; - } - - public boolean removeRequiredActionProvider(String id) { - boolean removed = this.requiredActionProviders.remove(id) != null; - updated |= removed; - return removed; - } - - public MapRequiredActionProviderEntity getRequiredActionProvider(String id) { - return requiredActionProviders.get(id); - } - - public Stream getIdentityProviders() { - return identityProviders.values().stream(); - } - - public void addIdentityProvider(MapIdentityProviderEntity identityProvider) { - if (identityProviders.containsKey(identityProvider.getId())) { - throw new ModelDuplicateException("An IdentityProvider with given id already exists"); - } - - this.updated = true; - identityProviders.put(identityProvider.getId(), identityProvider); - } - - public boolean removeIdentityProvider(String id) { - boolean removed = this.identityProviders.remove(id) != null; - updated |= removed; - return removed; - } - - public void updateIdentityProvider(MapIdentityProviderEntity identityProvider) { - this.updated |= identityProviders.replace(identityProvider.getId(), identityProvider) != null; - } - - public Stream getIdentityProviderMappers() { - return identityProviderMappers.values().stream(); - } - - public void addIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { - if (identityProviderMappers.containsKey(identityProviderMapper.getId())) { - throw new ModelDuplicateException("An IdentityProviderMapper with given id already exists"); - } - - this.updated = true; - identityProviderMappers.put(identityProviderMapper.getId(), identityProviderMapper); - } - - public boolean removeIdentityProviderMapper(String id) { - boolean removed = this.identityProviderMappers.remove(id) != null; - updated |= removed; - return removed; - } - - public void updateIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { - this.updated |= identityProviderMappers.replace(identityProviderMapper.getId(), identityProviderMapper) != null; - } - - public MapIdentityProviderMapperEntity getIdentityProviderMapper(String id) { - return identityProviderMappers.get(id); - } - - public boolean hasClientInitialAccess() { - return !clientInitialAccesses.isEmpty(); - } - - public void removeExpiredClientInitialAccesses() { - clientInitialAccesses.values().stream() - .filter(this::checkIfExpired) - .map(MapClientInitialAccessEntity::getId) - .collect(Collectors.toSet()) - .forEach(this::removeClientInitialAccess); - } - - private boolean checkIfExpired(MapClientInitialAccessEntity cia) { - return cia.getRemainingCount() < 1 || - (cia.getExpiration() > 0 && (cia.getTimestamp() + cia.getExpiration()) < Time.currentTime()); - } - - public void addClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { - this.updated = true; - clientInitialAccesses.put(clientInitialAccess.getId(), clientInitialAccess); - } - - public void updateClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { - this.updated |= clientInitialAccesses.replace(clientInitialAccess.getId(), clientInitialAccess) != null; - } - - public MapClientInitialAccessEntity getClientInitialAccess(String id) { - return clientInitialAccesses.get(id); - } - - public boolean removeClientInitialAccess(String id) { - boolean removed = this.clientInitialAccesses.remove(id) != null; - updated |= removed; - return removed; - } - - public Stream getClientInitialAccesses() { - return clientInitialAccesses.values().stream(); - } -} 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 2cfa0dc07a..dfa28b0347 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 @@ -60,7 +60,7 @@ import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity; import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity; import org.keycloak.models.utils.ComponentUtil; -public class MapRealmAdapter extends AbstractRealmModel implements RealmModel { +public abstract class MapRealmAdapter extends AbstractRealmModel> implements RealmModel { private static final String ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN = "actionTokenGeneratedByUserLifespan"; private static final String DEFAULT_SIGNATURE_ALGORITHM = "defaultSignatureAlgorithm"; @@ -75,15 +75,10 @@ public class MapRealmAdapter extends AbstractRealmModel implemen private PasswordPolicy passwordPolicy; - public MapRealmAdapter(KeycloakSession session, MapRealmEntity entity) { + public MapRealmAdapter(KeycloakSession session, MapRealmEntity entity) { super(session, entity); } - @Override - public String getId() { - return entity.getId(); - } - @Override public String getName() { return entity.getName(); diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java index 73b8280869..640bf64542 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java @@ -1,13 +1,13 @@ /* * Copyright 2021 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,14 +16,1015 @@ */ package org.keycloak.models.map.realm; -public class MapRealmEntity extends AbstractRealmEntity { +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.keycloak.common.util.Time; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.OTPPolicy; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.realm.entity.MapAuthenticationExecutionEntity; +import org.keycloak.models.map.realm.entity.MapAuthenticationFlowEntity; +import org.keycloak.models.map.realm.entity.MapAuthenticatorConfigEntity; +import org.keycloak.models.map.realm.entity.MapClientInitialAccessEntity; +import org.keycloak.models.map.realm.entity.MapComponentEntity; +import org.keycloak.models.map.realm.entity.MapIdentityProviderEntity; +import org.keycloak.models.map.realm.entity.MapIdentityProviderMapperEntity; +import org.keycloak.models.map.realm.entity.MapOTPPolicyEntity; +import org.keycloak.models.map.realm.entity.MapRequiredActionProviderEntity; +import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity; +import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity; + +public class MapRealmEntity implements AbstractEntity { + + private final K id; + private String name; + + private Boolean enabled = false; + private Boolean registrationAllowed = false; + private Boolean registrationEmailAsUsername = false; + private Boolean verifyEmail = false; + private Boolean resetPasswordAllowed = false; + private Boolean loginWithEmailAllowed = false; + private Boolean duplicateEmailsAllowed = false; + private Boolean rememberMe = false; + private Boolean editUsernameAllowed = false; + private Boolean revokeRefreshToken = false; + private Boolean adminEventsEnabled = false; + private Boolean adminEventsDetailsEnabled = false; + private Boolean internationalizationEnabled = false; + private Boolean allowUserManagedAccess = false; + private Boolean offlineSessionMaxLifespanEnabled = false; + private Boolean eventsEnabled = false; + private Integer refreshTokenMaxReuse = 0; + private Integer ssoSessionIdleTimeout = 0; + private Integer ssoSessionMaxLifespan = 0; + private Integer ssoSessionIdleTimeoutRememberMe = 0; + private Integer ssoSessionMaxLifespanRememberMe = 0; + private Integer offlineSessionIdleTimeout = 0; + private Integer accessTokenLifespan = 0; + private Integer accessTokenLifespanForImplicitFlow = 0; + private Integer accessCodeLifespan = 0; + private Integer accessCodeLifespanUserAction = 0; + private Integer accessCodeLifespanLogin = 0; + private Integer notBefore = 0; + private Integer clientSessionIdleTimeout = 0; + private Integer clientSessionMaxLifespan = 0; + private Integer clientOfflineSessionIdleTimeout = 0; + private Integer clientOfflineSessionMaxLifespan = 0; + private Integer actionTokenGeneratedByAdminLifespan = 0; + private Integer offlineSessionMaxLifespan = 0; + private Long eventsExpiration = 0l; + private String displayName; + private String displayNameHtml; + private String passwordPolicy; + private String sslRequired; + private String loginTheme; + private String accountTheme; + private String adminTheme; + private String emailTheme; + private String masterAdminClient; + private String defaultRoleId; + private String defaultLocale; + private String browserFlow; + private String registrationFlow; + private String directGrantFlow; + private String resetCredentialsFlow; + private String clientAuthenticationFlow; + private String dockerAuthenticationFlow; + private MapOTPPolicyEntity otpPolicy = MapOTPPolicyEntity.fromModel(OTPPolicy.DEFAULT_POLICY);; + private MapWebAuthnPolicyEntity webAuthnPolicy = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy();; + private MapWebAuthnPolicyEntity webAuthnPolicyPasswordless = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy();; + + private Set eventsListeners = new HashSet<>(); + private Set enabledEventTypes = new HashSet<>(); + private Set supportedLocales = new HashSet<>(); + private Map browserSecurityHeaders = new HashMap<>(); + private Map smtpConfig = new HashMap<>(); + + private final Set defaultGroupIds = new HashSet<>(); + private final Set defaultClientScopes = new HashSet<>(); + private final Set optionalClientScopes = new HashSet<>(); + private final Map attributes = new HashMap<>(); + private final Map> localizationTexts = new HashMap<>(); + private final Map clientInitialAccesses = new HashMap<>(); + private final Map components = new HashMap<>(); + private final Map authenticationFlows = new HashMap<>(); + private final Map authenticationExecutions = new HashMap<>(); + private final Map requiredCredentials = new HashMap<>(); + private final Map authenticatorConfigs = new HashMap<>(); + private final Map identityProviders = new HashMap<>(); + private final Map identityProviderMappers = new HashMap<>(); + private final Map requiredActionProviders = new HashMap<>(); + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; protected MapRealmEntity() { - super(); + this.id = null; } - public MapRealmEntity(String id) { - super(id); + public MapRealmEntity(K id) { + Objects.requireNonNull(id, "id"); + + this.id = id; } + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated + || authenticationExecutions.values().stream().anyMatch(MapAuthenticationExecutionEntity::isUpdated) + || authenticationFlows.values().stream().anyMatch(MapAuthenticationFlowEntity::isUpdated) + || authenticatorConfigs.values().stream().anyMatch(MapAuthenticatorConfigEntity::isUpdated) + || clientInitialAccesses.values().stream().anyMatch(MapClientInitialAccessEntity::isUpdated) + || components.values().stream().anyMatch(MapComponentEntity::isUpdated) + || identityProviders.values().stream().anyMatch(MapIdentityProviderEntity::isUpdated) + || identityProviderMappers.values().stream().anyMatch(MapIdentityProviderMapperEntity::isUpdated) + || requiredActionProviders.values().stream().anyMatch(MapRequiredActionProviderEntity::isUpdated) + || requiredCredentials.values().stream().anyMatch(MapRequiredCredentialEntity::isUpdated) + || otpPolicy.isUpdated() + || webAuthnPolicy.isUpdated() + || webAuthnPolicyPasswordless.isUpdated(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.updated |= ! Objects.equals(this.displayName, displayName); + this.displayName = displayName; + } + + public String getDisplayNameHtml() { + return displayNameHtml; + } + + public void setDisplayNameHtml(String displayNameHtml) { + this.updated |= ! Objects.equals(this.displayNameHtml, displayNameHtml); + this.displayNameHtml = displayNameHtml; + } + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.updated |= ! Objects.equals(this.enabled, enabled); + this.enabled = enabled; + } + + public Boolean isRegistrationAllowed() { + return registrationAllowed; + } + + public void setRegistrationAllowed(Boolean registrationAllowed) { + this.updated |= ! Objects.equals(this.registrationAllowed, registrationAllowed); + this.registrationAllowed = registrationAllowed; + } + + public Boolean isRegistrationEmailAsUsername() { + return registrationEmailAsUsername; + } + + public void setRegistrationEmailAsUsername(Boolean registrationEmailAsUsername) { + this.updated |= ! Objects.equals(this.registrationEmailAsUsername, registrationEmailAsUsername); + this.registrationEmailAsUsername = registrationEmailAsUsername; + } + + public Boolean isVerifyEmail() { + return verifyEmail; + } + + public void setVerifyEmail(Boolean verifyEmail) { + this.updated |= ! Objects.equals(this.verifyEmail, verifyEmail); + this.verifyEmail = verifyEmail; + } + + + public Boolean isResetPasswordAllowed() { + return resetPasswordAllowed; + } + + public void setResetPasswordAllowed(Boolean resetPasswordAllowed) { + this.updated |= ! Objects.equals(this.resetPasswordAllowed, resetPasswordAllowed); + this.resetPasswordAllowed = resetPasswordAllowed; + } + + public Boolean isLoginWithEmailAllowed() { + return loginWithEmailAllowed; + } + + public void setLoginWithEmailAllowed(Boolean loginWithEmailAllowed) { + this.updated |= ! Objects.equals(this.loginWithEmailAllowed, loginWithEmailAllowed); + this.loginWithEmailAllowed = loginWithEmailAllowed; + } + + public Boolean isDuplicateEmailsAllowed() { + return duplicateEmailsAllowed; + } + + public void setDuplicateEmailsAllowed(Boolean duplicateEmailsAllowed) { + this.updated |= ! Objects.equals(this.duplicateEmailsAllowed, duplicateEmailsAllowed); + this.duplicateEmailsAllowed = duplicateEmailsAllowed; + } + + public Boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(Boolean rememberMe) { + this.updated |= ! Objects.equals(this.rememberMe, rememberMe); + this.rememberMe = rememberMe; + } + + public Boolean isEditUsernameAllowed() { + return editUsernameAllowed; + } + + public void setEditUsernameAllowed(Boolean editUsernameAllowed) { + this.updated |= ! Objects.equals(this.editUsernameAllowed, editUsernameAllowed); + this.editUsernameAllowed = editUsernameAllowed; + } + + public Boolean isRevokeRefreshToken() { + return revokeRefreshToken; + } + + public void setRevokeRefreshToken(Boolean revokeRefreshToken) { + this.updated |= ! Objects.equals(this.revokeRefreshToken, revokeRefreshToken); + this.revokeRefreshToken = revokeRefreshToken; + } + + public Boolean isAdminEventsEnabled() { + return adminEventsEnabled; + } + + public void setAdminEventsEnabled(Boolean adminEventsEnabled) { + this.updated |= ! Objects.equals(this.adminEventsEnabled, adminEventsEnabled); + this.adminEventsEnabled = adminEventsEnabled; + } + + public Boolean isAdminEventsDetailsEnabled() { + return adminEventsDetailsEnabled; + } + + public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) { + this.updated |= ! Objects.equals(this.adminEventsDetailsEnabled, adminEventsDetailsEnabled); + this.adminEventsDetailsEnabled = adminEventsDetailsEnabled; + } + + public Boolean isInternationalizationEnabled() { + return internationalizationEnabled; + } + + public void setInternationalizationEnabled(Boolean internationalizationEnabled) { + this.updated |= ! Objects.equals(this.internationalizationEnabled, internationalizationEnabled); + this.internationalizationEnabled = internationalizationEnabled; + } + + public Boolean isAllowUserManagedAccess() { + return allowUserManagedAccess; + } + + public void setAllowUserManagedAccess(Boolean allowUserManagedAccess) { + this.updated |= ! Objects.equals(this.allowUserManagedAccess, allowUserManagedAccess); + this.allowUserManagedAccess = allowUserManagedAccess; + } + + public Boolean isOfflineSessionMaxLifespanEnabled() { + return offlineSessionMaxLifespanEnabled; + } + + public void setOfflineSessionMaxLifespanEnabled(Boolean offlineSessionMaxLifespanEnabled) { + this.updated |= ! Objects.equals(this.offlineSessionMaxLifespanEnabled, offlineSessionMaxLifespanEnabled); + this.offlineSessionMaxLifespanEnabled = offlineSessionMaxLifespanEnabled; + } + + public Boolean isEventsEnabled() { + return eventsEnabled; + } + + public void setEventsEnabled(Boolean eventsEnabled) { + this.updated |= ! Objects.equals(this.eventsEnabled, eventsEnabled); + this.eventsEnabled = eventsEnabled; + } + + public Integer getRefreshTokenMaxReuse() { + return refreshTokenMaxReuse; + } + + public void setRefreshTokenMaxReuse(Integer refreshTokenMaxReuse) { + this.updated |= ! Objects.equals(this.refreshTokenMaxReuse, refreshTokenMaxReuse); + this.refreshTokenMaxReuse = refreshTokenMaxReuse; + } + + public Integer getSsoSessionIdleTimeout() { + return ssoSessionIdleTimeout; + } + + public void setSsoSessionIdleTimeout(Integer ssoSessionIdleTimeout) { + this.updated |= ! Objects.equals(this.ssoSessionIdleTimeout, ssoSessionIdleTimeout); + this.ssoSessionIdleTimeout = ssoSessionIdleTimeout; + } + + public Integer getSsoSessionMaxLifespan() { + return ssoSessionMaxLifespan; + } + + public void setSsoSessionMaxLifespan(Integer ssoSessionMaxLifespan) { + this.updated |= ! Objects.equals(this.ssoSessionMaxLifespan, ssoSessionMaxLifespan); + this.ssoSessionMaxLifespan = ssoSessionMaxLifespan; + } + + public Integer getSsoSessionIdleTimeoutRememberMe() { + return ssoSessionIdleTimeoutRememberMe; + } + + public void setSsoSessionIdleTimeoutRememberMe(Integer ssoSessionIdleTimeoutRememberMe) { + this.updated |= ! Objects.equals(this.ssoSessionIdleTimeoutRememberMe, ssoSessionIdleTimeoutRememberMe); + this.ssoSessionIdleTimeoutRememberMe = ssoSessionIdleTimeoutRememberMe; + } + + public Integer getSsoSessionMaxLifespanRememberMe() { + return ssoSessionMaxLifespanRememberMe; + } + + public void setSsoSessionMaxLifespanRememberMe(Integer ssoSessionMaxLifespanRememberMe) { + this.updated |= ! Objects.equals(this.ssoSessionMaxLifespanRememberMe, ssoSessionMaxLifespanRememberMe); + this.ssoSessionMaxLifespanRememberMe = ssoSessionMaxLifespanRememberMe; + } + + public Integer getOfflineSessionIdleTimeout() { + return offlineSessionIdleTimeout; + } + + public void setOfflineSessionIdleTimeout(Integer offlineSessionIdleTimeout) { + this.updated |= ! Objects.equals(this.offlineSessionIdleTimeout, offlineSessionIdleTimeout); + this.offlineSessionIdleTimeout = offlineSessionIdleTimeout; + } + + public Integer getAccessTokenLifespan() { + return accessTokenLifespan; + } + + public void setAccessTokenLifespan(Integer accessTokenLifespan) { + this.updated |= ! Objects.equals(this.accessTokenLifespan, accessTokenLifespan); + this.accessTokenLifespan = accessTokenLifespan; + } + + public Integer getAccessTokenLifespanForImplicitFlow() { + return accessTokenLifespanForImplicitFlow; + } + + public void setAccessTokenLifespanForImplicitFlow(Integer accessTokenLifespanForImplicitFlow) { + this.updated |= ! Objects.equals(this.accessTokenLifespanForImplicitFlow, accessTokenLifespanForImplicitFlow); + this.accessTokenLifespanForImplicitFlow = accessTokenLifespanForImplicitFlow; + } + + public Integer getAccessCodeLifespan() { + return accessCodeLifespan; + } + + public void setAccessCodeLifespan(Integer accessCodeLifespan) { + this.updated |= ! Objects.equals(this.accessCodeLifespan, accessCodeLifespan); + this.accessCodeLifespan = accessCodeLifespan; + } + + public Integer getAccessCodeLifespanUserAction() { + return accessCodeLifespanUserAction; + } + + public void setAccessCodeLifespanUserAction(Integer accessCodeLifespanUserAction) { + this.updated |= ! Objects.equals(this.accessCodeLifespanUserAction, accessCodeLifespanUserAction); + this.accessCodeLifespanUserAction = accessCodeLifespanUserAction; + } + + public Integer getAccessCodeLifespanLogin() { + return accessCodeLifespanLogin; + } + + public void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin) { + this.updated |= ! Objects.equals(this.accessCodeLifespanLogin, accessCodeLifespanLogin); + this.accessCodeLifespanLogin = accessCodeLifespanLogin; + } + + public Integer getNotBefore() { + return notBefore; + } + + public void setNotBefore(Integer notBefore) { + this.updated |= ! Objects.equals(this.notBefore, notBefore); + this.notBefore = notBefore; + } + + public Integer getClientSessionIdleTimeout() { + return clientSessionIdleTimeout; + } + + public void setClientSessionIdleTimeout(Integer clientSessionIdleTimeout) { + this.updated |= ! Objects.equals(this.clientSessionIdleTimeout, clientSessionIdleTimeout); + this.clientSessionIdleTimeout = clientSessionIdleTimeout; + } + + public Integer getClientSessionMaxLifespan() { + return clientSessionMaxLifespan; + } + + public void setClientSessionMaxLifespan(Integer clientSessionMaxLifespan) { + this.updated |= ! Objects.equals(this.clientSessionMaxLifespan, clientSessionMaxLifespan); + this.clientSessionMaxLifespan = clientSessionMaxLifespan; + } + + public Integer getClientOfflineSessionIdleTimeout() { + return clientOfflineSessionIdleTimeout; + } + + public void setClientOfflineSessionIdleTimeout(Integer clientOfflineSessionIdleTimeout) { + this.updated |= ! Objects.equals(this.clientOfflineSessionIdleTimeout, clientOfflineSessionIdleTimeout); + this.clientOfflineSessionIdleTimeout = clientOfflineSessionIdleTimeout; + } + + public Integer getClientOfflineSessionMaxLifespan() { + return clientOfflineSessionMaxLifespan; + } + + public void setClientOfflineSessionMaxLifespan(Integer clientOfflineSessionMaxLifespan) { + this.updated |= ! Objects.equals(this.clientOfflineSessionMaxLifespan, clientOfflineSessionMaxLifespan); + this.clientOfflineSessionMaxLifespan = clientOfflineSessionMaxLifespan; + } + + public Integer getActionTokenGeneratedByAdminLifespan() { + return actionTokenGeneratedByAdminLifespan; + } + + public void setActionTokenGeneratedByAdminLifespan(Integer actionTokenGeneratedByAdminLifespan) { + this.updated |= ! Objects.equals(this.actionTokenGeneratedByAdminLifespan, actionTokenGeneratedByAdminLifespan); + this.actionTokenGeneratedByAdminLifespan = actionTokenGeneratedByAdminLifespan; + } + + public Integer getOfflineSessionMaxLifespan() { + return offlineSessionMaxLifespan; + } + + public void setOfflineSessionMaxLifespan(Integer offlineSessionMaxLifespan) { + this.updated |= ! Objects.equals(this.offlineSessionMaxLifespan, offlineSessionMaxLifespan); + this.offlineSessionMaxLifespan = offlineSessionMaxLifespan; + } + + public Long getEventsExpiration() { + return eventsExpiration; + } + + public void setEventsExpiration(Long eventsExpiration) { + this.updated |= ! Objects.equals(this.eventsExpiration, eventsExpiration); + this.eventsExpiration = eventsExpiration; + } + + public String getPasswordPolicy() { + return passwordPolicy; + } + + public void setPasswordPolicy(String passwordPolicy) { + this.updated |= ! Objects.equals(this.passwordPolicy, passwordPolicy); + this.passwordPolicy = passwordPolicy; + } + + public String getSslRequired() { + return sslRequired; + } + + public void setSslRequired(String sslRequired) { + this.updated |= ! Objects.equals(this.sslRequired, sslRequired); + this.sslRequired = sslRequired; + } + + public String getLoginTheme() { + return loginTheme; + } + + public void setLoginTheme(String loginTheme) { + this.updated |= ! Objects.equals(this.loginTheme, loginTheme); + this.loginTheme = loginTheme; + } + + public String getAccountTheme() { + return accountTheme; + } + + public void setAccountTheme(String accountTheme) { + this.updated |= ! Objects.equals(this.accountTheme, accountTheme); + this.accountTheme = accountTheme; + } + + public String getAdminTheme() { + return adminTheme; + } + + public void setAdminTheme(String adminTheme) { + this.updated |= ! Objects.equals(this.adminTheme, adminTheme); + this.adminTheme = adminTheme; + } + + public String getEmailTheme() { + return emailTheme; + } + + public void setEmailTheme(String emailTheme) { + this.updated |= ! Objects.equals(this.emailTheme, emailTheme); + this.emailTheme = emailTheme; + } + + public String getMasterAdminClient() { + return masterAdminClient; + } + + public void setMasterAdminClient(String masterAdminClient) { + this.updated |= ! Objects.equals(this.masterAdminClient, masterAdminClient); + this.masterAdminClient = masterAdminClient; + } + + public String getDefaultRoleId() { + return defaultRoleId; + } + + public void setDefaultRoleId(String defaultRoleId) { + this.updated |= ! Objects.equals(this.defaultRoleId, defaultRoleId); + this.defaultRoleId = defaultRoleId; + } + + public String getDefaultLocale() { + return defaultLocale; + } + + public void setDefaultLocale(String defaultLocale) { + this.updated |= ! Objects.equals(this.defaultLocale, defaultLocale); + this.defaultLocale = defaultLocale; + } + + public String getBrowserFlow() { + return browserFlow; + } + + public void setBrowserFlow(String browserFlow) { + this.updated |= ! Objects.equals(this.browserFlow, browserFlow); + this.browserFlow = browserFlow; + } + + public String getRegistrationFlow() { + return registrationFlow; + } + + public void setRegistrationFlow(String registrationFlow) { + this.updated |= ! Objects.equals(this.registrationFlow, registrationFlow); + this.registrationFlow = registrationFlow; + } + + public String getDirectGrantFlow() { + return directGrantFlow; + } + + public void setDirectGrantFlow(String directGrantFlow) { + this.updated |= ! Objects.equals(this.directGrantFlow, directGrantFlow); + this.directGrantFlow = directGrantFlow; + } + + public String getResetCredentialsFlow() { + return resetCredentialsFlow; + } + + public void setResetCredentialsFlow(String resetCredentialsFlow) { + this.updated |= ! Objects.equals(this.resetCredentialsFlow, resetCredentialsFlow); + this.resetCredentialsFlow = resetCredentialsFlow; + } + + public String getClientAuthenticationFlow() { + return clientAuthenticationFlow; + } + + public void setClientAuthenticationFlow(String clientAuthenticationFlow) { + this.updated |= ! Objects.equals(this.clientAuthenticationFlow, clientAuthenticationFlow); + this.clientAuthenticationFlow = clientAuthenticationFlow; + } + + public String getDockerAuthenticationFlow() { + return dockerAuthenticationFlow; + } + + public void setDockerAuthenticationFlow(String dockerAuthenticationFlow) { + this.updated |= ! Objects.equals(this.dockerAuthenticationFlow, dockerAuthenticationFlow); + this.dockerAuthenticationFlow = dockerAuthenticationFlow; + } + + public MapOTPPolicyEntity getOTPPolicy() { + return otpPolicy; + } + + public void setOTPPolicy(MapOTPPolicyEntity otpPolicy) { + this.updated |= ! Objects.equals(this.otpPolicy, otpPolicy); + this.otpPolicy = otpPolicy; + } + + public MapWebAuthnPolicyEntity getWebAuthnPolicy() { + return webAuthnPolicy; + } + + public void setWebAuthnPolicy(MapWebAuthnPolicyEntity webAuthnPolicy) { + this.updated |= ! Objects.equals(this.webAuthnPolicy, webAuthnPolicy); + this.webAuthnPolicy = webAuthnPolicy; + } + + public MapWebAuthnPolicyEntity getWebAuthnPolicyPasswordless() { + return webAuthnPolicyPasswordless; + } + + public void setWebAuthnPolicyPasswordless(MapWebAuthnPolicyEntity webAuthnPolicyPasswordless) { + this.updated |= ! Objects.equals(this.webAuthnPolicyPasswordless, webAuthnPolicyPasswordless); + this.webAuthnPolicyPasswordless = webAuthnPolicyPasswordless; + } + + public void setAttribute(String name, String value) { + this.updated |= !Objects.equals(this.attributes.put(name, value), value); + } + + public void removeAttribute(String name) { + this.updated |= attributes.remove(name) != null; + } + + public String getAttribute(String name) { + return attributes.get(name); + } + + public Map getAttributes() { + return attributes; + } + + public void addDefaultClientScope(String scopeId) { + this.updated |= this.defaultClientScopes.add(scopeId); + } + + public Stream getDefaultClientScopeIds() { + return defaultClientScopes.stream(); + } + + public void addOptionalClientScope(String scopeId) { + this.updated |= this.optionalClientScopes.add(scopeId); + } + + public Stream getOptionalClientScopeIds() { + return optionalClientScopes.stream(); + } + + public void removeDefaultOrOptionalClientScope(String scopeId) { + if (this.defaultClientScopes.remove(scopeId)) { + this.updated = true; + return ; + } + this.updated |= this.optionalClientScopes.remove(scopeId); + } + + public Stream getDefaultGroupIds() { + return defaultGroupIds.stream(); + } + + public void addDefaultGroup(String groupId) { + this.updated |= this.defaultGroupIds.add(groupId); + } + + public void removeDefaultGroup(String groupId) { + this.updated |= this.defaultGroupIds.remove(groupId); + } + + public Set getEventsListeners() { + return eventsListeners; + } + + public void setEventsListeners(Set eventsListeners) { + if (eventsListeners == null) return; + this.updated |= ! Objects.equals(eventsListeners, this.eventsListeners); + this.eventsListeners = eventsListeners; + } + + public Set getEnabledEventTypes() { + return enabledEventTypes; + } + + public void setEnabledEventTypes(Set enabledEventTypes) { + if (enabledEventTypes == null) return; + this.updated |= ! Objects.equals(enabledEventTypes, this.enabledEventTypes); + this.enabledEventTypes = enabledEventTypes; + } + + public Set getSupportedLocales() { + return supportedLocales; + } + + public void setSupportedLocales(Set supportedLocales) { + if (supportedLocales == null) return; + this.updated |= ! Objects.equals(supportedLocales, this.supportedLocales); + this.supportedLocales = supportedLocales; + } + + public Map> getLocalizationTexts() { + return localizationTexts; + } + + public Map getLocalizationText(String locale) { + if (localizationTexts.containsKey(locale)) { + return localizationTexts.get(locale); + } + return Collections.emptyMap(); + } + + public void addLocalizationTexts(String locale, Map texts) { + if (! localizationTexts.containsKey(locale)) { + updated = true; + localizationTexts.put(locale, texts); + } + } + + public void updateLocalizationTexts(String locale, Map texts) { + this.updated |= localizationTexts.replace(locale, texts) != null; + } + + public boolean removeLocalizationTexts(String locale) { + boolean removed = localizationTexts.remove(locale) != null; + updated |= removed; + return removed; + } + + public Map getBrowserSecurityHeaders() { + return browserSecurityHeaders; + } + + public void setBrowserSecurityHeaders(Map headers) { + if (headers == null) return; + this.updated |= ! Objects.equals(this.browserSecurityHeaders, headers); + this.browserSecurityHeaders = headers; + } + + public Map getSmtpConfig() { + return smtpConfig; + } + + public void setSmtpConfig(Map smtpConfig) { + if (smtpConfig == null) return; + this.updated |= ! Objects.equals(this.smtpConfig, smtpConfig); + this.smtpConfig = smtpConfig; + } + + public Stream getRequiredCredentials() { + return requiredCredentials.values().stream(); + } + + public void addRequiredCredential(MapRequiredCredentialEntity requiredCredential) { + if (requiredCredentials.containsKey(requiredCredential.getType())) { + throw new ModelDuplicateException("An RequiredCredential with given type already exists"); + } + this.updated = true; + requiredCredentials.put(requiredCredential.getType(), requiredCredential); + } + + public void updateRequiredCredential(MapRequiredCredentialEntity requiredCredential) { + this.updated |= requiredCredentials.replace(requiredCredential.getType(), requiredCredential) != null; + } + + public Stream getComponents() { + return components.values().stream(); + } + + public MapComponentEntity getComponent(String id) { + return components.get(id); + } + + public void addComponent(MapComponentEntity component) { + if (components.containsKey(component.getId())) { + throw new ModelDuplicateException("A Component with given id already exists"); + } + this.updated = true; + components.put(component.getId(), component); + } + + public void updateComponent(MapComponentEntity component) { + this.updated |= components.replace(component.getId(), component) != null; + } + + public boolean removeComponent(String id) { + boolean removed = this.components.remove(id) != null; + this.updated |= removed; + return removed; + } + + public Stream getAuthenticationFlows() { + return authenticationFlows.values().stream(); + } + + public MapAuthenticationFlowEntity getAuthenticationFlow(String flowId) { + return authenticationFlows.get(flowId); + } + + public void addAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { + if (authenticationFlows.containsKey(authenticationFlow.getId())) { + throw new ModelDuplicateException("An AuthenticationFlow with given id already exists"); + } + this.updated = true; + authenticationFlows.put(authenticationFlow.getId(), authenticationFlow); + } + + public boolean removeAuthenticationFlow(String flowId) { + boolean removed = this.authenticationFlows.remove(flowId) != null; + updated |= removed; + return removed; + } + + public void updateAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { + this.updated |= authenticationFlows.replace(authenticationFlow.getId(), authenticationFlow) != null; + } + + public void addAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { + if (authenticationExecutions.containsKey(authenticationExecution.getId())) { + throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); + } + + this.updated = true; + authenticationExecutions.put(authenticationExecution.getId(), authenticationExecution); + } + + public void updateAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { + this.updated |= authenticationExecutions.replace(authenticationExecution.getId(), authenticationExecution) != null; + } + + public boolean removeAuthenticatonExecution(String id) { + boolean removed = this.authenticationExecutions.remove(id) != null; + updated |= removed; + return removed; + } + + public MapAuthenticationExecutionEntity getAuthenticationExecution(String id) { + return authenticationExecutions.get(id); + } + + public Stream getAuthenticationExecutions() { + return authenticationExecutions.values().stream(); + } + + public Stream getAuthenticatorConfigs() { + return authenticatorConfigs.values().stream(); + } + + public void addAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { + this.updated |= ! Objects.equals(authenticatorConfigs.put(authenticatorConfig.getId(), authenticatorConfig), authenticatorConfig); + } + + public void updateAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { + this.updated |= authenticatorConfigs.replace(authenticatorConfig.getId(), authenticatorConfig) != null; + } + + public boolean removeAuthenticatorConfig(String id) { + boolean removed = this.authenticatorConfigs.remove(id) != null; + updated |= removed; + return removed; + } + + public MapAuthenticatorConfigEntity getAuthenticatorConfig(String id) { + return authenticatorConfigs.get(id); + } + + public Stream getRequiredActionProviders() { + return requiredActionProviders.values().stream(); + } + + public void addRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { + if (requiredActionProviders.containsKey(requiredActionProvider.getId())) { + throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); + } + + this.updated = true; + requiredActionProviders.put(requiredActionProvider.getId(), requiredActionProvider); + } + + public void updateRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { + this.updated |= requiredActionProviders.replace(requiredActionProvider.getId(), requiredActionProvider) != null; + } + + public boolean removeRequiredActionProvider(String id) { + boolean removed = this.requiredActionProviders.remove(id) != null; + updated |= removed; + return removed; + } + + public MapRequiredActionProviderEntity getRequiredActionProvider(String id) { + return requiredActionProviders.get(id); + } + + public Stream getIdentityProviders() { + return identityProviders.values().stream(); + } + + public void addIdentityProvider(MapIdentityProviderEntity identityProvider) { + if (identityProviders.containsKey(identityProvider.getId())) { + throw new ModelDuplicateException("An IdentityProvider with given id already exists"); + } + + this.updated = true; + identityProviders.put(identityProvider.getId(), identityProvider); + } + + public boolean removeIdentityProvider(String id) { + boolean removed = this.identityProviders.remove(id) != null; + updated |= removed; + return removed; + } + + public void updateIdentityProvider(MapIdentityProviderEntity identityProvider) { + this.updated |= identityProviders.replace(identityProvider.getId(), identityProvider) != null; + } + + public Stream getIdentityProviderMappers() { + return identityProviderMappers.values().stream(); + } + + public void addIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { + if (identityProviderMappers.containsKey(identityProviderMapper.getId())) { + throw new ModelDuplicateException("An IdentityProviderMapper with given id already exists"); + } + + this.updated = true; + identityProviderMappers.put(identityProviderMapper.getId(), identityProviderMapper); + } + + public boolean removeIdentityProviderMapper(String id) { + boolean removed = this.identityProviderMappers.remove(id) != null; + updated |= removed; + return removed; + } + + public void updateIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { + this.updated |= identityProviderMappers.replace(identityProviderMapper.getId(), identityProviderMapper) != null; + } + + public MapIdentityProviderMapperEntity getIdentityProviderMapper(String id) { + return identityProviderMappers.get(id); + } + + public boolean hasClientInitialAccess() { + return !clientInitialAccesses.isEmpty(); + } + + public void removeExpiredClientInitialAccesses() { + clientInitialAccesses.values().stream() + .filter(this::checkIfExpired) + .map(MapClientInitialAccessEntity::getId) + .collect(Collectors.toSet()) + .forEach(this::removeClientInitialAccess); + } + + private boolean checkIfExpired(MapClientInitialAccessEntity cia) { + return cia.getRemainingCount() < 1 || + (cia.getExpiration() > 0 && (cia.getTimestamp() + cia.getExpiration()) < Time.currentTime()); + } + + public void addClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { + this.updated = true; + clientInitialAccesses.put(clientInitialAccess.getId(), clientInitialAccess); + } + + public void updateClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { + this.updated |= clientInitialAccesses.replace(clientInitialAccess.getId(), clientInitialAccess) != null; + } + + public MapClientInitialAccessEntity getClientInitialAccess(String id) { + return clientInitialAccesses.get(id); + } + + public boolean removeClientInitialAccess(String id) { + boolean removed = this.clientInitialAccesses.remove(id) != null; + updated |= removed; + return removed; + } + + public Stream getClientInitialAccesses() { + return clientInitialAccesses.values().stream(); + } } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java index 147d97c843..e98904d21d 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java @@ -39,29 +39,34 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapRealmProvider implements RealmProvider { +public class MapRealmProvider implements RealmProvider { private static final Logger LOG = Logger.getLogger(MapRealmProvider.class); private final KeycloakSession session; - final MapKeycloakTransaction tx; - private final MapStorage realmStore; + final MapKeycloakTransaction, RealmModel> tx; + private final MapStorage, RealmModel> realmStore; - public MapRealmProvider(KeycloakSession session, MapStorage realmStore) { + public MapRealmProvider(KeycloakSession session, MapStorage, RealmModel> realmStore) { this.session = session; this.realmStore = realmStore; this.tx = realmStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private RealmModel entityToAdapter(MapRealmEntity entity) { + private RealmModel entityToAdapter(MapRealmEntity entity) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return new MapRealmAdapter(session, registerEntityForChanges(entity)); + return new MapRealmAdapter(session, registerEntityForChanges(entity)) { + @Override + public String getId() { + return realmStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } - private MapRealmEntity registerEntityForChanges(MapRealmEntity origEntity) { - final MapRealmEntity res = Serialization.from(origEntity); - tx.updateIfChanged(origEntity.getId(), res, MapRealmEntity::isUpdated); + private MapRealmEntity registerEntityForChanges(MapRealmEntity origEntity) { + final MapRealmEntity res = Serialization.from(origEntity); + tx.updateIfChanged(origEntity.getId(), res, MapRealmEntity::isUpdated); return res; } @@ -76,20 +81,21 @@ public class MapRealmProvider implements RealmProvider { throw new ModelDuplicateException("Realm with given name exists: " + name); } - if (id != null) { - if (tx.read(id) != null) { - throw new ModelDuplicateException("Realm exists: " + id); + K kId = id == null ? null : realmStore.getKeyConvertor().fromString(id); + if (kId != null) { + if (tx.read(kId) != null) { + throw new ModelDuplicateException("Realm exists: " + kId); } } else { - id = KeycloakModelUtils.generateId(); + kId = realmStore.getKeyConvertor().yieldNewUniqueKey(); } - LOG.tracef("createRealm(%s, %s)%s", id, name, getShortStackTrace()); + LOG.tracef("createRealm(%s, %s)%s", kId, name, getShortStackTrace()); - MapRealmEntity entity = new MapRealmEntity(id); + MapRealmEntity entity = new MapRealmEntity<>(kId); entity.setName(name); - tx.create(id, entity); + tx.create(kId, entity); return entityToAdapter(entity); } @@ -99,7 +105,7 @@ public class MapRealmProvider implements RealmProvider { LOG.tracef("getRealm(%s)%s", id, getShortStackTrace()); - MapRealmEntity entity = tx.read(id); + MapRealmEntity entity = tx.read(realmStore.getKeyConvertor().fromStringSafe(id)); return entity == null ? null : entityToAdapter(entity); } @@ -112,12 +118,12 @@ public class MapRealmProvider implements RealmProvider { ModelCriteriaBuilder mcb = realmStore.createCriteriaBuilder() .compare(SearchableFields.NAME, Operator.EQ, name); - String realmId = tx.getUpdatedNotRemoved(mcb) + K realmId = tx.getUpdatedNotRemoved(mcb) .findFirst() - .map(MapRealmEntity::getId) + .map(MapRealmEntity::getId) .orElse(null); //we need to go via session.realms() not to bypass cache - return realmId == null ? null : session.realms().getRealm(realmId); + return realmId == null ? null : session.realms().getRealm(realmStore.getKeyConvertor().keyToString(realmId)); } @Override @@ -167,7 +173,7 @@ public class MapRealmProvider implements RealmProvider { }); // TODO: ^^^^^^^ Up to here - tx.delete(id); + tx.delete(realmStore.getKeyConvertor().fromString(id)); return true; } @@ -178,7 +184,7 @@ public class MapRealmProvider implements RealmProvider { tx.getUpdatedNotRemoved(mcb) .map(this::registerEntityForChanges) - .forEach(MapRealmEntity::removeExpiredClientInitialAccesses); + .forEach(MapRealmEntity::removeExpiredClientInitialAccesses); } //TODO move the following method to adapter diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java index f50f631f78..525a334713 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java @@ -18,27 +18,23 @@ package org.keycloak.models.map.realm; import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.RealmProvider; import org.keycloak.models.RealmProviderFactory; -import org.keycloak.models.map.storage.MapStorageProvider; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProviderFactory; -public class MapRealmProviderFactory extends AbstractMapProviderFactory implements RealmProviderFactory { +public class MapRealmProviderFactory extends AbstractMapProviderFactory, RealmModel> implements RealmProviderFactory { - //for now we have to support String ids - private MapStorage store; - - @Override - public void postInit(KeycloakSessionFactory factory) { - MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("realm", String.class, MapRealmEntity.class, RealmModel.class); + public MapRealmProviderFactory() { + super(MapRealmEntity.class, RealmModel.class); } @Override public RealmProvider create(KeycloakSession session) { - return new MapRealmProvider(session, store); + return new MapRealmProvider<>(session, getStorage(session)); } + + @Override + public String getHelpText() { + return "Realm provider"; + } } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/AbstractRoleEntity.java b/model/map/src/main/java/org/keycloak/models/map/role/AbstractRoleEntity.java deleted file mode 100644 index 310fe27e7b..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/role/AbstractRoleEntity.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2020 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.models.map.role; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import org.keycloak.models.map.common.AbstractEntity; - -public abstract class AbstractRoleEntity implements AbstractEntity { - - private K id; - private String realmId; - - private String name; - private String description; - private boolean clientRole; - private String clientId; - private Set compositeRoles = new HashSet<>(); - private Map> attributes = new HashMap<>(); - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected AbstractRoleEntity() { - this.id = null; - this.realmId = null; - } - - public AbstractRoleEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated |= ! Objects.equals(this.description, description); - this.description = description; - } - - public Map> getAttributes() { - return attributes; - } - - public void setAttributes(Map> attributes) { - this.updated |= ! Objects.equals(this.attributes, attributes); - this.attributes = attributes; - } - - public void setAttribute(String name, List values) { - this.updated |= ! Objects.equals(this.attributes.put(name, values), values); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - public String getRealmId() { - return realmId; - } - - public void setRealmId(String realmId) { - this.updated |= ! Objects.equals(this.realmId, realmId); - this.realmId = realmId; - } - - public boolean isClientRole() { - return clientRole; - } - - public void setClientRole(boolean clientRole) { - this.updated |= ! Objects.equals(this.clientRole, clientRole); - this.clientRole = clientRole; - } - - public boolean isComposite() { - return ! (compositeRoles == null || compositeRoles.isEmpty()); - } - - public Set getCompositeRoles() { - return compositeRoles; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.updated |= ! Objects.equals(this.clientId, clientId); - this.clientId = clientId; - } - - public void setCompositeRoles(Set compositeRoles) { - this.updated |= ! Objects.equals(this.compositeRoles, compositeRoles); - this.compositeRoles.clear(); - this.compositeRoles.addAll(compositeRoles); - } - - public void addCompositeRole(String roleId) { - this.updated |= this.compositeRoles.add(roleId); - } - - public void removeCompositeRole(String roleId) { - this.updated |= this.compositeRoles.remove(roleId); - } -} diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleAdapter.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleAdapter.java index 17930961ae..3769aace87 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleAdapter.java @@ -30,11 +30,11 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapRoleAdapter extends AbstractRoleModel implements RoleModel { +public abstract class MapRoleAdapter extends AbstractRoleModel> implements RoleModel { private static final Logger LOG = Logger.getLogger(MapRoleAdapter.class); - public MapRoleAdapter(KeycloakSession session, RealmModel realm, MapRoleEntity entity) { + public MapRoleAdapter(KeycloakSession session, RealmModel realm, MapRoleEntity entity) { super(session, realm, entity); } @@ -53,11 +53,6 @@ public class MapRoleAdapter extends AbstractRoleModel implements entity.setDescription(description); } - @Override - public String getId() { - return entity.getId().toString(); - } - @Override public void setName(String name) { entity.setName(name); diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java index 3ee414f807..536771a9e6 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java @@ -1,13 +1,13 @@ /* * Copyright 2020 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,16 +16,135 @@ */ package org.keycloak.models.map.role; -import java.util.UUID; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.keycloak.models.map.common.AbstractEntity; -public class MapRoleEntity extends AbstractRoleEntity { +public class MapRoleEntity implements AbstractEntity { + + private K id; + private String realmId; + + private String name; + private String description; + private boolean clientRole; + private String clientId; + private Set compositeRoles = new HashSet<>(); + private Map> attributes = new HashMap<>(); + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; protected MapRoleEntity() { - super(); + this.id = null; + this.realmId = null; } - public MapRoleEntity(UUID id, String realmId) { - super(id, realmId); + public MapRoleEntity(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; } + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.updated |= ! Objects.equals(this.description, description); + this.description = description; + } + + public Map> getAttributes() { + return attributes; + } + + public void setAttributes(Map> attributes) { + this.updated |= ! Objects.equals(this.attributes, attributes); + this.attributes = attributes; + } + + public void setAttribute(String name, List values) { + this.updated |= ! Objects.equals(this.attributes.put(name, values), values); + } + + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + public String getRealmId() { + return realmId; + } + + public void setRealmId(String realmId) { + this.updated |= ! Objects.equals(this.realmId, realmId); + this.realmId = realmId; + } + + public boolean isClientRole() { + return clientRole; + } + + public void setClientRole(boolean clientRole) { + this.updated |= ! Objects.equals(this.clientRole, clientRole); + this.clientRole = clientRole; + } + + public boolean isComposite() { + return ! (compositeRoles == null || compositeRoles.isEmpty()); + } + + public Set getCompositeRoles() { + return compositeRoles; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.updated |= ! Objects.equals(this.clientId, clientId); + this.clientId = clientId; + } + + public void setCompositeRoles(Set compositeRoles) { + this.updated |= ! Objects.equals(this.compositeRoles, compositeRoles); + this.compositeRoles.clear(); + this.compositeRoles.addAll(compositeRoles); + } + + public void addCompositeRole(String roleId) { + this.updated |= this.compositeRoles.add(roleId); + } + + public void removeCompositeRole(String roleId) { + this.updated |= this.compositeRoles.remove(roleId); + } } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java index 457ce30ad3..43a33c3d50 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java @@ -28,7 +28,6 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.common.Serialization; import java.util.Comparator; import java.util.Objects; -import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -43,16 +42,16 @@ import org.keycloak.models.map.common.StreamUtils; import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; -public class MapRoleProvider implements RoleProvider { +public class MapRoleProvider implements RoleProvider { private static final Logger LOG = Logger.getLogger(MapRoleProvider.class); private final KeycloakSession session; - final MapKeycloakTransaction tx; - private final MapStorage roleStore; + final MapKeycloakTransaction, RoleModel> tx; + private final MapStorage, RoleModel> roleStore; - private static final Comparator COMPARE_BY_NAME = new Comparator() { + private static final Comparator> COMPARE_BY_NAME = new Comparator>() { @Override - public int compare(MapRoleEntity o1, MapRoleEntity o2) { + public int compare(MapRoleEntity o1, MapRoleEntity o2) { String r1 = o1 == null ? null : o1.getName(); String r2 = o2 == null ? null : o2.getName(); return r1 == r2 ? 0 @@ -63,22 +62,27 @@ public class MapRoleProvider implements RoleProvider { } }; - public MapRoleProvider(KeycloakSession session, MapStorage roleStore) { + public MapRoleProvider(KeycloakSession session, MapStorage, RoleModel> roleStore) { this.session = session; this.roleStore = roleStore; this.tx = roleStore.createTransaction(session); session.getTransactionManager().enlist(tx); } - private Function entityToAdapterFunc(RealmModel realm) { + private Function, RoleModel> entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapRoleAdapter(session, realm, registerEntityForChanges(origEntity)); + return origEntity -> new MapRoleAdapter(session, realm, registerEntityForChanges(origEntity)) { + @Override + public String getId() { + return roleStore.getKeyConvertor().keyToString(entity.getId()); + } + }; } - private MapRoleEntity registerEntityForChanges(MapRoleEntity origEntity) { - final MapRoleEntity res = Serialization.from(origEntity); - tx.updateIfChanged(origEntity.getId(), res, MapRoleEntity::isUpdated); + private MapRoleEntity registerEntityForChanges(MapRoleEntity origEntity) { + final MapRoleEntity res = Serialization.from(origEntity); + tx.updateIfChanged(origEntity.getId(), res, MapRoleEntity::isUpdated); return res; } @@ -88,11 +92,11 @@ public class MapRoleProvider implements RoleProvider { throw new ModelDuplicateException("Role exists: " + id); } - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? roleStore.getKeyConvertor().yieldNewUniqueKey() : roleStore.getKeyConvertor().fromString(id); LOG.tracef("addRealmRole(%s, %s, %s)%s", realm, id, name, getShortStackTrace()); - MapRoleEntity entity = new MapRoleEntity(entityId, realm.getId()); + MapRoleEntity entity = new MapRoleEntity(entityId, realm.getId()); entity.setName(name); entity.setRealmId(realm.getId()); if (tx.read(entity.getId()) != null) { @@ -124,11 +128,11 @@ public class MapRoleProvider implements RoleProvider { throw new ModelDuplicateException("Role exists: " + id); } - final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); + final K entityId = id == null ? roleStore.getKeyConvertor().yieldNewUniqueKey() : roleStore.getKeyConvertor().fromString(id); LOG.tracef("addClientRole(%s, %s, %s)%s", client, id, name, getShortStackTrace()); - MapRoleEntity entity = new MapRoleEntity(entityId, client.getRealm().getId()); + MapRoleEntity entity = new MapRoleEntity(entityId, client.getRealm().getId()); entity.setName(name); entity.setClientRole(true); entity.setClientId(client.getId()); @@ -168,13 +172,13 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false); //remove role from realm-roles composites - try (Stream baseStream = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> baseStream = tx.getUpdatedNotRemoved(mcb)) { - StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) + StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) .filter(pair -> role.getId().equals(pair.getV())) .collect(Collectors.toSet()) .forEach(pair -> { - MapRoleEntity origEntity = pair.getK(); + MapRoleEntity origEntity = pair.getK(); // // TODO: Investigate what this is for - the return value is ignored @@ -192,13 +196,13 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId()) .compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false); - try (Stream baseStream = tx.getUpdatedNotRemoved(mcbClient)) { + try (Stream> baseStream = tx.getUpdatedNotRemoved(mcbClient)) { - StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) + StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) .filter(pair -> role.getId().equals(pair.getV())) .collect(Collectors.toSet()) .forEach(pair -> { - MapRoleEntity origEntity = pair.getK(); + MapRoleEntity origEntity = pair.getK(); // // TODO: Investigate what this is for - the return value is ignored @@ -223,7 +227,7 @@ public class MapRoleProvider implements RoleProvider { }); // TODO: ^^^^^^^ Up to here - tx.delete(UUID.fromString(role.getId())); + tx.delete(roleStore.getKeyConvertor().fromString(role.getId())); return true; } @@ -287,7 +291,7 @@ public class MapRoleProvider implements RoleProvider { LOG.tracef("getRoleById(%s, %s)%s", realm, id, getShortStackTrace()); - MapRoleEntity entity = tx.read(UUID.fromString(id)); + MapRoleEntity entity = tx.read(roleStore.getKeyConvertor().fromStringSafe(id)); String realmId = realm.getId(); return (entity == null || ! Objects.equals(realmId, entity.getRealmId())) ? null @@ -306,7 +310,7 @@ public class MapRoleProvider implements RoleProvider { roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%") ); - Stream s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.getUpdatedNotRemoved(mcb) .sorted(COMPARE_BY_NAME); return paginatedStream(s.map(entityToAdapterFunc(realm)), first, max); @@ -324,7 +328,7 @@ public class MapRoleProvider implements RoleProvider { roleStore.createCriteriaBuilder().compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"), roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%") ); - Stream s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.getUpdatedNotRemoved(mcb) .sorted(COMPARE_BY_NAME); return paginatedStream(s,first, max).map(entityToAdapterFunc(client.getRealm())); diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java index 84ec8cf09b..988ff305b9 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java @@ -16,30 +16,25 @@ */ package org.keycloak.models.map.role; -import java.util.UUID; import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RoleModel; import org.keycloak.models.RoleProvider; import org.keycloak.models.RoleProviderFactory; -import org.keycloak.models.map.storage.MapStorageProvider; -import org.keycloak.models.map.storage.MapStorage; -import org.keycloak.models.map.storage.MapStorageProviderFactory; -public class MapRoleProviderFactory extends AbstractMapProviderFactory implements RoleProviderFactory { +public class MapRoleProviderFactory extends AbstractMapProviderFactory, RoleModel> implements RoleProviderFactory { - private MapStorage store; - - @Override - public void postInit(KeycloakSessionFactory factory) { - MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class); - this.store = sp.getStorage("roles", UUID.class, MapRoleEntity.class, RoleModel.class); + public MapRoleProviderFactory() { + super(MapRoleEntity.class, RoleModel.class); } - @Override public RoleProvider create(KeycloakSession session) { - return new MapRoleProvider(session, store); + return new MapRoleProvider<>(session, getStorage(session)); + } + + @Override + public String getHelpText() { + return "Role provider"; } } diff --git a/model/map/src/main/java/org/keycloak/models/map/serverinfo/MapServerInfoProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/serverinfo/MapServerInfoProviderFactory.java index c93516e78c..276761d027 100644 --- a/model/map/src/main/java/org/keycloak/models/map/serverinfo/MapServerInfoProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/serverinfo/MapServerInfoProviderFactory.java @@ -27,14 +27,21 @@ import org.keycloak.common.util.RandomString; import org.keycloak.migration.MigrationModel; import org.keycloak.migration.ModelVersion; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.ServerInfoProvider; import org.keycloak.models.ServerInfoProviderFactory; -import org.keycloak.models.map.common.AbstractMapProviderFactory; -public class MapServerInfoProviderFactory extends AbstractMapProviderFactory implements ServerInfoProviderFactory { +public class MapServerInfoProviderFactory implements ServerInfoProviderFactory { + + public static final String PROVIDER_ID = "map"; private static final String RESOURCES_VERSION_SEED = "resourcesVersionSeed"; + @Override + public ServerInfoProvider create(KeycloakSession session) { + return INSTANCE; + } + @Override public void init(Config.Scope config) { String seed = config.get(RESOURCES_VERSION_SEED); @@ -53,8 +60,16 @@ public class MapServerInfoProviderFactory extends AbstractMapProviderFactory, UpdatePredicatesFunc, RealmModel>> REALM_PREDICATES = basePredicates(RealmModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, ClientModel>> CLIENT_PREDICATES = basePredicates(ClientModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, ClientScopeModel>> CLIENT_SCOPE_PREDICATES = basePredicates(ClientScopeModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, GroupModel>> GROUP_PREDICATES = basePredicates(GroupModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, RoleModel>> ROLE_PREDICATES = basePredicates(RoleModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, UserModel>> USER_PREDICATES = basePredicates(UserModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, RootAuthenticationSessionModel>> AUTHENTICATION_SESSION_PREDICATES = basePredicates(RootAuthenticationSessionModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, ResourceServer>> AUTHZ_RESOURCE_SERVER_PREDICATES = basePredicates(ResourceServer.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, Resource>> AUTHZ_RESOURCE_PREDICATES = basePredicates(Resource.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, Scope>> AUTHZ_SCOPE_PREDICATES = basePredicates(Scope.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, PermissionTicket>> AUTHZ_PERMISSION_TICKET_PREDICATES = basePredicates(PermissionTicket.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, Policy>> AUTHZ_POLICY_PREDICATES = basePredicates(Policy.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, UserSessionModel>> USER_SESSION_PREDICATES = basePredicates(UserSessionModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, AuthenticatedClientSessionModel>> CLIENT_SESSION_PREDICATES = basePredicates(AuthenticatedClientSessionModel.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc, UserLoginFailureModel>> USER_LOGIN_FAILURE_PREDICATES = basePredicates(UserLoginFailureModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, AuthenticatedClientSessionModel>> CLIENT_SESSION_PREDICATES = basePredicates(AuthenticatedClientSessionModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, ClientModel>> CLIENT_PREDICATES = basePredicates(ClientModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, ClientScopeModel>> CLIENT_SCOPE_PREDICATES = basePredicates(ClientScopeModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, GroupModel>> GROUP_PREDICATES = basePredicates(GroupModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, RoleModel>> ROLE_PREDICATES = basePredicates(RoleModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, RootAuthenticationSessionModel>> AUTHENTICATION_SESSION_PREDICATES = basePredicates(RootAuthenticationSessionModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, RealmModel>> REALM_PREDICATES = basePredicates(RealmModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, ResourceServer>> AUTHZ_RESOURCE_SERVER_PREDICATES = basePredicates(ResourceServer.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, Resource>> AUTHZ_RESOURCE_PREDICATES = basePredicates(Resource.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, Scope>> AUTHZ_SCOPE_PREDICATES = basePredicates(Scope.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, PermissionTicket>> AUTHZ_PERMISSION_TICKET_PREDICATES = basePredicates(PermissionTicket.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, Policy>> AUTHZ_POLICY_PREDICATES = basePredicates(Policy.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, UserLoginFailureModel>> USER_LOGIN_FAILURE_PREDICATES = basePredicates(UserLoginFailureModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, UserModel>> USER_PREDICATES = basePredicates(UserModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc, UserSessionModel>> USER_SESSION_PREDICATES = basePredicates(UserSessionModel.SearchableFields.ID); @SuppressWarnings("unchecked") private static final Map, Map> PREDICATES = new HashMap<>(); static { - put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, AbstractRealmEntity::getName); + put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, MapRealmEntity::getName); put(REALM_PREDICATES, RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, MapFieldPredicates::checkRealmsWithClientInitialAccess); put(REALM_PREDICATES, RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, MapFieldPredicates::checkRealmsWithComponentType); - put(CLIENT_PREDICATES, ClientModel.SearchableFields.REALM_ID, AbstractClientEntity::getRealmId); - put(CLIENT_PREDICATES, ClientModel.SearchableFields.CLIENT_ID, AbstractClientEntity::getClientId); + put(CLIENT_PREDICATES, ClientModel.SearchableFields.REALM_ID, MapClientEntity::getRealmId); + put(CLIENT_PREDICATES, ClientModel.SearchableFields.CLIENT_ID, MapClientEntity::getClientId); put(CLIENT_PREDICATES, ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, MapFieldPredicates::checkScopeMappingRole); - put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.REALM_ID, AbstractClientScopeEntity::getRealmId); - put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.NAME, AbstractClientScopeEntity::getName); + put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.REALM_ID, MapClientScopeEntity::getRealmId); + put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.NAME, MapClientScopeEntity::getName); - put(GROUP_PREDICATES, GroupModel.SearchableFields.REALM_ID, AbstractGroupEntity::getRealmId); - put(GROUP_PREDICATES, GroupModel.SearchableFields.NAME, AbstractGroupEntity::getName); - put(GROUP_PREDICATES, GroupModel.SearchableFields.PARENT_ID, AbstractGroupEntity::getParentId); + put(GROUP_PREDICATES, GroupModel.SearchableFields.REALM_ID, MapGroupEntity::getRealmId); + put(GROUP_PREDICATES, GroupModel.SearchableFields.NAME, MapGroupEntity::getName); + put(GROUP_PREDICATES, GroupModel.SearchableFields.PARENT_ID, MapGroupEntity::getParentId); put(GROUP_PREDICATES, GroupModel.SearchableFields.ASSIGNED_ROLE, MapFieldPredicates::checkGrantedGroupRole); - put(ROLE_PREDICATES, RoleModel.SearchableFields.REALM_ID, AbstractRoleEntity::getRealmId); - put(ROLE_PREDICATES, RoleModel.SearchableFields.CLIENT_ID, AbstractRoleEntity::getClientId); - put(ROLE_PREDICATES, RoleModel.SearchableFields.DESCRIPTION, AbstractRoleEntity::getDescription); - put(ROLE_PREDICATES, RoleModel.SearchableFields.NAME, AbstractRoleEntity::getName); - put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_CLIENT_ROLE, AbstractRoleEntity::isClientRole); - put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_COMPOSITE_ROLE, AbstractRoleEntity::isComposite); + put(ROLE_PREDICATES, RoleModel.SearchableFields.REALM_ID, MapRoleEntity::getRealmId); + put(ROLE_PREDICATES, RoleModel.SearchableFields.CLIENT_ID, MapRoleEntity::getClientId); + put(ROLE_PREDICATES, RoleModel.SearchableFields.DESCRIPTION, MapRoleEntity::getDescription); + put(ROLE_PREDICATES, RoleModel.SearchableFields.NAME, MapRoleEntity::getName); + put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_CLIENT_ROLE, MapRoleEntity::isClientRole); + put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_COMPOSITE_ROLE, MapRoleEntity::isComposite); - put(USER_PREDICATES, UserModel.SearchableFields.REALM_ID, AbstractUserEntity::getRealmId); - put(USER_PREDICATES, UserModel.SearchableFields.USERNAME, AbstractUserEntity::getUsername); - put(USER_PREDICATES, UserModel.SearchableFields.FIRST_NAME, AbstractUserEntity::getFirstName); - put(USER_PREDICATES, UserModel.SearchableFields.LAST_NAME, AbstractUserEntity::getLastName); - put(USER_PREDICATES, UserModel.SearchableFields.EMAIL, AbstractUserEntity::getEmail); - put(USER_PREDICATES, UserModel.SearchableFields.ENABLED, AbstractUserEntity::isEnabled); - put(USER_PREDICATES, UserModel.SearchableFields.EMAIL_VERIFIED, AbstractUserEntity::isEmailVerified); - put(USER_PREDICATES, UserModel.SearchableFields.FEDERATION_LINK, AbstractUserEntity::getFederationLink); + put(USER_PREDICATES, UserModel.SearchableFields.REALM_ID, MapUserEntity::getRealmId); + put(USER_PREDICATES, UserModel.SearchableFields.USERNAME, MapUserEntity::getUsername); + put(USER_PREDICATES, UserModel.SearchableFields.FIRST_NAME, MapUserEntity::getFirstName); + put(USER_PREDICATES, UserModel.SearchableFields.LAST_NAME, MapUserEntity::getLastName); + put(USER_PREDICATES, UserModel.SearchableFields.EMAIL, MapUserEntity::getEmail); + put(USER_PREDICATES, UserModel.SearchableFields.ENABLED, MapUserEntity::isEnabled); + put(USER_PREDICATES, UserModel.SearchableFields.EMAIL_VERIFIED, MapUserEntity::isEmailVerified); + put(USER_PREDICATES, UserModel.SearchableFields.FEDERATION_LINK, MapUserEntity::getFederationLink); put(USER_PREDICATES, UserModel.SearchableFields.ATTRIBUTE, MapFieldPredicates::checkUserAttributes); put(USER_PREDICATES, UserModel.SearchableFields.IDP_AND_USER, MapFieldPredicates::getUserIdpAliasAtIdentityProviderPredicate); put(USER_PREDICATES, UserModel.SearchableFields.ASSIGNED_ROLE, MapFieldPredicates::checkGrantedUserRole); @@ -127,62 +127,62 @@ public class MapFieldPredicates { put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_FOR_CLIENT, MapFieldPredicates::checkUserClientConsent); put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_WITH_CLIENT_SCOPE, MapFieldPredicates::checkUserConsentsWithClientScope); put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, MapFieldPredicates::getUserConsentClientFederationLink); - put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, AbstractUserEntity::getServiceAccountClientLink); + put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, MapUserEntity::getServiceAccountClientLink); - put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, AbstractRootAuthenticationSessionEntity::getRealmId); - put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, AbstractRootAuthenticationSessionEntity::getTimestamp); + put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, MapRootAuthenticationSessionEntity::getRealmId); + put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, MapRootAuthenticationSessionEntity::getTimestamp); - put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, AbstractResourceServerEntity::getId); + put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, MapResourceServerEntity::getId); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(AbstractResourceEntity::getId)); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, AbstractResourceEntity::getName); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.RESOURCE_SERVER_ID, AbstractResourceEntity::getResourceServerId); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER, AbstractResourceEntity::getOwner); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.TYPE, AbstractResourceEntity::getType); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(MapResourceEntity::getId)); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, MapResourceEntity::getName); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.RESOURCE_SERVER_ID, MapResourceEntity::getResourceServerId); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER, MapResourceEntity::getOwner); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.TYPE, MapResourceEntity::getType); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.URI, MapFieldPredicates::checkResourceUri); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.SCOPE_ID, MapFieldPredicates::checkResourceScopes); - put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, AbstractResourceEntity::isOwnerManagedAccess); + put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, MapResourceEntity::isOwnerManagedAccess); - put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(AbstractScopeEntity::getId)); - put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, AbstractScopeEntity::getResourceServerId); - put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, AbstractScopeEntity::getName); + put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(MapScopeEntity::getId)); + put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, MapScopeEntity::getResourceServerId); + put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, MapScopeEntity::getName); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(AbstractPermissionTicketEntity::getId)); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, AbstractPermissionTicketEntity::getOwner); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REQUESTER, AbstractPermissionTicketEntity::getRequester); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_SERVER_ID, AbstractPermissionTicketEntity::getResourceServerId); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getResourceId)); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getScopeId)); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(AbstractPermissionTicketEntity::getPolicyId)); - put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, AbstractPermissionTicketEntity::getGrantedTimestamp); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(MapPermissionTicketEntity::getId)); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, MapPermissionTicketEntity::getOwner); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REQUESTER, MapPermissionTicketEntity::getRequester); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_SERVER_ID, MapPermissionTicketEntity::getResourceServerId); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_ID, predicateForKeyField(MapPermissionTicketEntity::getResourceId)); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(MapPermissionTicketEntity::getScopeId)); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(MapPermissionTicketEntity::getPolicyId)); + put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, MapPermissionTicketEntity::getGrantedTimestamp); - put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(AbstractPolicyEntity::getId)); - put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, AbstractPolicyEntity::getName); - put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, AbstractPolicyEntity::getOwner); - put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, AbstractPolicyEntity::getType); - put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, AbstractPolicyEntity::getResourceServerId); + put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(MapPolicyEntity::getId)); + put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, MapPolicyEntity::getName); + put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, MapPolicyEntity::getOwner); + put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, MapPolicyEntity::getType); + put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, MapPolicyEntity::getResourceServerId); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ASSOCIATED_POLICY_ID, MapFieldPredicates::checkAssociatedPolicy); put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, use -> use.getNote(CORRESPONDING_SESSION_ID)); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.REALM_ID, AbstractUserSessionEntity::getRealmId); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.USER_ID, AbstractUserSessionEntity::getUserId); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.REALM_ID, MapUserSessionEntity::getRealmId); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.USER_ID, MapUserSessionEntity::getUserId); put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.CLIENT_ID, MapFieldPredicates::checkUserSessionContainsAuthenticatedClientSession); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_SESSION_ID, AbstractUserSessionEntity::getBrokerSessionId); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_USER_ID, AbstractUserSessionEntity::getBrokerUserId); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.IS_OFFLINE, AbstractUserSessionEntity::isOffline); - put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.LAST_SESSION_REFRESH, AbstractUserSessionEntity::getLastSessionRefresh); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_SESSION_ID, MapUserSessionEntity::getBrokerSessionId); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_USER_ID, MapUserSessionEntity::getBrokerUserId); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.IS_OFFLINE, MapUserSessionEntity::isOffline); + put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.LAST_SESSION_REFRESH, MapUserSessionEntity::getLastSessionRefresh); - put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.REALM_ID, AbstractAuthenticatedClientSessionEntity::getRealmId); - put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, AbstractAuthenticatedClientSessionEntity::getClientId); - put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, AbstractAuthenticatedClientSessionEntity::getUserSessionId); - put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, AbstractAuthenticatedClientSessionEntity::isOffline); - put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.TIMESTAMP, AbstractAuthenticatedClientSessionEntity::getTimestamp); + put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.REALM_ID, MapAuthenticatedClientSessionEntity::getRealmId); + put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, MapAuthenticatedClientSessionEntity::getClientId); + put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, MapAuthenticatedClientSessionEntity::getUserSessionId); + put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, MapAuthenticatedClientSessionEntity::isOffline); + put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.TIMESTAMP, MapAuthenticatedClientSessionEntity::getTimestamp); - put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.REALM_ID, AbstractUserLoginFailureEntity::getRealmId); - put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.USER_ID, AbstractUserLoginFailureEntity::getUserId); + put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.REALM_ID, MapUserLoginFailureEntity::getRealmId); + put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.USER_ID, MapUserLoginFailureEntity::getUserId); } static { @@ -242,30 +242,30 @@ public class MapFieldPredicates { return expectedType.cast(ob); } - private static MapModelCriteriaBuilder, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder, ClientModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder, ClientModel> mcb, Operator op, Object[] values) { String roleIdS = ensureEqSingleValue(ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, "role_id", op, values); - Function, ?> getter; + Function, ?> getter; getter = ce -> ce.getScopeMappings().contains(roleIdS); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder, GroupModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder, GroupModel> mcb, Operator op, Object[] values) { String roleIdS = ensureEqSingleValue(GroupModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values); - Function, ?> getter; + Function, ?> getter; getter = ge -> ge.getGrantedRoles().contains(roleIdS); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> getUserConsentClientFederationLink(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> getUserConsentClientFederationLink(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { String providerId = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, "provider_id", op, values); String providerIdS = new StorageId((String) providerId, "").getId(); - Function, ?> getter; + Function, ?> getter; getter = ue -> ue.getUserConsents().map(UserConsentEntity::getClientId).anyMatch(v -> v != null && v.startsWith(providerIdS)); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> checkUserAttributes(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> checkUserAttributes(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { if (values == null || values.length <= 1) { throw new CriterionNotSupportedException(UserModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (attribute_name, ...), got: " + Arrays.toString(values)); } @@ -275,7 +275,7 @@ public class MapFieldPredicates { throw new CriterionNotSupportedException(UserModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (String attribute_name), got: " + Arrays.toString(values)); } String attrNameS = (String) attrName; - Function, ?> getter; + Function, ?> getter; Object[] realValues = new Object[values.length - 1]; System.arraycopy(values, 1, realValues, 0, values.length - 1); Predicate valueComparator = CriteriaOperator.predicateFor(op, realValues); @@ -287,16 +287,16 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { String roleIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values); - Function, ?> getter; + Function, ?> getter; getter = ue -> ue.getRolesMembership().contains(roleIdS); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Resource> checkResourceUri(MapModelCriteriaBuilder, Resource> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Resource> checkResourceUri(MapModelCriteriaBuilder, Resource> mcb, Operator op, Object[] values) { + Function, ?> getter; if (Operator.EXISTS.equals(op)) { getter = re -> re.getUris() != null && !re.getUris().isEmpty(); @@ -311,8 +311,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Resource> checkResourceScopes(MapModelCriteriaBuilder, Resource> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Resource> checkResourceScopes(MapModelCriteriaBuilder, Resource> mcb, Operator op, Object[] values) { + Function, ?> getter; if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) { Collection c = (Collection) values[0]; @@ -325,8 +325,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Policy> checkPolicyResources(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Policy> checkPolicyResources(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { + Function, ?> getter; if (op == Operator.NOT_EXISTS) { getter = re -> re.getResourceIds().isEmpty(); @@ -341,8 +341,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Policy> checkPolicyScopes(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Policy> checkPolicyScopes(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { + Function, ?> getter; if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) { Collection c = (Collection) values[0]; @@ -355,8 +355,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Policy> checkPolicyConfig(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Policy> checkPolicyConfig(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { + Function, ?> getter; final Object attrName = values[0]; if (!(attrName instanceof String)) { @@ -375,8 +375,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, Policy> checkAssociatedPolicy(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, Policy> checkAssociatedPolicy(MapModelCriteriaBuilder, Policy> mcb, Operator op, Object[] values) { + Function, ?> getter; if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) { Collection c = (Collection) values[0]; @@ -389,8 +389,8 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> checkUserGroup(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { - Function, ?> getter; + private static MapModelCriteriaBuilder, UserModel> checkUserGroup(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + Function, ?> getter; if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) { Collection c = (Collection) values[0]; getter = ue -> ue.getGroupsMembership().stream().anyMatch(c::contains); @@ -402,23 +402,23 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> checkUserClientConsent(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> checkUserClientConsent(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { String clientIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_id", op, values); - Function, ?> getter; + Function, ?> getter; getter = ue -> ue.getUserConsent(clientIdS); return mcb.fieldCompare(Operator.EXISTS, getter, null); } - private static MapModelCriteriaBuilder, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { String clientScopeIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_scope_id", op, values); - Function, ?> getter; + Function, ?> getter; getter = ue -> ue.getUserConsents().anyMatch(consent -> consent.getGrantedClientScopesIds().contains(clientScopeIdS)); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserModel> getUserIdpAliasAtIdentityProviderPredicate(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserModel> getUserIdpAliasAtIdentityProviderPredicate(MapModelCriteriaBuilder, UserModel> mcb, Operator op, Object[] values) { if (op != Operator.EQ) { throw new CriterionNotSupportedException(UserModel.SearchableFields.IDP_AND_USER, op); } @@ -427,7 +427,7 @@ public class MapFieldPredicates { } final Object idpAlias = values[0]; - Function, ?> getter; + Function, ?> getter; if (values.length == 1) { getter = ue -> ue.getFederatedIdentities() .anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider())); @@ -444,25 +444,23 @@ public class MapFieldPredicates { return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, RealmModel> checkRealmsWithClientInitialAccess(MapModelCriteriaBuilder, RealmModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, RealmModel> checkRealmsWithClientInitialAccess(MapModelCriteriaBuilder, RealmModel> mcb, Operator op, Object[] values) { if (op != Operator.EXISTS) { throw new CriterionNotSupportedException(RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, op); } - Function, ?> getter = AbstractRealmEntity::hasClientInitialAccess; + Function, ?> getter = MapRealmEntity::hasClientInitialAccess; return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, RealmModel> checkRealmsWithComponentType(MapModelCriteriaBuilder, RealmModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, RealmModel> checkRealmsWithComponentType(MapModelCriteriaBuilder, RealmModel> mcb, Operator op, Object[] values) { String providerType = ensureEqSingleValue(RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, "component_provider_type", op, values); - Function, ?> getter = realmEntity -> realmEntity.getComponents().anyMatch(component -> component.getProviderType().equals(providerType)); + Function, ?> getter = realmEntity -> realmEntity.getComponents().anyMatch(component -> component.getProviderType().equals(providerType)); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } - private static MapModelCriteriaBuilder, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder, UserSessionModel> mcb, Operator op, Object[] values) { + private static MapModelCriteriaBuilder, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder, UserSessionModel> mcb, Operator op, Object[] values) { String clientId = ensureEqSingleValue(UserSessionModel.SearchableFields.CLIENT_ID, "client_id", op, values); - Function, ?> getter; - getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId)); - + Function, ?> getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId)); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java index 52f22a6a4f..f4954b2d49 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java @@ -54,7 +54,7 @@ public class MapKeycloakTransaction, M> implement @Override public void commit() { - log.trace("Commit"); + log.tracef("Commit - %s", map); if (rollback) { throw new RuntimeException("Rollback only!"); @@ -97,7 +97,11 @@ public class MapKeycloakTransaction, M> implement // This is for possibility to lookup for session by id, which was created in this transaction public V read(K key) { - return read(key, map::read); + try { // TODO: Consider using Optional rather than handling NPE + return read(key, map::read); + } catch (NullPointerException ex) { + return null; + } } public V read(K key, Function defaultValueFunc) { @@ -127,8 +131,6 @@ public class MapKeycloakTransaction, M> implement * Returns the stream of records that match given criteria and includes changes made in this transaction, i.e. * the result contains updates and excludes records that have been deleted in this transaction. * - * Note that returned stream might not reflect on the bulk delete. This is known limitation that can be fixed if necessary. - * * @param mcb * @return */ diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorage.java index d571b5ceef..80e4462825 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorage.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorage.java @@ -49,6 +49,8 @@ public interface MapStorage, M> { /** * Returns object with the given {@code key} from the storage or {@code null} if object does not exist. + *
+ * TODO: Consider returning {@code Optional} instead. * @param key Key of the object. Must not be {@code null}. * @return See description * @throws NullPointerException if the {@code key} is {@code null} @@ -115,7 +117,7 @@ public interface MapStorage, M> { * If possible, do not delay filtering after the models are reconstructed from * storage entities, in most cases this would be highly inefficient. * - * @return See description + * @return See description. Never returns {@code null} */ ModelCriteriaBuilder createCriteriaBuilder(); @@ -126,8 +128,16 @@ public interface MapStorage, M> { * shared same across storages accessing the same database within the same session; in other cases * (e.g. plain map) a separate transaction handler might be created per each storage. * - * @return See description. + * @return See description. Never returns {@code null} */ public MapKeycloakTransaction createTransaction(KeycloakSession session); + /** + * Returns a {@link StringKeyConvertor} that is used to convert primary keys + * from {@link String} to internal representation and vice versa. + * + * @return See above. Never returns {@code null}. + */ + public StringKeyConvertor getKeyConvertor(); + } diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProvider.java b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProvider.java index 4bc922a4f8..cb5f4b92c5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProvider.java @@ -27,12 +27,13 @@ import org.keycloak.provider.Provider; public interface MapStorageProvider extends Provider { /** - * Returns a key-value storage + * Returns a key-value storage implementation for the particular types. * @param type of the primary key * @param type of the value * @param name Name of the storage * @param flags * @return + * @throws IllegalArgumentException If some of the types is not supported by the underlying implementation. */ - , M> MapStorage getStorage(String name, Class keyType, Class valueType, Class modelType, Flag... flags); + , M> MapStorage getStorage(Class valueType, Class modelType, Flag... flags); } diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProviderFactory.java index 797ad6e42e..0cb164011c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/MapStorageProviderFactory.java @@ -16,27 +16,17 @@ */ package org.keycloak.models.map.storage; -import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.component.ComponentFactory; import org.keycloak.provider.ProviderFactory; /** * * @author hmlnarik */ -public interface MapStorageProviderFactory extends ProviderFactory { +public interface MapStorageProviderFactory extends ProviderFactory, ComponentFactory { public enum Flag { INITIALIZE_EMPTY, LOCAL } - - /** - * Returns a key-value storage - * @param type of the primary key - * @param type of the value - * @param name Name of the storage - * @param flags - * @return - */ - , M> MapStorage getStorage(String name, Class keyType, Class valueType, Class modelType, Flag... flags); } diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/ModelCriteriaBuilder.java b/model/map/src/main/java/org/keycloak/models/map/storage/ModelCriteriaBuilder.java index 7229a69597..d1fc31cf7c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/ModelCriteriaBuilder.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/ModelCriteriaBuilder.java @@ -142,7 +142,7 @@ public interface ModelCriteriaBuilder { * Creates and returns a new instance of {@code ModelCriteriaBuilder} that * combines the given builders with the Boolean OR operator. *

- * Predicate coming out of {@code and} on an empty array of {@code builders} + * Predicate coming out of {@code or} on an empty array of {@code builders} * (i.e. empty disjunction) is always {@code false}. * *

diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/StringKeyConvertor.java b/model/map/src/main/java/org/keycloak/models/map/storage/StringKeyConvertor.java
new file mode 100644
index 0000000000..c5e98b2445
--- /dev/null
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/StringKeyConvertor.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.map.storage;
+
+import java.security.SecureRandom;
+import java.util.UUID;
+
+/**
+ *  Converts given storage key from and to {@code String} representation.
+ *
+ *  @author hmlnarik
+ */
+public interface StringKeyConvertor {
+
+    /**
+     * Returns String representation of the key from native representation
+     * @param key 
+     * @throws IllegalArgumentException if the string format is not recognized
+     * @throws NullPointerException if the parameter is {@code null}
+     * @return See above
+     */
+    default String keyToString(K key)   { return key == null ? null : key.toString(); }
+
+    /**
+     * Returns a new unique primary key for the storage that
+     * this {@code StringKeyConvertor} belongs to. The uniqueness
+     * needs to be guaranteed by e.g. using database sequences or
+     * using a random value that is proved sufficiently improbable
+     * to be repeated.
+     * 
+     * @return
+     */
+    K yieldNewUniqueKey();
+
+    /**
+     * Returns native representation of the key from String representation
+     * @param key
+     * @throws IllegalArgumentException if the string format is not recognized
+     * @throws NullPointerException if the parameter is {@code null}
+     * @return See above
+     */
+    K fromString(String key);
+
+    /**
+     * Exception-free variant of {@link #fromString} method.
+     * Returns native representation of the key from String representation,
+     * or {@code null} if the {@code key} is either {@code null} or invalid.
+     * @param key
+     * @return See above
+     */
+    default K fromStringSafe(String key) {
+        try {
+            return fromString(key);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    public static class UUIDKey implements StringKeyConvertor {
+
+        public static final UUIDKey INSTANCE = new UUIDKey();
+
+        @Override
+        public UUID yieldNewUniqueKey() {
+            return UUID.randomUUID();
+        }
+
+        @Override
+        public UUID fromString(String key) {
+            return UUID.fromString(key);
+        }
+    }
+
+    public static class StringKey implements StringKeyConvertor {
+
+        public static final StringKey INSTANCE = new StringKey();
+
+        @Override
+        public String fromString(String key) {
+            return key;
+        }
+
+        @Override
+        public String yieldNewUniqueKey() {
+            return fromString(UUID.randomUUID().toString());
+        }
+    }
+
+    public static class ULongKey implements StringKeyConvertor {
+
+        public static final ULongKey INSTANCE = new ULongKey();
+
+        /*
+         * The random number generator used by this class to create random
+         * based UUIDs. In a holder class to defer initialization until needed.
+         */
+        private static class Holder {
+            static final SecureRandom numberGenerator = new SecureRandom();
+        }
+
+        @Override
+        public Long fromString(String key) {
+            return key == null ? null : Long.parseUnsignedLong(key);
+        }
+
+        @Override
+        public Long yieldNewUniqueKey() {
+            return Holder.numberGenerator.nextLong();
+        }
+    }
+
+}
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java
index 760fad0959..4603f4071f 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java
@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Stream;
 import org.keycloak.models.map.storage.MapModelCriteriaBuilder.UpdatePredicatesFunc;
+import org.keycloak.models.map.storage.StringKeyConvertor;
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -43,10 +44,12 @@ public class ConcurrentHashMapStorage, M> impleme
     private final ConcurrentMap store = new ConcurrentHashMap<>();
 
     private final Map, UpdatePredicatesFunc> fieldPredicates;
+    private final StringKeyConvertor keyConvertor;
 
     @SuppressWarnings("unchecked")
-    public ConcurrentHashMapStorage(Class modelClass) {
+    public ConcurrentHashMapStorage(Class modelClass, StringKeyConvertor keyConvertor) {
         this.fieldPredicates = MapFieldPredicates.getPredicates(modelClass);
+        this.keyConvertor = keyConvertor;
     }
 
     @Override
@@ -108,6 +111,10 @@ public class ConcurrentHashMapStorage, M> impleme
         return sessionTransaction == null ? new MapKeycloakTransaction<>(this) : (MapKeycloakTransaction) sessionTransaction;
     }
 
+    @Override
+    public StringKeyConvertor getKeyConvertor() {
+        return keyConvertor;
+    }
 
     @Override
     public Stream read(ModelCriteriaBuilder criteria) {
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProvider.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProvider.java
index 90a6e3cd15..2fdd7f79e0 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProvider.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProvider.java
@@ -37,8 +37,8 @@ public class ConcurrentHashMapStorageProvider implements MapStorageProvider {
     }
 
     @Override
-    public , M> ConcurrentHashMapStorage getStorage(String name,
-      Class keyType, Class valueType, Class modelType, Flag... flags) {
-        return factory.getStorage(name, keyType, valueType, modelType, flags);
+    public , M> ConcurrentHashMapStorage getStorage(
+      Class valueType, Class modelType, Flag... flags) {
+        return factory.getStorage(valueType, modelType, flags);
     }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java
index e34dc39f09..4adedd1494 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java
@@ -16,10 +16,23 @@
  */
 package org.keycloak.models.map.storage.chm;
 
+import org.keycloak.component.AmphibianProviderFactory;
 import org.keycloak.Config.Scope;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.component.ComponentModelScope;
 import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserLoginFailureModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.map.common.AbstractEntity;
 import org.keycloak.models.map.common.Serialization;
@@ -35,13 +48,18 @@ import org.keycloak.models.map.storage.MapStorageProvider;
 import org.keycloak.models.map.storage.MapStorageProviderFactory;
 import org.keycloak.models.map.storage.ModelCriteriaBuilder;
 import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
-import java.util.UUID;
+import org.keycloak.models.map.storage.StringKeyConvertor;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.sessions.RootAuthenticationSessionModel;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  *
  * @author hmlnarik
  */
-public class ConcurrentHashMapStorageProviderFactory implements MapStorageProviderFactory {
+public class ConcurrentHashMapStorageProviderFactory implements AmphibianProviderFactory,MapStorageProviderFactory {
 
     public static final String PROVIDER_ID = "concurrenthashmap";
 
@@ -49,22 +67,69 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
 
     private final ConcurrentHashMap> storages = new ConcurrentHashMap<>();
 
+    private final Map keyConvertors = new HashMap<>();
+
     private File storageDirectory;
 
+    private String suffix;
+
+    private StringKeyConvertor defaultKeyConvertor;
+
+    public static final Map, String> MODEL_TO_NAME = new HashMap<>();
+    static {
+        MODEL_TO_NAME.put(AuthenticatedClientSessionModel.class, "client-sessions");
+        MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes");
+        MODEL_TO_NAME.put(ClientModel.class, "clients");
+        MODEL_TO_NAME.put(GroupModel.class, "groups");
+        MODEL_TO_NAME.put(RealmModel.class, "realms");
+        MODEL_TO_NAME.put(RoleModel.class, "roles");
+        MODEL_TO_NAME.put(RootAuthenticationSessionModel.class, "auth-sessions");
+        MODEL_TO_NAME.put(UserLoginFailureModel.class, "user-login-failures");
+        MODEL_TO_NAME.put(UserModel.class, "users");
+        MODEL_TO_NAME.put(UserSessionModel.class, "user-sessions");
+
+        // authz
+        MODEL_TO_NAME.put(PermissionTicket.class, "authz-permission-tickets");
+        MODEL_TO_NAME.put(Policy.class, "authz-policies");
+        MODEL_TO_NAME.put(ResourceServer.class, "authz-resource-servers");
+        MODEL_TO_NAME.put(Resource.class, "authz-resources");
+        MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes");
+    }
+
+    private static final Map KEY_CONVERTORS = new HashMap<>();
+    static {
+        KEY_CONVERTORS.put("uuid", StringKeyConvertor.UUIDKey.INSTANCE);
+        KEY_CONVERTORS.put("string", StringKeyConvertor.StringKey.INSTANCE);
+        KEY_CONVERTORS.put("ulong", StringKeyConvertor.ULongKey.INSTANCE);
+    }
+
     @Override
     public MapStorageProvider create(KeycloakSession session) {
         return new ConcurrentHashMapStorageProvider(this);
     }
 
+
     @Override
     public void init(Scope config) {
-        final String dir = config.get("dir");
-        if (dir == null || dir.trim().isEmpty()) {
-            LOG.warn("No directory set, created objects will not survive server restart");
-            this.storageDirectory = null;
+        if (config instanceof ComponentModelScope) {
+            this.suffix = "-" + ((ComponentModelScope) config).getComponentId();
         } else {
-            File f = new File(dir);
-            try {
+            this.suffix = "";
+        }
+    
+        final String keyType = config.get("keyType", "uuid");
+        defaultKeyConvertor = getKeyConvertor(keyType);
+        for (String name : MODEL_TO_NAME.values()) {
+            keyConvertors.put(name, getKeyConvertor(config.get("keyType." + name, keyType)));
+        }
+
+        final String dir = config.get("dir");
+        try {
+            if (dir == null || dir.trim().isEmpty()) {
+                LOG.warn("No directory set, created objects will not survive server restart");
+                this.storageDirectory = null;
+            } else {
+                File f = new File(dir);
                 Files.createDirectories(f.toPath());
                 if (f.exists()) {
                     this.storageDirectory = f;
@@ -72,13 +137,21 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
                     LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
                     this.storageDirectory = null;
                 }
-            } catch (IOException ex) {
-                LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
-                this.storageDirectory = null;
             }
+        } catch (IOException ex) {
+            LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
+            this.storageDirectory = null;
         }
     }
 
+    private StringKeyConvertor getKeyConvertor(final String keyType) throws IllegalArgumentException {
+        StringKeyConvertor res = KEY_CONVERTORS.get(keyType);
+        if (res == null) {
+            throw new IllegalArgumentException("Unknown key type: " + keyType);
+        }
+        return res;
+    }
+
     @Override
     public void postInit(KeycloakSessionFactory factory) {
     }
@@ -88,17 +161,17 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
         storages.forEach(this::storeMap);
     }
 
-    private void storeMap(String fileName, ConcurrentHashMapStorage store) {
-        if (fileName != null) {
-            File f = getFile(fileName);
+    private void storeMap(String mapName, ConcurrentHashMapStorage store) {
+        if (mapName != null) {
+            File f = getFile(mapName);
             try {
-                if (storageDirectory != null && storageDirectory.exists()) {
+                if (storageDirectory != null) {
                     LOG.debugf("Storing contents to %s", f.getCanonicalPath());
                     @SuppressWarnings("unchecked")
                     final ModelCriteriaBuilder readAllCriteria = store.createCriteriaBuilder();
                     Serialization.MAPPER.writeValue(f, store.read(readAllCriteria));
                 } else {
-                    LOG.debugf("Not storing contents of %s because directory %s does not exist", fileName, this.storageDirectory);
+                    LOG.debugf("Not storing contents of %s because directory not set", mapName);
                 }
             } catch (IOException ex) {
                 throw new RuntimeException(ex);
@@ -106,19 +179,33 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
         }
     }
 
-    private , M> ConcurrentHashMapStorage loadMap(String fileName,
+    private , M> ConcurrentHashMapStorage loadMap(String mapName,
       Class valueType, Class modelType, EnumSet flags) {
+        final StringKeyConvertor kc = keyConvertors.getOrDefault(mapName, defaultKeyConvertor);
+
+        LOG.debugf("Initializing new map storage: %s", mapName);
+
+        @SuppressWarnings("unchecked")
         ConcurrentHashMapStorage store;
         if (modelType == UserSessionModel.class) {
-            ConcurrentHashMapStorage clientSessionStore =
-              getStorage("clientSessions", UUID.class, MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
-            store = new UserSessionConcurrentHashMapStorage<>(clientSessionStore);
+            ConcurrentHashMapStorage clientSessionStore = getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+            store = new UserSessionConcurrentHashMapStorage(clientSessionStore, kc) {
+                @Override
+                public String toString() {
+                    return "ConcurrentHashMapStorage(" + mapName + suffix + ")";
+                }
+            };
         } else {
-            store = new ConcurrentHashMapStorage<>(modelType);
+            store = new ConcurrentHashMapStorage(modelType, kc) {
+                @Override
+                public String toString() {
+                    return "ConcurrentHashMapStorage(" + mapName + suffix + ")";
+                }
+            };
         }
 
         if (! flags.contains(Flag.INITIALIZE_EMPTY)) {
-            final File f = getFile(fileName);
+            final File f = getFile(mapName);
             if (f != null && f.exists()) {
                 try {
                     LOG.debugf("Restoring contents from %s", f.getCanonicalPath());
@@ -141,17 +228,38 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
     }
 
     @SuppressWarnings("unchecked")
-    @Override
-    public , M> ConcurrentHashMapStorage getStorage(String name,
-      Class keyType, Class valueType, Class modelType, Flag... flags) {
+    public , M> ConcurrentHashMapStorage getStorage(
+      Class valueType, Class modelType, Flag... flags) {
         EnumSet f = flags == null || flags.length == 0 ? EnumSet.noneOf(Flag.class) : EnumSet.of(flags[0], flags);
+        String name = MODEL_TO_NAME.getOrDefault(modelType, modelType.getSimpleName());
+        /* From ConcurrentHashMapStorage.computeIfAbsent javadoc:
+         *
+         *   "... the computation [...] must not attempt to update any other mappings of this map."
+         *
+         * For UserSessionModel, there is a separate clientSessionStore in this CHM implementation. Thus
+         * we cannot guarantee that this won't be the case e.g. for user and client sessions. Hence we need
+         * to prepare clientSessionStore outside computeIfAbsent, otherwise deadlock occurs.
+         */
+        if (modelType == UserSessionModel.class) {
+            getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+        }
         return (ConcurrentHashMapStorage) storages.computeIfAbsent(name, n -> loadMap(name, valueType, modelType, f));
     }
 
     private File getFile(String fileName) {
         return storageDirectory == null
           ? null
-          : new File(storageDirectory, "map-" + fileName + ".json");
+          : new File(storageDirectory, "map-" + fileName + suffix + ".json");
+    }
+
+    @Override
+    public String getHelpText() {
+        return "In-memory ConcurrentHashMap storage";
+    }
+
+    @Override
+    public List getConfigProperties() {
+        return Collections.emptyList();
     }
 
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java
index 9aa637adfc..f3811d86c0 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java
@@ -23,8 +23,9 @@ import org.keycloak.models.map.common.AbstractEntity;
 import org.keycloak.models.map.storage.MapKeycloakTransaction;
 import org.keycloak.models.map.storage.ModelCriteriaBuilder;
 import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
-import org.keycloak.models.map.userSession.AbstractAuthenticatedClientSessionEntity;
-import org.keycloak.models.map.userSession.AbstractUserSessionEntity;
+import org.keycloak.models.map.storage.StringKeyConvertor;
+import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
+import org.keycloak.models.map.userSession.MapUserSessionEntity;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -34,15 +35,15 @@ import java.util.stream.Collectors;
  *
  * @author hmlnarik
  */
-public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapStorage, UserSessionModel> {
+public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapStorage, UserSessionModel> {
 
-    private final ConcurrentHashMapStorage, AuthenticatedClientSessionModel> clientSessionStore;
+    private final ConcurrentHashMapStorage, AuthenticatedClientSessionModel> clientSessionStore;
 
-    private class Transaction extends MapKeycloakTransaction, UserSessionModel> {
+    private class Transaction extends MapKeycloakTransaction, UserSessionModel> {
 
-        private final MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTr;
+        private final MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTr;
 
-        public Transaction(MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTr) {
+        public Transaction(MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTr) {
             super(UserSessionConcurrentHashMapStorage.this);
             this.clientSessionTr = clientSessionTr;
         }
@@ -65,15 +66,16 @@ public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapSto
     }
 
     @SuppressWarnings("unchecked")
-    public UserSessionConcurrentHashMapStorage(ConcurrentHashMapStorage, AuthenticatedClientSessionModel> clientSessionStore) {
-        super(UserSessionModel.class);
+    public UserSessionConcurrentHashMapStorage(ConcurrentHashMapStorage, AuthenticatedClientSessionModel> clientSessionStore,
+      StringKeyConvertor keyConvertor) {
+        super(UserSessionModel.class, keyConvertor);
         this.clientSessionStore = clientSessionStore;
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public MapKeycloakTransaction, UserSessionModel> createTransaction(KeycloakSession session) {
+    public MapKeycloakTransaction, UserSessionModel> createTransaction(KeycloakSession session) {
         MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
-        return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction, UserSessionModel>) sessionTransaction;
+        return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction, UserSessionModel>) sessionTransaction;
     }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/user/AbstractUserEntity.java b/model/map/src/main/java/org/keycloak/models/map/user/AbstractUserEntity.java
deleted file mode 100644
index 8a353e9aa1..0000000000
--- a/model/map/src/main/java/org/keycloak/models/map/user/AbstractUserEntity.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright 2020 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.keycloak.models.map.user;
-
-import org.keycloak.models.ModelDuplicateException;
-import org.keycloak.models.map.common.AbstractEntity;
-import org.keycloak.models.utils.KeycloakModelUtils;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- *
- * @author mhajas
- */
-public abstract class AbstractUserEntity implements AbstractEntity {
-
-    private final K id;
-    private final String realmId;
-
-    private String username;
-    private String firstName;
-    private Long createdTimestamp;
-    private String lastName;
-    private String email;
-    private boolean enabled;
-    private boolean emailVerified;
-    // This is necessary to be able to dynamically switch unique email constraints on and off in the realm settings
-    private String emailConstraint = KeycloakModelUtils.generateId();
-    private Map> attributes = new HashMap<>();
-    private Set requiredActions = new HashSet<>();
-    private final Map credentials = new HashMap<>();
-    private final List credentialsOrder = new LinkedList<>();
-    private final Map federatedIdentities = new HashMap<>();
-    private final Map userConsents = new HashMap<>();
-    private Set groupsMembership = new HashSet<>();
-    private Set rolesMembership = new HashSet<>();
-    private String federationLink;
-    private String serviceAccountClientLink;
-    private int notBefore;
-
-    static Comparator> COMPARE_BY_USERNAME = Comparator.comparing(AbstractUserEntity::getUsername);
-
-    /**
-     * Flag signalizing that any of the setters has been meaningfully used.
-     */
-    protected boolean updated;
-
-    protected AbstractUserEntity() {
-        this.id = null;
-        this.realmId = null;
-    }
-
-    public AbstractUserEntity(K id, String realmId) {
-        Objects.requireNonNull(id, "id");
-        Objects.requireNonNull(realmId, "realmId");
-
-        this.id = id;
-        this.realmId = realmId;
-    }
-
-    @Override
-    public K getId() {
-        return this.id;
-    }
-
-    @Override
-    public boolean isUpdated() {
-        return this.updated
-                || userConsents.values().stream().anyMatch(UserConsentEntity::isUpdated)
-                || credentials.values().stream().anyMatch(UserCredentialEntity::isUpdated)
-                || federatedIdentities.values().stream().anyMatch(UserFederatedIdentityEntity::isUpdated);
-    }
-
-    public String getRealmId() {
-        return realmId;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public void setUsername(String username) {
-        this.updated |= !Objects.equals(this.username, username);
-        this.username = username;
-    }
-
-    public String getFirstName() {
-        return firstName;
-    }
-
-    public void setFirstName(String firstName) {
-        this.updated |= !Objects.equals(this.firstName, firstName);
-        this.firstName = firstName;
-    }
-
-    public Long getCreatedTimestamp() {
-        return createdTimestamp;
-    }
-
-    public void setCreatedTimestamp(Long createdTimestamp) {
-        this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
-        this.createdTimestamp = createdTimestamp;
-    }
-
-    public String getLastName() {
-        return lastName;
-    }
-
-    public void setLastName(String lastName) {
-        this.updated |= !Objects.equals(this.lastName, lastName);
-        this.lastName = lastName;
-    }
-
-    public String getEmail() {
-        return email;
-    }
-
-    public void setEmail(String email, boolean duplicateEmailsAllowed) {
-        this.updated |= !Objects.equals(this.email, email);
-        this.email = email;
-        this.emailConstraint = email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email;
-    }
-
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    public void setEnabled(boolean enabled) {
-        this.updated |= !Objects.equals(this.enabled, enabled);
-        this.enabled = enabled;
-    }
-
-    public boolean isEmailVerified() {
-        return emailVerified;
-    }
-
-    public void setEmailVerified(boolean emailVerified) {
-        this.updated |= !Objects.equals(this.emailVerified, emailVerified);
-        this.emailVerified = emailVerified;
-    }
-
-    public String getEmailConstraint() {
-        return emailConstraint;
-    }
-
-    public void setEmailConstraint(String emailConstraint) {
-        this.updated |= !Objects.equals(this.emailConstraint, emailConstraint);
-        this.emailConstraint = emailConstraint;
-    }
-
-    public Map> getAttributes() {
-        return attributes;
-    }
-
-    public List getAttribute(String name) {
-        return attributes.getOrDefault(name, Collections.emptyList());
-    }
-
-    public void setAttributes(Map> attributes) {
-        this.updated |= !Objects.equals(this.attributes, attributes);
-        this.attributes = attributes;
-    }
-
-    public void setAttribute(String name, List value) {
-        this.updated |= !Objects.equals(this.attributes.put(name, value), value);
-    }
-    
-    public void removeAttribute(String name) {
-        this.updated |= this.attributes.remove(name) != null;
-    }
-
-    public Set getRequiredActions() {
-        return requiredActions;
-    }
-
-    public void setRequiredActions(Set requiredActions) {
-        this.updated |= !Objects.equals(this.requiredActions, requiredActions);
-        this.requiredActions = requiredActions;
-    }
-
-    public void addRequiredAction(String requiredAction) {
-        this.updated |= this.requiredActions.add(requiredAction);
-    }
-
-    public void removeRequiredAction(String requiredAction) {
-        this.updated |= this.requiredActions.remove(requiredAction);
-    }
-
-    public void updateCredential(UserCredentialEntity credentialEntity) {
-        this.updated |= credentials.replace(credentialEntity.getId(), credentialEntity) != null;
-    }
-
-    public void addCredential(UserCredentialEntity credentialEntity) {
-        if (credentials.containsKey(credentialEntity.getId())) {
-            throw new ModelDuplicateException("A CredentialModel with given id already exists");
-        }
-
-        this.updated = true;
-        credentials.put(credentialEntity.getId(), credentialEntity);
-        credentialsOrder.add(credentialEntity.getId());
-    }
-    
-    public boolean removeCredential(String credentialId) {
-        if (!credentials.containsKey(credentialId)) {
-            return false;
-        }
-
-        this.updated = true;
-        this.credentials.remove(credentialId);
-        this.credentialsOrder.remove(credentialId);
-
-        return true;
-    }
-    
-    public UserCredentialEntity getCredential(String id) {
-        return credentials.get(id);
-    }
-    
-    public Stream getCredentials() {
-        return credentialsOrder.stream()
-                .map(credentials::get);
-    }
-    
-    public int getCredentialIndex(String credentialId) {
-        return credentialsOrder.indexOf(credentialId);
-    }
-    
-    public void moveCredential(int currentPosition, int newPosition) {
-        this.updated |= currentPosition != newPosition;
-        credentialsOrder.add(newPosition, credentialsOrder.remove(currentPosition));
-    }
-
-    public Stream getFederatedIdentities() {
-        return federatedIdentities.values().stream();
-    }
-
-    public void setFederatedIdentities(Collection federatedIdentities) {
-        this.updated = true;
-        this.federatedIdentities.clear();
-        this.federatedIdentities.putAll(federatedIdentities.stream()
-                .collect(Collectors.toMap(UserFederatedIdentityEntity::getIdentityProvider, Function.identity())));
-    }
-    
-    public void addFederatedIdentity(UserFederatedIdentityEntity federatedIdentity) {
-        String idpId = federatedIdentity.getIdentityProvider();
-        this.updated |= !Objects.equals(this.federatedIdentities.put(idpId, federatedIdentity), federatedIdentity);
-    }
-
-    public UserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity) {
-        return this.federatedIdentities.get(federatedIdentity);
-    }
-    
-    public boolean removeFederatedIdentity(String providerId) {
-        boolean removed = federatedIdentities.remove(providerId) != null;
-        this.updated |= removed;
-        return removed;
-    }
-
-    public void updateFederatedIdentity(UserFederatedIdentityEntity federatedIdentityModel) {
-        this.updated |= federatedIdentities.replace(federatedIdentityModel.getIdentityProvider(), federatedIdentityModel) != null;
-    }
-
-    public Stream getUserConsents() {
-        return userConsents.values().stream();
-    }
-
-    public UserConsentEntity getUserConsent(String clientId) {
-        return this.userConsents.get(clientId);
-    }
-
-    
-    public void addUserConsent(UserConsentEntity userConsentEntity) {
-        String clientId = userConsentEntity.getClientId();
-        this.updated |= !Objects.equals(this.userConsents.put(clientId, userConsentEntity), userConsentEntity);
-    }
-
-    public boolean removeUserConsent(String clientId) {
-        boolean removed = userConsents.remove(clientId) != null;
-        this.updated |= removed;
-        return removed;
-    }
-
-    public Set getGroupsMembership() {
-        return groupsMembership;
-    }
-
-    public void setGroupsMembership(Set groupsMembership) {
-        this.updated |= Objects.equals(groupsMembership, this.groupsMembership);
-        this.groupsMembership = groupsMembership;
-    }
-    
-    public void addGroupsMembership(String groupId) {
-        this.updated |= this.groupsMembership.add(groupId);
-    }
-
-    public void removeGroupsMembership(String groupId) {
-        this.updated |= this.groupsMembership.remove(groupId);
-    }
-
-    public Set getRolesMembership() {
-        return rolesMembership;
-    }
-
-    public void setRolesMembership(Set rolesMembership) {
-        this.updated |= Objects.equals(rolesMembership, this.rolesMembership);
-        this.rolesMembership = rolesMembership;
-    }
-
-    public void addRolesMembership(String roleId) {
-        this.updated |= this.rolesMembership.add(roleId);
-    }
-
-    public void removeRolesMembership(String roleId) {
-        this.updated |= this.rolesMembership.remove(roleId);
-    }
-
-    public String getFederationLink() {
-        return federationLink;
-    }
-
-    public void setFederationLink(String federationLink) {
-        this.updated |= !Objects.equals(this.federationLink, federationLink);
-        this.federationLink = federationLink;
-    }
-
-    public String getServiceAccountClientLink() {
-        return serviceAccountClientLink;
-    }
-
-    public void setServiceAccountClientLink(String serviceAccountClientLink) {
-        this.updated |= !Objects.equals(this.serviceAccountClientLink, serviceAccountClientLink);
-        this.serviceAccountClientLink = serviceAccountClientLink;
-    }
-
-    public int getNotBefore() {
-        return notBefore;
-    }
-
-    public void setNotBefore(int notBefore) {
-        this.updated |= !Objects.equals(this.notBefore, notBefore);
-        this.notBefore = notBefore;
-    }
-
-}
diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java
index cd48bca631..1c7b8c2cdf 100644
--- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java
+++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java
@@ -32,20 +32,14 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
 import java.util.stream.Stream;
 
 
-public abstract class MapUserAdapter extends AbstractUserModel {
-    public MapUserAdapter(KeycloakSession session, RealmModel realm, MapUserEntity entity) {
+public abstract class MapUserAdapter extends AbstractUserModel> {
+    public MapUserAdapter(KeycloakSession session, RealmModel realm, MapUserEntity entity) {
         super(session, realm, entity);
     }
 
-    @Override
-    public String getId() {
-        return entity.getId().toString();
-    }
-
     @Override
     public String getUsername() {
         return entity.getUsername();
diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java
index 5e3cec58db..495dd0053b 100644
--- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java
+++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java
@@ -1,13 +1,13 @@
 /*
  * Copyright 2020 Red Hat, Inc. and/or its affiliates
  * and other contributors as indicated by the @author tags.
- *
+ * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- *
+ * 
  * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,18 +17,355 @@
 
 package org.keycloak.models.map.user;
 
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.map.common.AbstractEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
-import java.util.UUID;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-public class MapUserEntity extends AbstractUserEntity {
+/**
+ *
+ * @author mhajas
+ */
+public class MapUserEntity implements AbstractEntity {
+
+    private final K id;
+    private final String realmId;
+
+    private String username;
+    private String firstName;
+    private Long createdTimestamp;
+    private String lastName;
+    private String email;
+    private boolean enabled;
+    private boolean emailVerified;
+    // This is necessary to be able to dynamically switch unique email constraints on and off in the realm settings
+    private String emailConstraint = KeycloakModelUtils.generateId();
+    private Map> attributes = new HashMap<>();
+    private Set requiredActions = new HashSet<>();
+    private final Map credentials = new HashMap<>();
+    private final List credentialsOrder = new LinkedList<>();
+    private final Map federatedIdentities = new HashMap<>();
+    private final Map userConsents = new HashMap<>();
+    private Set groupsMembership = new HashSet<>();
+    private Set rolesMembership = new HashSet<>();
+    private String federationLink;
+    private String serviceAccountClientLink;
+    private int notBefore;
+
+    static Comparator> COMPARE_BY_USERNAME = Comparator.comparing(MapUserEntity::getUsername);
+
+    /**
+     * Flag signalizing that any of the setters has been meaningfully used.
+     */
+    protected boolean updated;
 
-    public static final Comparator COMPARE_BY_USERNAME = Comparator.comparing(MapUserEntity::getUsername, String.CASE_INSENSITIVE_ORDER);
-    
     protected MapUserEntity() {
-        super();
+        this.id = null;
+        this.realmId = null;
     }
 
-    public MapUserEntity(UUID id, String realmId) {
-        super(id, realmId);
+    public MapUserEntity(K id, String realmId) {
+        Objects.requireNonNull(id, "id");
+        Objects.requireNonNull(realmId, "realmId");
+
+        this.id = id;
+        this.realmId = realmId;
     }
+
+    @Override
+    public K getId() {
+        return this.id;
+    }
+
+    @Override
+    public boolean isUpdated() {
+        return this.updated
+                || userConsents.values().stream().anyMatch(UserConsentEntity::isUpdated)
+                || credentials.values().stream().anyMatch(UserCredentialEntity::isUpdated)
+                || federatedIdentities.values().stream().anyMatch(UserFederatedIdentityEntity::isUpdated);
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.updated |= !Objects.equals(this.username, username);
+        this.username = username;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.updated |= !Objects.equals(this.firstName, firstName);
+        this.firstName = firstName;
+    }
+
+    public Long getCreatedTimestamp() {
+        return createdTimestamp;
+    }
+
+    public void setCreatedTimestamp(Long createdTimestamp) {
+        this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
+        this.createdTimestamp = createdTimestamp;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.updated |= !Objects.equals(this.lastName, lastName);
+        this.lastName = lastName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email, boolean duplicateEmailsAllowed) {
+        this.updated |= !Objects.equals(this.email, email);
+        this.email = email;
+        this.emailConstraint = email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.updated |= !Objects.equals(this.enabled, enabled);
+        this.enabled = enabled;
+    }
+
+    public boolean isEmailVerified() {
+        return emailVerified;
+    }
+
+    public void setEmailVerified(boolean emailVerified) {
+        this.updated |= !Objects.equals(this.emailVerified, emailVerified);
+        this.emailVerified = emailVerified;
+    }
+
+    public String getEmailConstraint() {
+        return emailConstraint;
+    }
+
+    public void setEmailConstraint(String emailConstraint) {
+        this.updated |= !Objects.equals(this.emailConstraint, emailConstraint);
+        this.emailConstraint = emailConstraint;
+    }
+
+    public Map> getAttributes() {
+        return attributes;
+    }
+
+    public List getAttribute(String name) {
+        return attributes.getOrDefault(name, Collections.emptyList());
+    }
+
+    public void setAttributes(Map> attributes) {
+        this.updated |= !Objects.equals(this.attributes, attributes);
+        this.attributes = attributes;
+    }
+
+    public void setAttribute(String name, List value) {
+        this.updated |= !Objects.equals(this.attributes.put(name, value), value);
+    }
+    
+    public void removeAttribute(String name) {
+        this.updated |= this.attributes.remove(name) != null;
+    }
+
+    public Set getRequiredActions() {
+        return requiredActions;
+    }
+
+    public void setRequiredActions(Set requiredActions) {
+        this.updated |= !Objects.equals(this.requiredActions, requiredActions);
+        this.requiredActions = requiredActions;
+    }
+
+    public void addRequiredAction(String requiredAction) {
+        this.updated |= this.requiredActions.add(requiredAction);
+    }
+
+    public void removeRequiredAction(String requiredAction) {
+        this.updated |= this.requiredActions.remove(requiredAction);
+    }
+
+    public void updateCredential(UserCredentialEntity credentialEntity) {
+        this.updated |= credentials.replace(credentialEntity.getId(), credentialEntity) != null;
+    }
+
+    public void addCredential(UserCredentialEntity credentialEntity) {
+        if (credentials.containsKey(credentialEntity.getId())) {
+            throw new ModelDuplicateException("A CredentialModel with given id already exists");
+        }
+
+        this.updated = true;
+        credentials.put(credentialEntity.getId(), credentialEntity);
+        credentialsOrder.add(credentialEntity.getId());
+    }
+    
+    public boolean removeCredential(String credentialId) {
+        if (!credentials.containsKey(credentialId)) {
+            return false;
+        }
+
+        this.updated = true;
+        this.credentials.remove(credentialId);
+        this.credentialsOrder.remove(credentialId);
+
+        return true;
+    }
+    
+    public UserCredentialEntity getCredential(String id) {
+        return credentials.get(id);
+    }
+    
+    public Stream getCredentials() {
+        return credentialsOrder.stream()
+                .map(credentials::get);
+    }
+    
+    public int getCredentialIndex(String credentialId) {
+        return credentialsOrder.indexOf(credentialId);
+    }
+    
+    public void moveCredential(int currentPosition, int newPosition) {
+        this.updated |= currentPosition != newPosition;
+        credentialsOrder.add(newPosition, credentialsOrder.remove(currentPosition));
+    }
+
+    public Stream getFederatedIdentities() {
+        return federatedIdentities.values().stream();
+    }
+
+    public void setFederatedIdentities(Collection federatedIdentities) {
+        this.updated = true;
+        this.federatedIdentities.clear();
+        this.federatedIdentities.putAll(federatedIdentities.stream()
+                .collect(Collectors.toMap(UserFederatedIdentityEntity::getIdentityProvider, Function.identity())));
+    }
+    
+    public void addFederatedIdentity(UserFederatedIdentityEntity federatedIdentity) {
+        String idpId = federatedIdentity.getIdentityProvider();
+        this.updated |= !Objects.equals(this.federatedIdentities.put(idpId, federatedIdentity), federatedIdentity);
+    }
+
+    public UserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity) {
+        return this.federatedIdentities.get(federatedIdentity);
+    }
+    
+    public boolean removeFederatedIdentity(String providerId) {
+        boolean removed = federatedIdentities.remove(providerId) != null;
+        this.updated |= removed;
+        return removed;
+    }
+
+    public void updateFederatedIdentity(UserFederatedIdentityEntity federatedIdentityModel) {
+        this.updated |= federatedIdentities.replace(federatedIdentityModel.getIdentityProvider(), federatedIdentityModel) != null;
+    }
+
+    public Stream getUserConsents() {
+        return userConsents.values().stream();
+    }
+
+    public UserConsentEntity getUserConsent(String clientId) {
+        return this.userConsents.get(clientId);
+    }
+
+    
+    public void addUserConsent(UserConsentEntity userConsentEntity) {
+        String clientId = userConsentEntity.getClientId();
+        this.updated |= !Objects.equals(this.userConsents.put(clientId, userConsentEntity), userConsentEntity);
+    }
+
+    public boolean removeUserConsent(String clientId) {
+        boolean removed = userConsents.remove(clientId) != null;
+        this.updated |= removed;
+        return removed;
+    }
+
+    public Set getGroupsMembership() {
+        return groupsMembership;
+    }
+
+    public void setGroupsMembership(Set groupsMembership) {
+        this.updated |= Objects.equals(groupsMembership, this.groupsMembership);
+        this.groupsMembership = groupsMembership;
+    }
+    
+    public void addGroupsMembership(String groupId) {
+        this.updated |= this.groupsMembership.add(groupId);
+    }
+
+    public void removeGroupsMembership(String groupId) {
+        this.updated |= this.groupsMembership.remove(groupId);
+    }
+
+    public Set getRolesMembership() {
+        return rolesMembership;
+    }
+
+    public void setRolesMembership(Set rolesMembership) {
+        this.updated |= Objects.equals(rolesMembership, this.rolesMembership);
+        this.rolesMembership = rolesMembership;
+    }
+
+    public void addRolesMembership(String roleId) {
+        this.updated |= this.rolesMembership.add(roleId);
+    }
+
+    public void removeRolesMembership(String roleId) {
+        this.updated |= this.rolesMembership.remove(roleId);
+    }
+
+    public String getFederationLink() {
+        return federationLink;
+    }
+
+    public void setFederationLink(String federationLink) {
+        this.updated |= !Objects.equals(this.federationLink, federationLink);
+        this.federationLink = federationLink;
+    }
+
+    public String getServiceAccountClientLink() {
+        return serviceAccountClientLink;
+    }
+
+    public void setServiceAccountClientLink(String serviceAccountClientLink) {
+        this.updated |= !Objects.equals(this.serviceAccountClientLink, serviceAccountClientLink);
+        this.serviceAccountClientLink = serviceAccountClientLink;
+    }
+
+    public int getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(int notBefore) {
+        this.updated |= !Objects.equals(this.notBefore, notBefore);
+        this.notBefore = notBefore;
+    }
+
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java
index 608ef154c2..44fde64bc4 100644
--- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java
+++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java
@@ -58,7 +58,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -73,30 +72,33 @@ import static org.keycloak.models.UserModel.LAST_NAME;
 import static org.keycloak.models.UserModel.USERNAME;
 import static org.keycloak.utils.StreamsUtil.paginatedStream;
 
-public class MapUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
+public class MapUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
 
     private static final Logger LOG = Logger.getLogger(MapUserProvider.class);
-    private static final Predicate ALWAYS_FALSE = c -> { return false; };
     private final KeycloakSession session;
-    final MapKeycloakTransaction tx;
-    private final MapStorage userStore;
+    final MapKeycloakTransaction, UserModel> tx;
+    private final MapStorage, UserModel> userStore;
 
-    public MapUserProvider(KeycloakSession session, MapStorage store) {
+    public MapUserProvider(KeycloakSession session, MapStorage, UserModel> store) {
         this.session = session;
         this.userStore = store;
         this.tx = userStore.createTransaction(session);
         session.getTransactionManager().enlist(tx);
     }
 
-    private MapUserEntity registerEntityForChanges(MapUserEntity origEntity) {
-        MapUserEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
-        tx.updateIfChanged(origEntity.getId(), res, MapUserEntity::isUpdated);
+    private MapUserEntity registerEntityForChanges(MapUserEntity origEntity) {
+        MapUserEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
+        tx.updateIfChanged(origEntity.getId(), res, MapUserEntity::isUpdated);
         return res;
     }
 
-    private Function entityToAdapterFunc(RealmModel realm) {
+    private Function, UserModel> entityToAdapterFunc(RealmModel realm) {
         // Clone entity before returning back, to avoid giving away a reference to the live object to the caller
-        return origEntity -> new MapUserAdapter(session, realm, registerEntityForChanges(origEntity)) {
+        return origEntity -> new MapUserAdapter(session, realm, registerEntityForChanges(origEntity)) {
+            @Override
+            public String getId() {
+                return userStore.getKeyConvertor().keyToString(entity.getId());
+            }
 
             @Override
             public boolean checkEmailUniqueness(RealmModel realm, String email) {
@@ -110,9 +112,9 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
         };
     }
 
-    private Predicate entityRealmFilter(RealmModel realm) {
+    private Predicate> entityRealmFilter(RealmModel realm) {
         if (realm == null || realm.getId() == null) {
-            return MapUserProvider.ALWAYS_FALSE;
+            return c -> false;
         }
         String realmId = realm.getId();
         return entity -> Objects.equals(realmId, entity.getRealmId());
@@ -122,22 +124,22 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
         return new ModelException("Specified user doesn't exist.");
     }
 
-    private Optional getEntityById(RealmModel realm, String id) {
+    private Optional> getEntityById(RealmModel realm, String id) {
         try {
-            return getEntityById(realm, UUID.fromString(id));
+            return getEntityById(realm, userStore.getKeyConvertor().fromString(id));
         } catch (IllegalArgumentException ex) {
             return Optional.empty();
         }
     }
 
-    private MapUserEntity getRegisteredEntityByIdOrThrow(RealmModel realm, String id) {
+    private MapUserEntity getRegisteredEntityByIdOrThrow(RealmModel realm, String id) {
         return getEntityById(realm, id)
                 .map(this::registerEntityForChanges)
                 .orElseThrow(this::userDoesntExistException);
     }
 
-    private Optional getEntityById(RealmModel realm, UUID id) {
-        MapUserEntity mapUserEntity = tx.read(id);
+    private Optional> getEntityById(RealmModel realm, K id) {
+        MapUserEntity mapUserEntity = tx.read(id);
         if (mapUserEntity != null && entityRealmFilter(realm).test(mapUserEntity)) {
             return Optional.of(mapUserEntity);
         }
@@ -145,7 +147,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
         return Optional.empty();
     }
 
-    private Optional getRegisteredEntityById(RealmModel realm, String id) {
+    private Optional> getRegisteredEntityById(RealmModel realm, String id) {
         return getEntityById(realm, id).map(this::registerEntityForChanges);
     }
 
@@ -193,7 +195,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     public Stream getFederatedIdentitiesStream(RealmModel realm, UserModel user) {
         LOG.tracef("getFederatedIdentitiesStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
         return getEntityById(realm, user.getId())
-                .map(AbstractUserEntity::getFederatedIdentities).orElseGet(Stream::empty)
+                .map(MapUserEntity::getFederatedIdentities).orElseGet(Stream::empty)
                 .map(UserFederatedIdentityEntity::toModel);
     }
 
@@ -249,7 +251,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     public Stream getConsentsStream(RealmModel realm, String userId) {
         LOG.tracef("getConsentByClientStream(%s, %s)%s", realm, userId, getShortStackTrace());
         return getEntityById(realm, userId)
-                .map(AbstractUserEntity::getUserConsents)
+                .map(MapUserEntity::getUserConsents)
                 .orElse(Stream.empty())
                 .map(consent -> UserConsentEntity.toModel(realm, consent));
     }
@@ -258,7 +260,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     public void updateConsent(RealmModel realm, String userId, UserConsentModel consent) {
         LOG.tracef("updateConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
 
-        MapUserEntity user = getRegisteredEntityByIdOrThrow(realm, userId);
+        MapUserEntity user = getRegisteredEntityByIdOrThrow(realm, userId);
         UserConsentEntity userConsentEntity = user.getUserConsent(consent.getClient().getId());
         if (userConsentEntity == null) {
             throw new ModelException("Consent not found for client [" + consent.getClient().getId() + "] and user [" + userId + "]");
@@ -329,13 +331,13 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
             throw new ModelDuplicateException("User with username '" + username + "' in realm " + realm.getName() + " already exists" );
         }
         
-        final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
+        final K entityId = id == null ? userStore.getKeyConvertor().yieldNewUniqueKey() : userStore.getKeyConvertor().fromString(id);
 
         if (tx.read(entityId) != null) {
             throw new ModelDuplicateException("User exists: " + entityId);
         }
 
-        MapUserEntity entity = new MapUserEntity(entityId, realm.getId());
+        MapUserEntity entity = new MapUserEntity<>(entityId, realm.getId());
         entity.setUsername(username.toLowerCase());
         entity.setCreatedTimestamp(Time.currentTimeMillis());
 
@@ -366,7 +368,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
         ModelCriteriaBuilder mcb = userStore.createCriteriaBuilder()
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
 
-        tx.delete(UUID.randomUUID(), mcb);
+        tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
     }
 
     @Override
@@ -376,7 +378,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
 
-        tx.delete(UUID.randomUUID(), mcb);
+        tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
     }
 
     @Override
@@ -386,7 +388,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             s.map(this::registerEntityForChanges)
               .forEach(userEntity -> userEntity.setFederationLink(null));
         }
@@ -400,7 +402,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             s.map(this::registerEntityForChanges)
               .forEach(userEntity -> userEntity.removeRolesMembership(roleId));
         }
@@ -414,7 +416,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             s.map(this::registerEntityForChanges)
               .forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
         }
@@ -428,7 +430,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             s.map(this::registerEntityForChanges)
               .forEach(userEntity -> userEntity.removeUserConsent(clientId));
         }
@@ -448,8 +450,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, clientScope.getRealm().getId())
           .compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
-            s.flatMap(AbstractUserEntity::getUserConsents)
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
+            s.flatMap(MapUserEntity::getUserConsents)
               .forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId));
         }
     }
@@ -466,14 +468,14 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
               .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
               .compare(SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, Operator.EQ, componentId);
 
-            try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+            try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
                 String providerIdS = new StorageId(componentId, "").getId();
                 s.forEach(removeConsentsForExternalClient(providerIdS));
             }
         }
     }
 
-    private Consumer removeConsentsForExternalClient(String idPrefix) {
+    private Consumer> removeConsentsForExternalClient(String idPrefix) {
         return userEntity -> {
             List consentClientIds = userEntity.getUserConsents()
               .map(UserConsentEntity::getClientId)
@@ -494,7 +496,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
         ModelCriteriaBuilder mcb = userStore.createCriteriaBuilder()
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             s.map(this::registerEntityForChanges)
               .forEach(entity -> entity.addRolesMembership(roleId));
         }
@@ -514,7 +516,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.USERNAME, Operator.ILIKE, username);
 
-        try (Stream s = tx.getUpdatedNotRemoved(mcb)) {
+        try (Stream> s = tx.getUpdatedNotRemoved(mcb)) {
             return s.findFirst()
               .map(entityToAdapterFunc(realm)).orElse(null);
         }
@@ -527,7 +529,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
           .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
           .compare(SearchableFields.EMAIL, Operator.EQ, email);
 
-        List usersWithEmail = tx.getUpdatedNotRemoved(mcb)
+        List> usersWithEmail = tx.getUpdatedNotRemoved(mcb)
                 .filter(userEntity -> Objects.equals(userEntity.getEmail(), email))
                 .collect(Collectors.toList());
         if (usersWithEmail.isEmpty()) return null;
@@ -538,7 +540,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
             throw new ModelDuplicateException("Multiple users with email '" + email + "' exist in Keycloak.");
         }
 
-        MapUserEntity userEntity = registerEntityForChanges(usersWithEmail.get(0));
+        MapUserEntity userEntity = registerEntityForChanges(usersWithEmail.get(0));
         
         if (!realm.isDuplicateEmailsAllowed()) {
             if (userEntity.getEmail() != null && !userEntity.getEmail().equals(userEntity.getEmailConstraint())) {
@@ -548,7 +550,12 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
             }
         }
         
-        return new MapUserAdapter(session, realm, userEntity) {
+        return new MapUserAdapter(session, realm, userEntity) {
+            @Override
+            public String getId() {
+                return userStore.getKeyConvertor().keyToString(userEntity.getId());
+            }
+
             @Override
             public boolean checkEmailUniqueness(RealmModel realm, String email) {
                 return getUserByEmail(realm, email) != null;
@@ -700,8 +707,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
             mcb = mcb.compare(SearchableFields.ASSIGNED_GROUP, Operator.IN, authorizedGroups);
         }
 
-        Stream usersStream = tx.getUpdatedNotRemoved(mcb)
-                .sorted(AbstractUserEntity.COMPARE_BY_USERNAME); // Sort before paginating
+        Stream> usersStream = tx.getUpdatedNotRemoved(mcb)
+                .sorted(MapUserEntity.COMPARE_BY_USERNAME); // Sort before paginating
         
         return paginatedStream(usersStream, firstResult, maxResults) // paginate if necessary
                 .map(entityToAdapterFunc(realm))
@@ -739,9 +746,9 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     @Override
     public boolean removeUser(RealmModel realm, UserModel user) {
         String userId = user.getId();
-        Optional userById = getEntityById(realm, userId);
+        Optional> userById = getEntityById(realm, userId);
         if (userById.isPresent()) {
-            tx.delete(UUID.fromString(userId));
+            tx.delete(userStore.getKeyConvertor().fromString(userId));
             return true;
         }
 
@@ -766,7 +773,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
                 .ifPresent(updateCredential(cred));
     }
     
-    private Consumer updateCredential(CredentialModel credentialModel) {
+    private Consumer> updateCredential(CredentialModel credentialModel) {
         return user -> {
             UserCredentialEntity credentialEntity = user.getCredential(credentialModel.getId());
             if (credentialEntity == null) return;
@@ -811,7 +818,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     public Stream getStoredCredentialsStream(RealmModel realm, UserModel user) {
         LOG.tracef("getStoredCredentialsStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
         return getEntityById(realm, user.getId())
-                .map(AbstractUserEntity::getCredentials)
+                .map(MapUserEntity::getCredentials)
                 .orElseGet(Stream::empty)
                 .map(UserCredentialEntity::toModel);
     }
@@ -835,7 +842,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
     public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) {
         LOG.tracef("moveCredentialTo(%s, %s, %s, %s)%s", realm, user.getId(), id, newPreviousCredentialId, getShortStackTrace());
         String userId = user.getId();
-        MapUserEntity userEntity = getRegisteredEntityById(realm, userId).orElse(null);
+        MapUserEntity userEntity = getRegisteredEntityById(realm, userId).orElse(null);
         if (userEntity == null) {
             LOG.warnf("User with id: [%s] not found", userId);
             return false;
diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java
index fba1703131..155ad06950 100644
--- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java
+++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java
@@ -18,34 +18,29 @@
 package org.keycloak.models.map.user;
 
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserProvider;
 import org.keycloak.models.UserProviderFactory;
 import org.keycloak.models.map.common.AbstractMapProviderFactory;
-import org.keycloak.models.map.storage.MapStorage;
-import org.keycloak.models.map.storage.MapStorageProvider;
-
-import org.keycloak.models.map.storage.MapStorageProviderFactory;
-import java.util.UUID;
 
 /**
  *
  * @author mhajas
  */
-public class MapUserProviderFactory extends AbstractMapProviderFactory implements UserProviderFactory {
+public class MapUserProviderFactory extends AbstractMapProviderFactory, UserModel> implements UserProviderFactory {
 
-    private MapStorage store;
-
-    @Override
-    public void postInit(KeycloakSessionFactory factory) {
-        MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
-        this.store = sp.getStorage("users", UUID.class, MapUserEntity.class, UserModel.class);
+    public MapUserProviderFactory() {
+        super(MapUserEntity.class, UserModel.class);
     }
 
-
     @Override
     public UserProvider create(KeycloakSession session) {
-        return new MapUserProvider(session, store);
+        return new MapUserProvider<>(session, getStorage(session));
     }
+
+    @Override
+    public String getHelpText() {
+        return "User provider";
+    }
+
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionEntity.java
deleted file mode 100644
index f186a70ece..0000000000
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionEntity.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2021 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.keycloak.models.map.userSession;
-
-import org.keycloak.common.util.Time;
-import org.keycloak.models.map.common.AbstractEntity;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Martin Kanis
- */
-public abstract class AbstractAuthenticatedClientSessionEntity implements AbstractEntity {
-
-    private K id;
-    private String userSessionId;
-    private String realmId;
-    private String clientId;
-
-    /**
-     * Flag signalizing that any of the setters has been meaningfully used.
-     */
-    protected boolean updated;
-
-    private String authMethod;
-    private String redirectUri;
-    private volatile int timestamp;
-    private long expiration;
-    private String action;
-
-    private Map notes = new ConcurrentHashMap<>();
-
-    private String currentRefreshToken;
-    private int currentRefreshTokenUseCount;
-
-    private boolean offline;
-
-    public AbstractAuthenticatedClientSessionEntity() {
-        this.id = null;
-        this.realmId = null;
-    }
-
-    public AbstractAuthenticatedClientSessionEntity(K id, String userSessionId, String realmId, String clientId, boolean offline) {
-        Objects.requireNonNull(id, "id");
-        Objects.requireNonNull(userSessionId, "userSessionId");
-        Objects.requireNonNull(realmId, "realmId");
-        Objects.requireNonNull(clientId, "clientId");
-
-        this.id = id;
-        this.userSessionId = userSessionId;
-        this.realmId = realmId;
-        this.clientId = clientId;
-        this.offline = offline;
-        this.timestamp = Time.currentTime();
-    }
-
-    @Override
-    public K getId() {
-        return this.id;
-    }
-
-    @Override
-    public boolean isUpdated() {
-        return this.updated;
-    }
-
-    public String getRealmId() {
-        return realmId;
-    }
-
-    public void setRealmId(String realmId) {
-        this.updated |= !Objects.equals(this.realmId, realmId);
-        this.realmId = realmId;
-    }
-
-    public String getClientId() {
-        return clientId;
-    }
-
-    public void setClientId(String clientId) {
-        this.updated |= !Objects.equals(this.clientId, clientId);
-        this.clientId = clientId;
-    }
-
-    public String getUserSessionId() {
-        return userSessionId;
-    }
-
-    public void setUserSessionId(String userSessionId) {
-        this.updated |= !Objects.equals(this.userSessionId, userSessionId);
-        this.userSessionId = userSessionId;
-    }
-
-    public String getAuthMethod() {
-        return authMethod;
-    }
-
-    public void setAuthMethod(String authMethod) {
-        this.updated |= !Objects.equals(this.authMethod, authMethod);
-        this.authMethod = authMethod;
-    }
-
-    public String getRedirectUri() {
-        return redirectUri;
-    }
-
-    public void setRedirectUri(String redirectUri) {
-        this.updated |= !Objects.equals(this.redirectUri, redirectUri);
-        this.redirectUri = redirectUri;
-    }
-
-    public int getTimestamp() {
-        return timestamp;
-    }
-
-    public void setTimestamp(int timestamp) {
-        this.updated |= this.timestamp != timestamp;
-        this.timestamp = timestamp;
-    }
-
-    public long getExpiration() {
-        return expiration;
-    }
-
-    public void setExpiration(long expiration) {
-        this.updated |= this.expiration != expiration;
-        this.expiration = expiration;
-    }
-
-    public String getAction() {
-        return action;
-    }
-
-    public void setAction(String action) {
-        this.updated |= !Objects.equals(this.action, action);
-        this.action = action;
-    }
-
-    public Map getNotes() {
-        return notes;
-    }
-
-    public void setNotes(Map notes) {
-        this.updated |= !Objects.equals(this.notes, notes);
-        this.notes = notes;
-    }
-
-    public String removeNote(String name) {
-        String note = this.notes.remove(name);
-        this.updated |= note != null;
-        return note;
-    }
-
-    public void addNote(String name, String value) {
-        this.updated |= !Objects.equals(this.notes.put(name, value), value);
-    }
-
-    public String getCurrentRefreshToken() {
-        return currentRefreshToken;
-    }
-
-    public void setCurrentRefreshToken(String currentRefreshToken) {
-        this.updated |= !Objects.equals(this.currentRefreshToken, currentRefreshToken);
-        this.currentRefreshToken = currentRefreshToken;
-    }
-
-    public int getCurrentRefreshTokenUseCount() {
-        return currentRefreshTokenUseCount;
-    }
-
-    public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) {
-        this.updated |= this.currentRefreshTokenUseCount != currentRefreshTokenUseCount;
-        this.currentRefreshTokenUseCount = currentRefreshTokenUseCount;
-    }
-
-    public boolean isOffline() {
-        return offline;
-    }
-
-    public void setOffline(boolean offline) {
-        this.updated |= this.offline != offline;
-        this.offline = offline;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s@%08x", getId(), hashCode());
-    }
-}
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionModel.java b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionModel.java
index 47942cb1c2..4ecde34e65 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionModel.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractAuthenticatedClientSessionModel.java
@@ -28,15 +28,15 @@ import java.util.Objects;
 /**
  * @author Martin Kanis
  */
-public abstract class AbstractAuthenticatedClientSessionModel implements AuthenticatedClientSessionModel {
+public abstract class AbstractAuthenticatedClientSessionModel implements AuthenticatedClientSessionModel {
     protected final KeycloakSession session;
     protected final RealmModel realm;
     protected ClientModel client;
     protected UserSessionModel userSession;
-    protected final E entity;
+    protected final MapAuthenticatedClientSessionEntity entity;
 
     public AbstractAuthenticatedClientSessionModel(KeycloakSession session, RealmModel realm, ClientModel client,
-                                                   UserSessionModel userSession, E entity) {
+                                                   UserSessionModel userSession, MapAuthenticatedClientSessionEntity entity) {
         Objects.requireNonNull(entity, "entity");
         Objects.requireNonNull(realm, "realm");
         Objects.requireNonNull(client, "client");
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionEntity.java
deleted file mode 100644
index 16b5db18e6..0000000000
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionEntity.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright 2021 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.keycloak.models.map.userSession;
-
-import org.keycloak.common.util.Time;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.map.common.AbstractEntity;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Martin Kanis
- */
-public abstract class AbstractUserSessionEntity implements AbstractEntity {
-    private K id;
-
-    private String realmId;
-
-    /**
-     * Flag signalizing that any of the setters has been meaningfully used.
-     */
-    protected boolean updated;
-
-    private String userId;
-
-    private String brokerSessionId;
-    private String brokerUserId;
-
-    private String loginUsername;
-
-    private String ipAddress;
-
-    private String authMethod;
-
-    private boolean rememberMe;
-
-    private int started;
-
-    private int lastSessionRefresh;
-
-    private long expiration;
-
-    private Map notes = new ConcurrentHashMap<>();
-
-    private UserSessionModel.State state;
-
-    private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT;
-
-    private Map authenticatedClientSessions = new ConcurrentHashMap<>();
-
-    private boolean offline;
-
-    public AbstractUserSessionEntity() {
-        this.id = null;
-        this.realmId = null;
-    }
-
-    public AbstractUserSessionEntity(K id, String realmId) {
-        Objects.requireNonNull(id, "id");
-        Objects.requireNonNull(realmId, "realmId");
-
-        this.id = id;
-        this.realmId = realmId;
-    }
-
-    public AbstractUserSessionEntity(K id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
-                                     String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
-                                     boolean offline) {
-        this.id = id;
-        this.realmId = realm.getId();
-        this.userId = user.getId();
-        this.loginUsername = loginUsername;
-        this.ipAddress = ipAddress;
-        this.authMethod = authMethod;
-        this.rememberMe = rememberMe;
-        this.brokerSessionId = brokerSessionId;
-        this.brokerUserId = brokerUserId;
-        this.started = Time.currentTime();
-        this.lastSessionRefresh = started;
-        this.offline = offline;
-    }
-
-    @Override
-    public K getId() {
-        return this.id;
-    }
-
-    @Override
-    public boolean isUpdated() {
-        return this.updated;
-    }
-
-    public String getRealmId() {
-        return realmId;
-    }
-
-    public void setRealmId(String realmId) {
-        this.updated |= !Objects.equals(this.realmId, realmId);
-        this.realmId = realmId;
-    }
-
-    public String getUserId() {
-        return userId;
-    }
-
-    public void setUserId(String userId) {
-        this.updated |= !Objects.equals(this.userId, userId);
-        this.userId = userId;
-    }
-
-    public String getBrokerSessionId() {
-        return brokerSessionId;
-    }
-
-    public void setBrokerSessionId(String brokerSessionId) {
-        this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId);
-        this.brokerSessionId = brokerSessionId;
-    }
-
-    public String getBrokerUserId() {
-        return brokerUserId;
-    }
-
-    public void setBrokerUserId(String brokerUserId) {
-        this.updated |= !Objects.equals(this.brokerUserId, brokerUserId);
-        this.brokerUserId = brokerUserId;
-    }
-
-    public String getLoginUsername() {
-        return loginUsername;
-    }
-
-    public void setLoginUsername(String loginUsername) {
-        this.updated |= !Objects.equals(this.loginUsername, loginUsername);
-        this.loginUsername = loginUsername;
-    }
-
-    public String getIpAddress() {
-        return ipAddress;
-    }
-
-    public void setIpAddress(String ipAddress) {
-        this.updated |= !Objects.equals(this.ipAddress, ipAddress);
-        this.ipAddress = ipAddress;
-    }
-
-    public String getAuthMethod() {
-        return authMethod;
-    }
-
-    public void setAuthMethod(String authMethod) {
-        this.updated |= !Objects.equals(this.authMethod, authMethod);
-        this.authMethod = authMethod;
-    }
-
-    public boolean isRememberMe() {
-        return rememberMe;
-    }
-
-    public void setRememberMe(boolean rememberMe) {
-        this.updated |= this.rememberMe != rememberMe;
-        this.rememberMe = rememberMe;
-    }
-
-    public int getStarted() {
-        return started;
-    }
-
-    public void setStarted(int started) {
-        this.updated |= this.started != started;
-        this.started = started;
-    }
-
-    public int getLastSessionRefresh() {
-        return lastSessionRefresh;
-    }
-
-    public void setLastSessionRefresh(int lastSessionRefresh) {
-        this.updated |= this.lastSessionRefresh != lastSessionRefresh;
-        this.lastSessionRefresh = lastSessionRefresh;
-    }
-
-    public long getExpiration() {
-        return expiration;
-    }
-
-    public void setExpiration(long expiration) {
-        this.updated |= this.expiration != expiration;
-        this.expiration = expiration;
-    }
-
-    public Map getNotes() {
-        return notes;
-    }
-
-    public String getNote(String name) {
-        return notes.get(name);
-    }
-
-    public void setNotes(Map notes) {
-        this.updated |= !Objects.equals(this.notes, notes);
-        this.notes = notes;
-    }
-
-    public String removeNote(String name) {
-        String note = this.notes.remove(name);
-        this.updated |= note != null;
-        return note;
-    }
-
-    public void addNote(String name, String value) {
-        this.updated |= !Objects.equals(this.notes.put(name, value), value);
-    }
-
-    public UserSessionModel.State getState() {
-        return state;
-    }
-
-    public void setState(UserSessionModel.State state) {
-        this.updated |= !Objects.equals(this.state, state);
-        this.state = state;
-    }
-
-    public Map getAuthenticatedClientSessions() {
-        return authenticatedClientSessions;
-    }
-
-    public void setAuthenticatedClientSessions(Map authenticatedClientSessions) {
-        this.updated |= !Objects.equals(this.authenticatedClientSessions, authenticatedClientSessions);
-        this.authenticatedClientSessions = authenticatedClientSessions;
-    }
-
-    public void addAuthenticatedClientSession(String clientId, K clientSessionId) {
-        this.updated |= !Objects.equals(this.authenticatedClientSessions.put(clientId, clientSessionId), clientSessionId);
-    }
-
-    public K removeAuthenticatedClientSession(String clientId) {
-        K entity = this.authenticatedClientSessions.remove(clientId);
-        this.updated |= entity != null;
-        return entity;
-    }
-
-    public void clearAuthenticatedClientSessions() {
-        this.updated |= !authenticatedClientSessions.isEmpty();
-        this.authenticatedClientSessions.clear();
-    }
-
-    public boolean isOffline() {
-        return offline;
-    }
-
-    public void setOffline(boolean offline) {
-        this.updated |= this.offline != offline;
-        this.offline = offline;
-    }
-
-    public UserSessionModel.SessionPersistenceState getPersistenceState() {
-        return persistenceState;
-    }
-
-    public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) {
-        this.updated |= !Objects.equals(this.persistenceState, persistenceState);
-        this.persistenceState = persistenceState;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s@%08x", getId(), hashCode());
-    }
-}
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionModel.java b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionModel.java
index 44b5b76ccb..34d3271fab 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionModel.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/AbstractUserSessionModel.java
@@ -26,12 +26,12 @@ import java.util.Objects;
 /**
  * @author Martin Kanis
  */
-public abstract class AbstractUserSessionModel implements UserSessionModel {
+public abstract class AbstractUserSessionModel implements UserSessionModel {
     protected final KeycloakSession session;
     protected final RealmModel realm;
-    protected final E entity;
+    protected final MapUserSessionEntity entity;
 
-    public AbstractUserSessionModel(KeycloakSession session, RealmModel realm, E entity) {
+    public AbstractUserSessionModel(KeycloakSession session, RealmModel realm, MapUserSessionEntity entity) {
         Objects.requireNonNull(entity, "entity");
         Objects.requireNonNull(realm, "realm");
 
@@ -39,18 +39,4 @@ public abstract class AbstractUserSessionModel impleme
         this.realm = realm;
         this.entity = entity;
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof UserSessionModel)) return false;
-
-        UserSessionModel that = (UserSessionModel) o;
-        return Objects.equals(that.getId(), getId());
-    }
-
-    @Override
-    public int hashCode() {
-        return getId().hashCode();
-    }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java
index c9bded203b..931daaaf58 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java
@@ -26,18 +26,13 @@ import java.util.Map;
 /**
  * @author Martin Kanis
  */
-public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthenticatedClientSessionModel {
+public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthenticatedClientSessionModel {
 
     public MapAuthenticatedClientSessionAdapter(KeycloakSession session, RealmModel realm, ClientModel client,
-                                                UserSessionModel userSession, MapAuthenticatedClientSessionEntity entity) {
+                                                UserSessionModel userSession, MapAuthenticatedClientSessionEntity entity) {
         super(session, realm, client, userSession, entity);
     }
 
-    @Override
-    public String getId() {
-        return entity.getId().toString();
-    }
-
     @Override
     public int getTimestamp() {
         return entity.getTimestamp();
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java
index e9eed706fb..41ea5a2d9f 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java
@@ -16,18 +16,190 @@
  */
 package org.keycloak.models.map.userSession;
 
-import java.util.UUID;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.map.common.AbstractEntity;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @author Martin Kanis
  */
-public class MapAuthenticatedClientSessionEntity extends AbstractAuthenticatedClientSessionEntity {
+public class MapAuthenticatedClientSessionEntity implements AbstractEntity {
 
-    protected MapAuthenticatedClientSessionEntity() {
-        super();
+    private K id;
+    private String userSessionId;
+    private String realmId;
+    private String clientId;
+
+    /**
+     * Flag signalizing that any of the setters has been meaningfully used.
+     */
+    protected boolean updated;
+
+    private String authMethod;
+    private String redirectUri;
+    private volatile int timestamp;
+    private long expiration;
+    private String action;
+
+    private Map notes = new ConcurrentHashMap<>();
+
+    private String currentRefreshToken;
+    private int currentRefreshTokenUseCount;
+
+    private boolean offline;
+
+    public MapAuthenticatedClientSessionEntity() {
+        this.id = null;
+        this.realmId = null;
     }
 
-    public MapAuthenticatedClientSessionEntity(UUID id, String userSessionId, String realmId, String clientId, boolean offline) {
-        super(id, userSessionId, realmId, clientId, offline);
+    public MapAuthenticatedClientSessionEntity(K id, String userSessionId, String realmId, String clientId, boolean offline) {
+        Objects.requireNonNull(id, "id");
+        Objects.requireNonNull(userSessionId, "userSessionId");
+        Objects.requireNonNull(realmId, "realmId");
+        Objects.requireNonNull(clientId, "clientId");
+
+        this.id = id;
+        this.userSessionId = userSessionId;
+        this.realmId = realmId;
+        this.clientId = clientId;
+        this.offline = offline;
+        this.timestamp = Time.currentTime();
+    }
+
+    @Override
+    public K getId() {
+        return this.id;
+    }
+
+    @Override
+    public boolean isUpdated() {
+        return this.updated;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.updated |= !Objects.equals(this.realmId, realmId);
+        this.realmId = realmId;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.updated |= !Objects.equals(this.clientId, clientId);
+        this.clientId = clientId;
+    }
+
+    public String getUserSessionId() {
+        return userSessionId;
+    }
+
+    public void setUserSessionId(String userSessionId) {
+        this.updated |= !Objects.equals(this.userSessionId, userSessionId);
+        this.userSessionId = userSessionId;
+    }
+
+    public String getAuthMethod() {
+        return authMethod;
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.updated |= !Objects.equals(this.authMethod, authMethod);
+        this.authMethod = authMethod;
+    }
+
+    public String getRedirectUri() {
+        return redirectUri;
+    }
+
+    public void setRedirectUri(String redirectUri) {
+        this.updated |= !Objects.equals(this.redirectUri, redirectUri);
+        this.redirectUri = redirectUri;
+    }
+
+    public int getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(int timestamp) {
+        this.updated |= this.timestamp != timestamp;
+        this.timestamp = timestamp;
+    }
+
+    public long getExpiration() {
+        return expiration;
+    }
+
+    public void setExpiration(long expiration) {
+        this.updated |= this.expiration != expiration;
+        this.expiration = expiration;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.updated |= !Objects.equals(this.action, action);
+        this.action = action;
+    }
+
+    public Map getNotes() {
+        return notes;
+    }
+
+    public void setNotes(Map notes) {
+        this.updated |= !Objects.equals(this.notes, notes);
+        this.notes = notes;
+    }
+
+    public String removeNote(String name) {
+        String note = this.notes.remove(name);
+        this.updated |= note != null;
+        return note;
+    }
+
+    public void addNote(String name, String value) {
+        this.updated |= !Objects.equals(this.notes.put(name, value), value);
+    }
+
+    public String getCurrentRefreshToken() {
+        return currentRefreshToken;
+    }
+
+    public void setCurrentRefreshToken(String currentRefreshToken) {
+        this.updated |= !Objects.equals(this.currentRefreshToken, currentRefreshToken);
+        this.currentRefreshToken = currentRefreshToken;
+    }
+
+    public int getCurrentRefreshTokenUseCount() {
+        return currentRefreshTokenUseCount;
+    }
+
+    public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) {
+        this.updated |= this.currentRefreshTokenUseCount != currentRefreshTokenUseCount;
+        this.currentRefreshTokenUseCount = currentRefreshTokenUseCount;
+    }
+
+    public boolean isOffline() {
+        return offline;
+    }
+
+    public void setOffline(boolean offline) {
+        this.updated |= this.offline != offline;
+        this.offline = offline;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s@%08x", getId(), hashCode());
     }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java
index 580fe28e84..c3b7555933 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java
@@ -23,28 +23,25 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 
+import org.keycloak.models.UserSessionModel;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @author Martin Kanis
  */
-public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
+public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
 
-    public MapUserSessionAdapter(KeycloakSession session, RealmModel realm, MapUserSessionEntity entity) {
+    public MapUserSessionAdapter(KeycloakSession session, RealmModel realm, MapUserSessionEntity entity) {
         super(session, realm, entity);
     }
 
-    @Override
-    public String getId() {
-        return entity.getId().toString();
-    }
-
     @Override
     public RealmModel getRealm() {
         return realm;
@@ -134,7 +131,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModelMartin Kanis
  */
-public class MapUserSessionEntity extends AbstractUserSessionEntity {
-    protected MapUserSessionEntity() {
-        super();
+public class MapUserSessionEntity implements AbstractEntity {
+    private K id;
+
+    private String realmId;
+
+    /**
+     * Flag signalizing that any of the setters has been meaningfully used.
+     */
+    protected boolean updated;
+
+    private String userId;
+
+    private String brokerSessionId;
+    private String brokerUserId;
+
+    private String loginUsername;
+
+    private String ipAddress;
+
+    private String authMethod;
+
+    private boolean rememberMe;
+
+    private int started;
+
+    private int lastSessionRefresh;
+
+    private long expiration;
+
+    private Map notes = new ConcurrentHashMap<>();
+
+    private UserSessionModel.State state;
+
+    private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT;
+
+    private Map authenticatedClientSessions = new ConcurrentHashMap<>();
+
+    private boolean offline;
+
+    public MapUserSessionEntity() {
+        this.id = null;
+        this.realmId = null;
     }
 
-    public MapUserSessionEntity(UUID id, String realmId) {
-        super(id, realmId);
+    public MapUserSessionEntity(K id, String realmId) {
+        Objects.requireNonNull(id, "id");
+        Objects.requireNonNull(realmId, "realmId");
+
+        this.id = id;
+        this.realmId = realmId;
     }
 
-    public MapUserSessionEntity(UUID id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
-                                String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
-                                boolean offline) {
-        super(id, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, offline);
+    public MapUserSessionEntity(K id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
+                                     String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
+                                     boolean offline) {
+        this.id = id;
+        this.realmId = realm.getId();
+        this.userId = user.getId();
+        this.loginUsername = loginUsername;
+        this.ipAddress = ipAddress;
+        this.authMethod = authMethod;
+        this.rememberMe = rememberMe;
+        this.brokerSessionId = brokerSessionId;
+        this.brokerUserId = brokerUserId;
+        this.started = Time.currentTime();
+        this.lastSessionRefresh = started;
+        this.offline = offline;
+    }
+
+    @Override
+    public K getId() {
+        return this.id;
+    }
+
+    @Override
+    public boolean isUpdated() {
+        return this.updated;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.updated |= !Objects.equals(this.realmId, realmId);
+        this.realmId = realmId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.updated |= !Objects.equals(this.userId, userId);
+        this.userId = userId;
+    }
+
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId);
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.updated |= !Objects.equals(this.brokerUserId, brokerUserId);
+        this.brokerUserId = brokerUserId;
+    }
+
+    public String getLoginUsername() {
+        return loginUsername;
+    }
+
+    public void setLoginUsername(String loginUsername) {
+        this.updated |= !Objects.equals(this.loginUsername, loginUsername);
+        this.loginUsername = loginUsername;
+    }
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.updated |= !Objects.equals(this.ipAddress, ipAddress);
+        this.ipAddress = ipAddress;
+    }
+
+    public String getAuthMethod() {
+        return authMethod;
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.updated |= !Objects.equals(this.authMethod, authMethod);
+        this.authMethod = authMethod;
+    }
+
+    public boolean isRememberMe() {
+        return rememberMe;
+    }
+
+    public void setRememberMe(boolean rememberMe) {
+        this.updated |= this.rememberMe != rememberMe;
+        this.rememberMe = rememberMe;
+    }
+
+    public int getStarted() {
+        return started;
+    }
+
+    public void setStarted(int started) {
+        this.updated |= this.started != started;
+        this.started = started;
+    }
+
+    public int getLastSessionRefresh() {
+        return lastSessionRefresh;
+    }
+
+    public void setLastSessionRefresh(int lastSessionRefresh) {
+        this.updated |= this.lastSessionRefresh != lastSessionRefresh;
+        this.lastSessionRefresh = lastSessionRefresh;
+    }
+
+    public long getExpiration() {
+        return expiration;
+    }
+
+    public void setExpiration(long expiration) {
+        this.updated |= this.expiration != expiration;
+        this.expiration = expiration;
+    }
+
+    public Map getNotes() {
+        return notes;
+    }
+
+    public String getNote(String name) {
+        return notes.get(name);
+    }
+
+    public void setNotes(Map notes) {
+        this.updated |= !Objects.equals(this.notes, notes);
+        this.notes = notes;
+    }
+
+    public String removeNote(String name) {
+        String note = this.notes.remove(name);
+        this.updated |= note != null;
+        return note;
+    }
+
+    public void addNote(String name, String value) {
+        this.updated |= !Objects.equals(this.notes.put(name, value), value);
+    }
+
+    public UserSessionModel.State getState() {
+        return state;
+    }
+
+    public void setState(UserSessionModel.State state) {
+        this.updated |= !Objects.equals(this.state, state);
+        this.state = state;
+    }
+
+    public Map getAuthenticatedClientSessions() {
+        return authenticatedClientSessions;
+    }
+
+    public void setAuthenticatedClientSessions(Map authenticatedClientSessions) {
+        this.updated |= !Objects.equals(this.authenticatedClientSessions, authenticatedClientSessions);
+        this.authenticatedClientSessions = authenticatedClientSessions;
+    }
+
+    public void addAuthenticatedClientSession(String clientId, String clientSessionId) {
+        this.updated |= !Objects.equals(this.authenticatedClientSessions.put(clientId, clientSessionId), clientSessionId);
+    }
+
+    public String removeAuthenticatedClientSession(String clientId) {
+        String entity = this.authenticatedClientSessions.remove(clientId);
+        this.updated |= entity != null;
+        return entity;
+    }
+
+    public void clearAuthenticatedClientSessions() {
+        this.updated |= !authenticatedClientSessions.isEmpty();
+        this.authenticatedClientSessions.clear();
+    }
+
+    public boolean isOffline() {
+        return offline;
+    }
+
+    public void setOffline(boolean offline) {
+        this.updated |= this.offline != offline;
+        this.offline = offline;
+    }
+
+    public UserSessionModel.SessionPersistenceState getPersistenceState() {
+        return persistenceState;
+    }
+
+    public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) {
+        this.updated |= !Objects.equals(this.persistenceState, persistenceState);
+        this.persistenceState = persistenceState;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s@%08x", getId(), hashCode());
     }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java
index cd54773cda..b9c2236a94 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java
@@ -39,7 +39,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -56,22 +55,22 @@ import static org.keycloak.utils.StreamsUtil.paginatedStream;
 /**
  * @author Martin Kanis
  */
-public class MapUserSessionProvider implements UserSessionProvider {
+public class MapUserSessionProvider implements UserSessionProvider {
 
     private static final Logger LOG = Logger.getLogger(MapUserSessionProvider.class);
     private final KeycloakSession session;
-    protected final MapKeycloakTransaction userSessionTx;
-    protected final MapKeycloakTransaction clientSessionTx;
-    private final MapStorage userSessionStore;
-    private final MapStorage clientSessionStore;
+    protected final MapKeycloakTransaction, UserSessionModel> userSessionTx;
+    protected final MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTx;
+    private final MapStorage, UserSessionModel> userSessionStore;
+    private final MapStorage, AuthenticatedClientSessionModel> clientSessionStore;
 
     /**
      * Storage for transient user sessions which lifespan is limited to one request.
      */
-    private final Map transientUserSessions = new HashMap<>();
+    private final Map> transientUserSessions = new HashMap<>();
 
-    public MapUserSessionProvider(KeycloakSession session, MapStorage userSessionStore,
-                                  MapStorage clientSessionStore) {
+    public MapUserSessionProvider(KeycloakSession session, MapStorage, UserSessionModel> userSessionStore,
+                                  MapStorage, AuthenticatedClientSessionModel> clientSessionStore) {
         this.session = session;
         this.userSessionStore = userSessionStore;
         this.clientSessionStore = clientSessionStore;
@@ -82,7 +81,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
         session.getTransactionManager().enlistAfterCompletion(clientSessionTx);
     }
 
-    private Function userEntityToAdapterFunc(RealmModel realm) {
+    private Function, UserSessionModel> userEntityToAdapterFunc(RealmModel realm) {
         // Clone entity before returning back, to avoid giving away a reference to the live object to the caller
         return (origEntity) -> {
             if (origEntity.getExpiration() <= Time.currentTime()) {
@@ -92,12 +91,16 @@ public class MapUserSessionProvider implements UserSessionProvider {
                 userSessionTx.delete(origEntity.getId());
                 return null;
             } else {
-                return new MapUserSessionAdapter(session, realm,
+                return new MapUserSessionAdapter(session, realm,
                         Objects.equals(origEntity.getPersistenceState(), TRANSIENT) ? origEntity : registerEntityForChanges(origEntity)) {
+                    @Override
+                    public String getId() {
+                        return userSessionStore.getKeyConvertor().keyToString(entity.getId());
+                    }
 
                     @Override
-                    public void removeAuthenticatedClientSessions(Collection removedClientUUIDS) {
-                        removedClientUUIDS.forEach(entity::removeAuthenticatedClientSession);
+                    public void removeAuthenticatedClientSessions(Collection removedClientUKS) {
+                        removedClientUKS.forEach(entity::removeAuthenticatedClientSession);
                     }
 
                     @Override
@@ -111,7 +114,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
         };
     }
 
-    private Function clientEntityToAdapterFunc(RealmModel realm,
+    private Function, AuthenticatedClientSessionModel> clientEntityToAdapterFunc(RealmModel realm,
                                                                                                                      ClientModel client,
                                                                                                                      UserSessionModel userSession) {
         // Clone entity before returning back, to avoid giving away a reference to the live object to the caller
@@ -121,7 +124,12 @@ public class MapUserSessionProvider implements UserSessionProvider {
                 clientSessionTx.delete(origEntity.getId());
                 return null;
             } else {
-                return new MapAuthenticatedClientSessionAdapter(session, realm, client, userSession, registerEntityForChanges(origEntity)) {
+                return new MapAuthenticatedClientSessionAdapter(session, realm, client, userSession, registerEntityForChanges(origEntity)) {
+                    @Override
+                    public String getId() {
+                        return clientSessionStore.getKeyConvertor().keyToString(entity.getId());
+                    }
+
                     @Override
                     public void detachFromUserSession() {
                         this.userSession = null;
@@ -140,15 +148,15 @@ public class MapUserSessionProvider implements UserSessionProvider {
         };
     }
 
-    private MapUserSessionEntity registerEntityForChanges(MapUserSessionEntity origEntity) {
-        MapUserSessionEntity res = userSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
-        userSessionTx.updateIfChanged(origEntity.getId(), res, MapUserSessionEntity::isUpdated);
+    private MapUserSessionEntity registerEntityForChanges(MapUserSessionEntity origEntity) {
+        MapUserSessionEntity res = userSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
+        userSessionTx.updateIfChanged(origEntity.getId(), res, MapUserSessionEntity::isUpdated);
         return res;
     }
 
-    private MapAuthenticatedClientSessionEntity registerEntityForChanges(MapAuthenticatedClientSessionEntity origEntity) {
-        MapAuthenticatedClientSessionEntity res = clientSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
-        clientSessionTx.updateIfChanged(origEntity.getId(), res, MapAuthenticatedClientSessionEntity::isUpdated);
+    private MapAuthenticatedClientSessionEntity registerEntityForChanges(MapAuthenticatedClientSessionEntity origEntity) {
+        MapAuthenticatedClientSessionEntity res = clientSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
+        clientSessionTx.updateIfChanged(origEntity.getId(), res, MapAuthenticatedClientSessionEntity::isUpdated);
         return res;
     }
 
@@ -159,28 +167,28 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
     @Override
     public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
-        MapAuthenticatedClientSessionEntity entity =
-                new MapAuthenticatedClientSessionEntity(UUID.randomUUID(), userSession.getId(), realm.getId(), client.getId(), false);
+        MapAuthenticatedClientSessionEntity entity =
+                new MapAuthenticatedClientSessionEntity<>(clientSessionStore.getKeyConvertor().yieldNewUniqueKey(), userSession.getId(), realm.getId(), client.getId(), false);
         setClientSessionExpiration(entity, realm, client);
 
         LOG.tracef("createClientSession(%s, %s, %s)%s", realm, client, userSession, getShortStackTrace());
 
         clientSessionTx.create(entity.getId(), entity);
 
-        MapUserSessionEntity userSessionEntity = getUserSessionById(UUID.fromString(userSession.getId()));
+        MapUserSessionEntity userSessionEntity = getUserSessionById(userSessionStore.getKeyConvertor().fromString(userSession.getId()));
 
         if (userSessionEntity == null) {
             throw new IllegalStateException("User session entity does not exist: " + userSession.getId());
         }
 
-        userSessionEntity.addAuthenticatedClientSession(client.getId(), entity.getId());
+        userSessionEntity.addAuthenticatedClientSession(client.getId(), clientSessionStore.getKeyConvertor().keyToString(entity.getId()));
 
         return clientEntityToAdapterFunc(realm, client, userSession).apply(entity);
     }
 
     @Override
     public AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client,
-                                                            UUID clientSessionId, boolean offline) {
+                                                            String clientSessionId, boolean offline) {
         LOG.tracef("getClientSession(%s, %s, %s, %s)%s", userSession, client,
                 clientSessionId, offline, getShortStackTrace());
 
@@ -190,8 +198,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
             return null;
         }
 
+        CK ck = clientSessionStore.getKeyConvertor().fromStringSafe(clientSessionId);
         ModelCriteriaBuilder mcb = clientSessionStore.createCriteriaBuilder()
-                .compare(AuthenticatedClientSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, clientSessionId)
+                .compare(AuthenticatedClientSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, ck)
                 .compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, ModelCriteriaBuilder.Operator.EQ, userSession.getId())
                 .compare(AuthenticatedClientSessionModel.SearchableFields.REALM_ID, ModelCriteriaBuilder.Operator.EQ, userSession.getRealm().getId())
                 .compare(AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, ModelCriteriaBuilder.Operator.EQ, client.getId())
@@ -214,11 +223,11 @@ public class MapUserSessionProvider implements UserSessionProvider {
     public UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername,
                                               String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId,
                                               String brokerUserId, UserSessionModel.SessionPersistenceState persistenceState) {
-        final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
+        final UK entityId = id == null ? userSessionStore.getKeyConvertor().yieldNewUniqueKey(): userSessionStore.getKeyConvertor().fromString(id);
 
         LOG.tracef("createUserSession(%s, %s, %s, %s)%s", id, realm, loginUsername, persistenceState, getShortStackTrace());
 
-        MapUserSessionEntity entity = new MapUserSessionEntity(entityId, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false);
+        MapUserSessionEntity entity = new MapUserSessionEntity<>(entityId, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false);
         entity.setPersistenceState(persistenceState);
         setUserSessionExpiration(entity, realm);
 
@@ -247,12 +256,12 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         LOG.tracef("getUserSession(%s, %s)%s", realm, id, getShortStackTrace());
 
-        UUID uuid = toUUID(id);
+        UK uuid = userSessionStore.getKeyConvertor().fromStringSafe(id);
         if (uuid == null) {
             return null;
         }
 
-        MapUserSessionEntity userSessionEntity = transientUserSessions.get(uuid);
+        MapUserSessionEntity userSessionEntity = transientUserSessions.get(uuid);
         if (userSessionEntity != null) {
             return userEntityToAdapterFunc(realm).apply(userSessionEntity);
         }
@@ -371,12 +380,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
     public void removeUserSession(RealmModel realm, UserSessionModel session) {
         Objects.requireNonNull(session, "The provided user session can't be null!");
 
+        UK uk = userSessionStore.getKeyConvertor().fromString(session.getId());
         ModelCriteriaBuilder mcb = realmAndOfflineCriteriaBuilder(realm, false)
-                .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(session.getId()));
+                .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
 
         LOG.tracef("removeUserSession(%s, %s)%s", realm, session, getShortStackTrace());
 
-        userSessionTx.delete(UUID.randomUUID(), mcb);
+        userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
     }
 
     @Override
@@ -387,7 +397,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         LOG.tracef("removeUserSessions(%s, %s)%s", realm, user, getShortStackTrace());
 
-        userSessionTx.delete(UUID.randomUUID(), mcb);
+        userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
     }
 
     @Override
@@ -406,7 +416,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         LOG.tracef("removeUserSessions(%s)%s", realm, getShortStackTrace());
 
-        userSessionTx.delete(UUID.randomUUID(), mcb);
+        userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
     }
 
     @Override
@@ -425,7 +435,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
     public UserSessionModel createOfflineUserSession(UserSessionModel userSession) {
         LOG.tracef("createOfflineUserSession(%s)%s", userSession, getShortStackTrace());
 
-        MapUserSessionEntity offlineUserSession = createUserSessionEntityInstance(userSession, true);
+        MapUserSessionEntity offlineUserSession = createUserSessionEntityInstance(userSession, true);
 
         // set a reference for the offline user session to the original online user session
         userSession.setNote(CORRESPONDING_SESSION_ID, offlineUserSession.getId().toString());
@@ -458,12 +468,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         ModelCriteriaBuilder mcb;
         if (userSession.isOffline()) {
-            userSessionTx.delete(UUID.fromString(userSession.getId()));
+            userSessionTx.delete(userSessionStore.getKeyConvertor().fromString(userSession.getId()));
         } else if (userSession.getNote(CORRESPONDING_SESSION_ID) != null) {
-                mcb = realmAndOfflineCriteriaBuilder(realm, true)
-                        .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(userSession.getNote(CORRESPONDING_SESSION_ID)));
-                userSessionTx.delete(UUID.randomUUID(), mcb);
-                userSession.removeNote(CORRESPONDING_SESSION_ID);
+            UK uk = userSessionStore.getKeyConvertor().fromString(userSession.getNote(CORRESPONDING_SESSION_ID));
+            mcb = realmAndOfflineCriteriaBuilder(realm, true)
+                    .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
+            userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
+            userSession.removeNote(CORRESPONDING_SESSION_ID);
         }
     }
 
@@ -472,13 +483,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
                                                                       UserSessionModel offlineUserSession) {
         LOG.tracef("createOfflineClientSession(%s, %s)%s", clientSession, offlineUserSession, getShortStackTrace());
 
-        MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
+        MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
         clientSessionEntity.setTimestamp(Time.currentTime());
         setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient());
 
-        Optional userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst();
+        Optional> userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst();
         if (userSessionEntity.isPresent()) {
-            userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId());
+            userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionStore.getKeyConvertor().keyToString(clientSessionEntity.getId()));
         }
 
         clientSessionTx.create(clientSessionEntity.getId(), clientSessionEntity);
@@ -556,17 +567,17 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         persistentUserSessions.stream()
             .map(pus -> {
-                MapUserSessionEntity userSessionEntity = new MapUserSessionEntity(UUID.randomUUID(), pus.getRealm(), pus.getUser(),
+                MapUserSessionEntity userSessionEntity = new MapUserSessionEntity(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), pus.getRealm(), pus.getUser(),
                         pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(),
                         pus.isRememberMe(), pus.getBrokerSessionId(), pus.getBrokerUserId(), offline);
 
                 for (Map.Entry entry : pus.getAuthenticatedClientSessions().entrySet()) {
-                    MapAuthenticatedClientSessionEntity clientSession = createAuthenticatedClientSessionInstance(entry.getValue(), entry.getValue().getUserSession(), offline);
+                    MapAuthenticatedClientSessionEntity clientSession = createAuthenticatedClientSessionInstance(entry.getValue(), entry.getValue().getUserSession(), offline);
 
                     // Update timestamp to same value as userSession. LastSessionRefresh of userSession from DB will have correct value
                     clientSession.setTimestamp(userSessionEntity.getLastSessionRefresh());
 
-                    userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSession.getId());
+                    userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSessionStore.getKeyConvertor().keyToString(clientSession.getId()));
 
                     clientSessionTx.create(clientSession.getId(), clientSession);
                 }
@@ -581,8 +592,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
     }
 
-    private Stream getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
-        UUID uuid = toUUID(userSessionId);
+    private Stream> getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
+        UK uuid = userSessionStore.getKeyConvertor().fromStringSafe(userSessionId);
         if (uuid == null) {
             return Stream.empty();
         }
@@ -593,7 +604,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
                 .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid);
 
         // check if it's an offline user session
-        MapUserSessionEntity userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
+        MapUserSessionEntity userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
         if (userSessionEntity != null) {
             if (userSessionEntity.isOffline()) {
                 return Stream.of(userSessionEntity);
@@ -608,8 +619,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
         // it's online user session so lookup offline user session by corresponding session id reference
         String offlineUserSessionId = userSessionEntity.getNote(CORRESPONDING_SESSION_ID);
         if (offlineUserSessionId != null) {
+            UK uk = userSessionStore.getKeyConvertor().fromStringSafe(offlineUserSessionId);
             mcb = realmAndOfflineCriteriaBuilder(realm, true)
-                    .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(offlineUserSessionId));
+                    .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
             return userSessionTx.getUpdatedNotRemoved(mcb);
         }
 
@@ -622,18 +634,18 @@ public class MapUserSessionProvider implements UserSessionProvider {
                 .compare(UserSessionModel.SearchableFields.IS_OFFLINE, ModelCriteriaBuilder.Operator.EQ, offline);
     }
 
-    private MapUserSessionEntity getUserSessionById(UUID id) {
-        MapUserSessionEntity userSessionEntity = transientUserSessions.get(id);
+    private MapUserSessionEntity getUserSessionById(UK id) {
+        MapUserSessionEntity userSessionEntity = transientUserSessions.get(id);
 
         if (userSessionEntity == null) {
-            MapUserSessionEntity userSession = userSessionTx.read(id);
+            MapUserSessionEntity userSession = userSessionTx.read(id);
             return userSession != null ? registerEntityForChanges(userSession) : null;
         }
         return userSessionEntity;
     }
 
-    private MapUserSessionEntity createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) {
-        MapUserSessionEntity entity = new MapUserSessionEntity(UUID.randomUUID(), userSession.getRealm().getId());
+    private MapUserSessionEntity createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) {
+        MapUserSessionEntity entity = new MapUserSessionEntity(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), userSession.getRealm().getId());
 
         entity.setAuthMethod(userSession.getAuthMethod());
         entity.setBrokerSessionId(userSession.getBrokerSessionId());
@@ -655,9 +667,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
         return entity;
     }
 
-    private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession,
+    private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession,
                                                                                          UserSessionModel userSession, boolean offline) {
-        MapAuthenticatedClientSessionEntity entity = new MapAuthenticatedClientSessionEntity(UUID.randomUUID(),
+        MapAuthenticatedClientSessionEntity entity = new MapAuthenticatedClientSessionEntity(clientSessionStore.getKeyConvertor().yieldNewUniqueKey(),
                 userSession.getId(), clientSession.getRealm().getId(), clientSession.getClient().getId(), offline);
 
         entity.setAction(clientSession.getAction());
@@ -669,12 +681,4 @@ public class MapUserSessionProvider implements UserSessionProvider {
 
         return entity;
     }
-
-    private UUID toUUID(String id) {
-        try {
-            return UUID.fromString(id);
-        } catch (IllegalArgumentException ex) {
-            return null;
-        }
-    }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProviderFactory.java
index 4a12bdc98f..8eeaf49082 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProviderFactory.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProviderFactory.java
@@ -16,6 +16,8 @@
  */
 package org.keycloak.models.map.userSession;
 
+import org.keycloak.Config.Scope;
+import org.keycloak.component.AmphibianProviderFactory;
 import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -28,40 +30,79 @@ import org.keycloak.models.map.storage.MapStorage;
 import org.keycloak.models.map.storage.MapStorageProvider;
 import org.keycloak.models.map.storage.MapStorageProviderFactory;
 
-import java.util.UUID;
+import org.keycloak.models.map.storage.MapStorageSpi;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderEventListener;
+import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
 
 /**
  * @author Martin Kanis
  */
-public class MapUserSessionProviderFactory extends AbstractMapProviderFactory
-        implements UserSessionProviderFactory {
+public class MapUserSessionProviderFactory implements AmphibianProviderFactory, UserSessionProviderFactory, ProviderEventListener {
 
-    private MapStorage userSessionStore;
-    private MapStorage clientSessionStore;
+    public static final String CONFIG_STORAGE_USER_SESSIONS = "storage-user-sessions";
+    public static final String CONFIG_STORAGE_CLIENT_SESSIONS = "storage-client-sessions";
+    public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID;
+
+    private Scope storageConfigScopeUserSessions;
+    private Scope storageConfigScopeClientSessions;
+
+    private Runnable onClose;
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public void init(Scope config) {
+        storageConfigScopeUserSessions = config.scope(AbstractMapProviderFactory.CONFIG_STORAGE + "-user-sessions");
+        storageConfigScopeClientSessions = config.scope(AbstractMapProviderFactory.CONFIG_STORAGE + "-client-sessions");
+    }
 
     @Override
     public void postInit(KeycloakSessionFactory factory) {
-        MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
-        userSessionStore = sp.getStorage("userSessions", UUID.class, MapUserSessionEntity.class, UserSessionModel.class);
-        clientSessionStore = sp.getStorage("clientSessions", UUID.class, MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+        factory.register(this);
+        onClose = () -> factory.unregister(this);
+    }
 
-        factory.register(event -> {
-            if (event instanceof UserModel.UserRemovedEvent) {
-                UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
-
-                MapUserSessionProvider provider = MapUserSessionProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
-                provider.removeUserSessions(userRemovedEvent.getRealm(), userRemovedEvent.getUser());
-            }
-        });
+    @Override
+    public void close() {
+        AmphibianProviderFactory.super.close();
+        onClose.run();
     }
 
     @Override
     public void loadPersistentSessions(KeycloakSessionFactory sessionFactory, int maxErrors, int sessionsPerSegment) {
-
     }
 
     @Override
-    public MapUserSessionProvider create(KeycloakSession session) {
-        return new MapUserSessionProvider(session, userSessionStore, clientSessionStore);
+    public MapUserSessionProvider create(KeycloakSession session) {
+        MapStorageProviderFactory storageProviderFactoryUs = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
+          MapStorageProvider.class, storageConfigScopeUserSessions, MapStorageSpi.NAME);
+        final MapStorageProvider factoryUs = storageProviderFactoryUs.create(session);
+        MapStorage userSessionStore = factoryUs.getStorage(MapUserSessionEntity.class, UserSessionModel.class);
+
+        MapStorageProviderFactory storageProviderFactoryCs = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
+          MapStorageProvider.class, storageConfigScopeClientSessions, MapStorageSpi.NAME);
+        final MapStorageProvider factoryCs = storageProviderFactoryCs.create(session);
+        MapStorage clientSessionStore = factoryCs.getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+
+        return new MapUserSessionProvider<>(session, userSessionStore, clientSessionStore);
+    }
+
+    @Override
+    public String getHelpText() {
+        return "User session provider";
+    }
+
+    @Override
+    public void onEvent(ProviderEvent event) {
+        if (event instanceof UserModel.UserRemovedEvent) {
+            UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
+
+            MapUserSessionProvider provider = MapUserSessionProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
+            provider.removeUserSessions(userRemovedEvent.getRealm(), userRemovedEvent.getUser());
+        }
     }
 }
diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java b/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java
index 5da92eb6f5..a84776eec5 100644
--- a/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java
+++ b/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java
@@ -26,7 +26,7 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes;
  */
 public class SessionExpiration {
 
-    public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) {
+    public static  void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) {
         if (entity.isOffline()) {
             long sessionExpires = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout();
             if (realm.isOfflineSessionMaxLifespanEnabled()) {
@@ -99,7 +99,7 @@ public class SessionExpiration {
         }
     }
 
-    public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) {
+    public static  void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) {
         if (entity.isOffline()) {
             long sessionExpires = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout();
             if (realm.isOfflineSessionMaxLifespanEnabled()) {
diff --git a/model/map/src/test/java/org/keycloak/models/map/user/AbstractUserEntityCredentialsOrderTest.java b/model/map/src/test/java/org/keycloak/models/map/user/AbstractUserEntityCredentialsOrderTest.java
index 4c82afbdd9..9e58bad9c0 100644
--- a/model/map/src/test/java/org/keycloak/models/map/user/AbstractUserEntityCredentialsOrderTest.java
+++ b/model/map/src/test/java/org/keycloak/models/map/user/AbstractUserEntityCredentialsOrderTest.java
@@ -28,11 +28,11 @@ import java.util.stream.Collectors;
 
 public class AbstractUserEntityCredentialsOrderTest {
 
-    private AbstractUserEntity user;
+    private MapUserEntity user;
     
     @Before
     public void init() {
-        user = new AbstractUserEntity(1, "realmId") {};
+        user = new MapUserEntity(1, "realmId") {};
         
         for (int i = 1; i <= 5; i++) {
             UserCredentialEntity credentialModel = new UserCredentialEntity();
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java
index 53bfb256c8..2527665423 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java
@@ -26,6 +26,9 @@ import org.keycloak.provider.Spi;
  * @author Pedro Igor
  */
 public class StoreFactorySpi implements Spi {
+
+    public static final String NAME = "authorizationPersister";
+
     @Override
     public boolean isInternal() {
         return true;
@@ -33,7 +36,7 @@ public class StoreFactorySpi implements Spi {
 
     @Override
     public String getName() {
-        return "authorizationPersister";
+        return NAME;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/models/ServerInfoSpi.java b/server-spi-private/src/main/java/org/keycloak/models/ServerInfoSpi.java
index 59ec9bbe16..cc01650318 100644
--- a/server-spi-private/src/main/java/org/keycloak/models/ServerInfoSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/ServerInfoSpi.java
@@ -23,6 +23,8 @@ import org.keycloak.provider.Spi;
 
 public class ServerInfoSpi implements Spi {
 
+    public static final String NAME = "serverInfo";
+
     @Override
     public boolean isInternal() {
         return true;
@@ -30,7 +32,7 @@ public class ServerInfoSpi implements Spi {
 
     @Override
     public String getName() {
-        return "serverInfo";
+        return NAME;
     }
 
     @Override
diff --git a/server-spi/src/main/java/org/keycloak/models/UserLoginFailureModel.java b/server-spi/src/main/java/org/keycloak/models/UserLoginFailureModel.java
index 15a69b133e..8192f2cde7 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserLoginFailureModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserLoginFailureModel.java
@@ -31,6 +31,7 @@ public interface UserLoginFailureModel {
       public static final SearchableModelField USER_ID  = new SearchableModelField<>("userId", String.class);
    }
 
+   String getId();
    String getUserId();
    int getFailedLoginNotBefore();
    void setFailedLoginNotBefore(int notBefore);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index 0ce8ccf14c..32c8237d6a 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -40,7 +40,14 @@ public interface UserSessionProvider extends Provider {
     KeycloakSession getKeycloakSession();
 
     AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
-    AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline);
+    
+    /**
+     * @deprecated Use {@link #getClientSession(UserSessionModel, ClientModel, String, boolean)} instead.
+     */
+    default AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) {
+        return getClientSession(userSession, client, clientSessionId == null ? null : clientSessionId.toString(), offline);
+    }
+    AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, String clientSessionId, boolean offline);
 
     UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
 
diff --git a/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java b/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java
index 10002956cc..8f20c46a47 100644
--- a/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java
@@ -59,10 +59,12 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
     private KeycloakSessionFactory factory;
     private boolean componentCachingAvailable;
     private boolean componentCachingEnabled;
+    private Boolean componentCachingForced;
 
     @Override
     public void init(Scope config) {
         this.componentCachingEnabled = config.getBoolean("cachingEnabled", true);
+        this.componentCachingForced = config.getBoolean("cachingForced", false);
     }
 
     @Override
@@ -72,7 +74,12 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
         if (! componentCachingEnabled) {
             LOG.warn("Caching of components disabled by the configuration which may have performance impact.");
         } else if (! componentCachingAvailable) {
-            LOG.warn("No system-wide ClusterProviderFactory found. Cannot send messages across cluster, thus disabling caching of components.");
+            if (Objects.equals(componentCachingForced, Boolean.TRUE)) {
+                LOG.warn("Component caching forced even though no system-wide ClusterProviderFactory found. This would be only reliable in single-node deployment.");
+                this.componentCachingAvailable = true;
+            } else {
+                LOG.warn("No system-wide ClusterProviderFactory found. Cannot send messages across cluster, thus disabling caching of components. Consider setting cachingForced option in single-node deployment.");
+            }
         }
     }
 
@@ -120,9 +127,13 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
         Scope scope = Config.scope(factory.getSpi(clazz).getName(), provider);
         ComponentModelScope configScope = new ComponentModelScope(scope, cm);
 
-        return this.componentCachingAvailable
-          ? componentsMap.get().computeIfAbsent(componentId, cId -> initializeFactory(clazz, realmId, componentId, newFactory, configScope))
-          : initializeFactory(clazz, realmId, componentId, newFactory, configScope);
+        ProviderFactory providerFactory;
+        if (this.componentCachingAvailable) {
+            providerFactory = componentsMap.get().computeIfAbsent(componentId, cId -> initializeFactory(clazz, realmId, componentId, newFactory, configScope));
+        } else {
+            providerFactory = initializeFactory(clazz, realmId, componentId, newFactory, configScope);
+        }
+        return providerFactory;
     }
 
     @SuppressWarnings("unchecked")
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index 3f82b8cbe5..a51d4a56a5 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -82,7 +82,9 @@
     "mapStorage": {
         "provider": "${keycloak.mapStorage.provider:concurrenthashmap}",
         "concurrenthashmap": {
-            "dir": "${project.build.directory:target}"
+            "dir": "${project.build.directory:target}",
+            "keyType.realms": "string",
+            "keyType.authz-resource-servers": "string"
         }
     },
 
diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
index 890420ab1d..ca8b9d690a 100644
--- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
+++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java
@@ -24,6 +24,7 @@ import org.keycloak.common.util.SystemEnvProperties;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
+import java.util.function.BooleanSupplier;
 import java.util.stream.Collectors;
 
 /**
@@ -34,15 +35,25 @@ public class Config implements ConfigProvider {
 
     private final Properties systemProperties = new SystemEnvProperties();
 
+    private final Map defaultProperties = new HashMap<>();
     private final ThreadLocal> properties = new ThreadLocal>() {
         @Override
         protected Map initialValue() {
             return new HashMap<>();
         }
     };
+    private final BooleanSupplier useGlobalConfigurationFunc;
+
+    public Config(BooleanSupplier useGlobalConfigurationFunc) {
+        this.useGlobalConfigurationFunc = useGlobalConfigurationFunc;
+    }
 
     void reset() {
-        properties.remove();
+        if (useGlobalConfigurationFunc.getAsBoolean()) {
+            defaultProperties.clear();
+        } else {
+            properties.remove();
+        }
     }
 
     public class SpiConfig {
@@ -63,9 +74,9 @@ public class Config implements ConfigProvider {
 
         public SpiConfig config(String key, String value) {
             if (value == null) {
-                properties.get().remove(prefix + key);
+                getConfig().remove(prefix + key);
             } else {
-                properties.get().put(prefix + key, value);
+                getConfig().put(prefix + key, value);
             }
             return this;
         }
@@ -87,9 +98,9 @@ public class Config implements ConfigProvider {
 
         public ProviderConfig config(String key, String value) {
             if (value == null) {
-                properties.get().remove(prefix + key);
+                getConfig().remove(prefix + key);
             } else {
-                properties.get().put(prefix + key, value);
+                getConfig().put(prefix + key, value);
             }
             return this;
         }
@@ -112,7 +123,7 @@ public class Config implements ConfigProvider {
 
         @Override
         public String get(String key, String defaultValue) {
-            String v = replaceProperties(properties.get().get(prefix + key));
+            String v = replaceProperties(getConfig().get(prefix + key));
             if (v == null || v.isEmpty()) {
                 v = System.getProperty("keycloak." + prefix + key, defaultValue);
             }
@@ -133,7 +144,11 @@ public class Config implements ConfigProvider {
 
     @Override
     public String getProvider(String spiName) {
-        return properties.get().get(spiName + ".provider");
+        return getConfig().get(spiName + ".provider");
+    }
+
+    public Map getConfig() {
+        return useGlobalConfigurationFunc.getAsBoolean() ? defaultProperties : properties.get();
     }
 
     private String replaceProperties(String value) {
@@ -156,7 +171,7 @@ public class Config implements ConfigProvider {
 
     @Override
     public String toString() {
-        return properties.get().entrySet().stream()
+        return getConfig().entrySet().stream()
           .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
           .map(e -> e.getKey() + " = " + e.getValue())
           .collect(Collectors.joining("\n    "));
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java
index 93959039de..dcf4810c15 100644
--- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java
@@ -21,6 +21,8 @@ import org.keycloak.authorization.AuthorizationSpi;
 import org.keycloak.authorization.DefaultAuthorizationProviderFactory;
 import org.keycloak.authorization.store.StoreFactorySpi;
 import org.keycloak.cluster.ClusterSpi;
+import org.keycloak.component.ComponentFactoryProviderFactory;
+import org.keycloak.component.ComponentFactorySpi;
 import org.keycloak.events.EventStoreSpi;
 import org.keycloak.executors.DefaultExecutorsProviderFactory;
 import org.keycloak.executors.ExecutorsSpi;
@@ -44,6 +46,7 @@ import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.provider.ProviderManager;
 import org.keycloak.provider.Spi;
+import org.keycloak.services.DefaultComponentFactoryProviderFactory;
 import org.keycloak.services.DefaultKeycloakSessionFactory;
 import org.keycloak.timer.TimerSpi;
 import com.google.common.collect.ImmutableSet;
@@ -193,6 +196,7 @@ public abstract class KeycloakModelTest {
       .add(AuthorizationSpi.class)
       .add(ClientScopeSpi.class)
       .add(ClientSpi.class)
+      .add(ComponentFactorySpi.class)
       .add(ClusterSpi.class)
       .add(EventStoreSpi.class)
       .add(ExecutorsSpi.class)
@@ -208,13 +212,14 @@ public abstract class KeycloakModelTest {
       .build();
 
     private static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder()
+      .add(ComponentFactoryProviderFactory.class)
       .add(DefaultAuthorizationProviderFactory.class)
       .add(DefaultExecutorsProviderFactory.class)
       .add(ServerInfoProviderFactory.class)
       .build();
 
     protected static final List MODEL_PARAMETERS;
-    protected static final Config CONFIG = new Config();
+    protected static final Config CONFIG = new Config(KeycloakModelTest::useDefaultFactory);
     private static volatile KeycloakSessionFactory DEFAULT_FACTORY;
     private static final ThreadLocal LOCAL_FACTORY = new ThreadLocal<>();
     protected static boolean USE_DEFAULT_FACTORY = false;
@@ -250,6 +255,9 @@ public abstract class KeycloakModelTest {
         int factoryIndex = FACTORY_COUNT.incrementAndGet();
         String threadName = Thread.currentThread().getName();
         CONFIG.reset();
+        CONFIG.spi(ComponentFactorySpi.NAME)
+          .provider(DefaultComponentFactoryProviderFactory.PROVIDER_ID)
+            .config("cachingForced", "true");
         MODEL_PARAMETERS.forEach(m -> m.updateConfig(CONFIG));
 
         LOG.debugf("Creating factory %d in %s using the following configuration:\n    %s", factoryIndex, threadName, CONFIG);
@@ -349,16 +357,16 @@ public abstract class KeycloakModelTest {
         }
     }
 
-    private static boolean isOnMainThread() {
-        return MAIN_THREAD_NAMES.contains(Thread.currentThread().getName());
+    protected static boolean useDefaultFactory() {
+        return USE_DEFAULT_FACTORY || MAIN_THREAD_NAMES.contains(Thread.currentThread().getName());
     }
 
     protected static KeycloakSessionFactory getFactory() {
-        return (USE_DEFAULT_FACTORY || isOnMainThread()) ? DEFAULT_FACTORY : LOCAL_FACTORY.get();
+        return useDefaultFactory() ? DEFAULT_FACTORY : LOCAL_FACTORY.get();
     }
 
     private static void setFactory(KeycloakSessionFactory factory) {
-        if (USE_DEFAULT_FACTORY || isOnMainThread()) {
+        if (useDefaultFactory()) {
             DEFAULT_FACTORY = factory;
         } else {
             LOCAL_FACTORY.set(factory);
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java
new file mode 100644
index 0000000000..e7e41f3076
--- /dev/null
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.model;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientProvider;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.map.client.MapClientEntity;
+import org.keycloak.models.map.client.MapClientProviderFactory;
+import org.keycloak.models.map.storage.MapStorage;
+import org.keycloak.models.map.storage.MapStorageProvider;
+import org.keycloak.models.map.storage.MapStorageProviderFactory;
+import org.keycloak.models.map.storage.StringKeyConvertor;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.InvalidationHandler.ObjectType;
+import org.hamcrest.Matchers;
+import org.jboss.logging.Logger;
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+
+/**
+ *
+ * @author hmlnarik
+ */
+@RequireProvider(value = ClientProvider.class, only = {MapClientProviderFactory.PROVIDER_ID})
+@RequireProvider(RealmProvider.class)
+@RequireProvider(MapStorageProvider.class)
+public class MapStorageTest extends KeycloakModelTest {
+
+    private static final Logger LOG = Logger.getLogger(MapStorageTest.class.getName());
+
+    private String realmId;
+
+    private String mapStorageProviderId;
+
+    @Before
+    public void initMapStorageProviderId() {
+        MapStorageProviderFactory ms = (MapStorageProviderFactory) getFactory().getProviderFactory(MapStorageProvider.class);
+        mapStorageProviderId = ms.getId();
+        assertThat(mapStorageProviderId, Matchers.notNullValue());
+    }
+
+    @Override
+    public void createEnvironment(KeycloakSession s) {
+        RealmModel realm = s.realms().createRealm("realm");
+        realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
+        this.realmId = realm.getId();
+    }
+
+    @Override
+    public void cleanEnvironment(KeycloakSession s) {
+        s.realms().removeRealm(realmId);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public  void testStorageSeparation() {
+        String component1Id = createMapStorageComponent("component1", "keyType", "ulong");
+        String component2Id = createMapStorageComponent("component2", "keyType", "string");
+
+        Object[] ids = withRealm(realmId, (session, realm) -> {
+            MapStorage, ClientModel> storageMain = (MapStorage, ClientModel>) session.getProvider(MapStorageProvider.class).getStorage(MapClientEntity.class, ClientModel.class);
+            MapStorage, ClientModel> storage1 = (MapStorage, ClientModel>) session.getComponentProvider(MapStorageProvider.class, component1Id).getStorage(MapClientEntity.class, ClientModel.class);
+            MapStorage, ClientModel> storage2 = (MapStorage, ClientModel>) session.getComponentProvider(MapStorageProvider.class, component2Id).getStorage(MapClientEntity.class, ClientModel.class);
+
+            // Assert that the map storage can be used both as a standalone store and a component
+            assertThat(storageMain, notNullValue());
+            assertThat(storage1, notNullValue());
+            assertThat(storage2, notNullValue());
+
+            final StringKeyConvertor kcMain = storageMain.getKeyConvertor();
+            final StringKeyConvertor kc1 = storage1.getKeyConvertor();
+            final StringKeyConvertor kc2 = storage2.getKeyConvertor();
+
+            K  idMain = kcMain.yieldNewUniqueKey();
+            K1 id1    = kc1.yieldNewUniqueKey();
+            K2 id2    = kc2.yieldNewUniqueKey();
+
+            assertThat(idMain, notNullValue());
+            assertThat(id1, notNullValue());
+            assertThat(id2, notNullValue());
+
+            // Assert that the stores do not contain the to-be-created clients
+            assertThat(storageMain.read(idMain), nullValue());
+            assertThat(storage1.read(id1), nullValue());
+            assertThat(storage2.read(id2), nullValue());
+
+            assertClientDoesNotExist(storageMain, id1, kc1, kcMain);
+            assertClientDoesNotExist(storageMain, id2, kc2, kcMain);
+            assertClientDoesNotExist(storage1, idMain, kcMain, kc1);
+            assertClientDoesNotExist(storage1, id2, kc2, kc1);
+            assertClientDoesNotExist(storage2, idMain, kcMain, kc2);
+            assertClientDoesNotExist(storage2, id1, kc1, kc2);
+
+            MapClientEntity clientMain = new MapClientEntity<>(idMain, realmId);
+            MapClientEntity client1 = new MapClientEntity<>(id1, realmId);
+            MapClientEntity client2 = new MapClientEntity<>(id2, realmId);
+
+            storageMain.create(clientMain.getId(), clientMain);
+            storage1.create(client1.getId(), client1);
+            storage2.create(client2.getId(), client2);
+
+            return new Object[] {idMain, id1, id2};
+        });
+
+        K idMain = (K) ids[0];
+        K1 id1 = (K1) ids[1];
+        K2 id2 = (K2) ids[2];
+
+        LOG.debugf("Object IDs: %s, %s, %s", idMain, id1, id2);
+
+        assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
+
+        // Invalidate one component and check that the storage still contains what it should
+        getFactory().invalidate(ObjectType.COMPONENT, component1Id);
+        assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
+
+        // Invalidate whole realm and check that the storage still contains what it should
+        getFactory().invalidate(ObjectType.REALM, realmId);
+        assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
+
+        // Refresh factory (akin server restart) and check that the storage still contains what it should
+        reinitializeKeycloakSessionFactory();
+        assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
+    }
+
+    private  void assertClientDoesNotExist(MapStorage, ClientModel> storage, K1 id, final StringKeyConvertor kc, final StringKeyConvertor kcStorage) {
+        // Assert that the other stores do not contain the to-be-created clients (if they use compatible key format)
+        try {
+            final K keyInStorageFormat = kcStorage.fromString(kc.keyToString(id));
+            assertThat(storage.read(keyInStorageFormat), nullValue());
+        } catch (Exception ex) {
+            // If the format is incompatible then the object does not exist in the store
+        }
+    }
+
+    private  void assertClientsPersisted(String component1Id, String component2Id, K idMain, K1 id1, K2 id2) {
+        // Check that in the next transaction, the objects are still there
+        withRealm(realmId, (session, realm) -> {
+            @SuppressWarnings("unchecked")
+            MapStorage, ClientModel> storageMain = (MapStorage, ClientModel>) session.getProvider(MapStorageProvider.class).getStorage(MapClientEntity.class, ClientModel.class);
+            @SuppressWarnings("unchecked")
+            MapStorage, ClientModel> storage1 = (MapStorage, ClientModel>) session.getComponentProvider(MapStorageProvider.class, component1Id).getStorage(MapClientEntity.class, ClientModel.class);
+            @SuppressWarnings("unchecked")
+            MapStorage, ClientModel> storage2 = (MapStorage, ClientModel>) session.getComponentProvider(MapStorageProvider.class, component2Id).getStorage(MapClientEntity.class, ClientModel.class);
+
+            final StringKeyConvertor kcMain = storageMain.getKeyConvertor();
+            final StringKeyConvertor kc1 = storage1.getKeyConvertor();
+            final StringKeyConvertor kc2 = storage2.getKeyConvertor();
+
+            // Assert that the stores contain the created clients
+            assertThat(storageMain.read(idMain), notNullValue());
+            assertThat(storage1.read(id1), notNullValue());
+            assertThat(storage2.read(id2), notNullValue());
+
+            // Assert that the other stores do not contain the to-be-created clients (if they use compatible key format)
+            assertClientDoesNotExist(storageMain, id1, kc1, kcMain);
+            assertClientDoesNotExist(storageMain, id2, kc2, kcMain);
+            assertClientDoesNotExist(storage1, idMain, kcMain, kc1);
+            assertClientDoesNotExist(storage1, id2, kc2, kc1);
+            assertClientDoesNotExist(storage2, idMain, kcMain, kc2);
+            assertClientDoesNotExist(storage2, id1, kc1, kc2);
+            assertThat(storageMain.read(idMain), notNullValue());
+
+            return null;
+        });
+    }
+
+    private String createMapStorageComponent(String name, String... config) {
+        ComponentModel c1 = KeycloakModelUtils.createComponentModel(name, realmId, mapStorageProviderId, MapStorageProvider.class.getName(), config);
+
+        return withRealm(realmId, (s, r) -> r.addComponentModel(c1).getId());
+    }
+}
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/ConcurrentHashMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/ConcurrentHashMapStorage.java
index 10be92f287..0c03ebe5da 100644
--- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/ConcurrentHashMapStorage.java
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/ConcurrentHashMapStorage.java
@@ -18,7 +18,6 @@ package org.keycloak.testsuite.model.parameters;
 
 import org.keycloak.models.map.storage.MapStorageSpi;
 import org.keycloak.testsuite.model.KeycloakModelParameters;
-import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProvider;
 import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.provider.Spi;
@@ -42,8 +41,8 @@ public class ConcurrentHashMapStorage extends KeycloakModelParameters {
     @Override
     public void updateConfig(Config cf) {
         cf.spi(MapStorageSpi.NAME)
-            .defaultProvider(ConcurrentHashMapStorageProvider.PROVIDER_ID)
-            .provider(ConcurrentHashMapStorageProvider.PROVIDER_ID)
+            .defaultProvider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
+            .provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
               .config("dir", "${project.build.directory:target}");
     }
 
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java
index 5307794a97..a2fbf2cc09 100644
--- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java
@@ -16,8 +16,12 @@
  */
 package org.keycloak.testsuite.model.parameters;
 
+import org.keycloak.authorization.store.StoreFactorySpi;
+import org.keycloak.models.ServerInfoSpi;
 import org.keycloak.models.UserLoginFailureSpi;
 import org.keycloak.models.UserSessionSpi;
+import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
+import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
 import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
 import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
 import org.keycloak.testsuite.model.KeycloakModelParameters;
@@ -26,6 +30,7 @@ import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
 import org.keycloak.models.map.group.MapGroupProviderFactory;
 import org.keycloak.models.map.realm.MapRealmProviderFactory;
 import org.keycloak.models.map.role.MapRoleProviderFactory;
+import org.keycloak.models.map.serverinfo.MapServerInfoProviderFactory;
 import org.keycloak.models.map.storage.MapStorageProviderFactory;
 import org.keycloak.models.map.storage.MapStorageSpi;
 import org.keycloak.models.map.user.MapUserProviderFactory;
@@ -48,15 +53,19 @@ public class Map extends KeycloakModelParameters {
       .build();
 
     static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder()
+      .add(MapAuthorizationStoreFactory.class)
       .add(MapClientProviderFactory.class)
       .add(MapClientScopeProviderFactory.class)
       .add(MapGroupProviderFactory.class)
       .add(MapRealmProviderFactory.class)
       .add(MapRoleProviderFactory.class)
+      .add(MapRootAuthenticationSessionProviderFactory.class)
+      .add(MapServerInfoProviderFactory.class)
       .add(MapUserProviderFactory.class)
-      .add(MapStorageProviderFactory.class)
       .add(MapUserSessionProviderFactory.class)
       .add(MapUserLoginFailureProviderFactory.class)
+
+      .add(MapStorageProviderFactory.class)
       .build();
 
     public Map() {
@@ -71,6 +80,8 @@ public class Map extends KeycloakModelParameters {
           .spi("group").defaultProvider(MapGroupProviderFactory.PROVIDER_ID)
           .spi("realm").defaultProvider(MapRealmProviderFactory.PROVIDER_ID)
           .spi("role").defaultProvider(MapRoleProviderFactory.PROVIDER_ID)
+          .spi(ServerInfoSpi.NAME).defaultProvider(MapServerInfoProviderFactory.PROVIDER_ID)
+          .spi(StoreFactorySpi.NAME).defaultProvider(MapAuthorizationStoreFactory.PROVIDER_ID)
           .spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
           .spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
           .spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java
index f3da9d24cf..62f28d80d2 100644
--- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java
@@ -152,7 +152,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
                 Assert.assertEquals(origSessions[0], userSession);
 
                 AuthenticatedClientSessionModel clientSession = session.sessions().getClientSession(userSession, realm.getClientByClientId("test-app"),
-                        UUID.fromString(origSessions[0].getAuthenticatedClientSessionByClient(realm.getClientByClientId("test-app").getId()).getId()),
+                        origSessions[0].getAuthenticatedClientSessionByClient(realm.getClientByClientId("test-app").getId()).getId(),
                         false);
                 Assert.assertEquals(origSessions[0].getAuthenticatedClientSessionByClient(realm.getClientByClientId("test-app").getId()).getId(), clientSession.getId());
 
diff --git a/testsuite/model/test-all-profiles.sh b/testsuite/model/test-all-profiles.sh
index fd620efcc9..b62c458045 100755
--- a/testsuite/model/test-all-profiles.sh
+++ b/testsuite/model/test-all-profiles.sh
@@ -9,9 +9,13 @@ for I in `perl -ne 'print "$1\n" if (m,([^<]+),)' pom.xml`; do
     echo "========"
     echo "======== Profile $I"
     echo "========"
-    mvn test "-P$I" "$@"
+    mvn -B test "-P$I" "$@"
     EXIT_CODE=$[$EXIT_CODE + $?]
     mv target/surefire-reports "target/surefire-reports-$I"
 done
 
+for I in `perl -ne 'print "$1\n" if (m,([^<]+),)' pom.xml`; do
+    grep -A 1 --no-filename '<<<' "target/surefire-reports-$I"/*.txt | perl -pe "print '::error::| $I | ';"
+done
+
 exit $EXIT_CODE