, M> exten
/**
* Instructs this transaction to add a new value into the underlying store on commit.
+ *
+ * Updates to the returned instances of {@code V} would be visible in the current transaction
+ * and will propagate into the underlying store upon commit.
*
* @param value the value
* @return Entity representing the {@code value} in the store. It may or may not be the same instance as {@code value}
@@ -35,29 +38,22 @@ public interface MapKeycloakTransaction, M> exten
/**
* Provides possibility to lookup for values by a {@code key} in the underlying store with respect to changes done
- * in current transaction.
+ * in current transaction. Updates to the returned instance would be visible in the current transaction
+ * and will propagate into the underlying store upon commit.
*
* @param key identifier of a value
* @return a value associated with the given {@code key}
*/
V read(K key);
- /**
- * Looks up a value in the current transaction with corresponding key, returns {@code defaultValueFunc} when
- * the transaction does not contain a value for the {@code key} identifier.
- *
- * @param key identifier of a value
- * @param defaultValueFunc fallback function if the transaction does not contain a value that corresponds to {@code key}
- * @return a value associated with the given {@code key}, or the result of {@code defaultValueFunc}
- *
- */
- V read(K key, Function defaultValueFunc);
-
/**
* Returns a stream of values from underlying storage that are updated based on the current transaction changes;
* i.e. the result contains updates and excludes of records that have been created, updated or deleted in this
* transaction by methods {@link MapKeycloakTransaction#create}, {@link MapKeycloakTransaction#update},
* {@link MapKeycloakTransaction#delete}, etc.
+ *
+ * Updates to the returned instances of {@code V} would be visible in the current transaction
+ * and will propagate into the underlying store upon commit.
*
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return values that fulfill the given criteria, that are updated based on changes in the current transaction
@@ -73,38 +69,6 @@ public interface MapKeycloakTransaction, M> exten
*/
long getCount(QueryParameters queryParameters);
- /**
- * Instructs this transaction to force-update the {@code value} associated with the identifier {@code value.getId()} in the
- * underlying store on commit.
- *
- * @param value updated version of the value
- * @return Entity representing the {@code value} in the store. It may or may not be the same instance as {@code value}
- */
- V update(V value);
-
- /**
- * Returns an updated version of the {@code orig} object as updated in this transaction.
- *
- * If the underlying store handles transactions on its own, this can return {@code orig} directly.
- *
- * @param orig possibly stale version of some object from the underlying store
- * @return the {@code orig} object as visible from this transaction, or {@code null} if the object has been removed.
- */
- default V getUpdated(V orig) {
- return orig;
- }
-
- /**
- * Instructs this transaction to update the {@code value} associated with the identifier {@code value.getId()} in the
- * underlying store on commit, if by the time of {@code commit} the {@code shouldPut} predicate returns {@code true}
- *
- * @param value new version of the value. Must not alter the {@code id} of the entity
- * @param shouldPut predicate to check in commit phase
- * @return Entity representing the {@code value} in the store. It may or may not be the same instance as {@code value}
- * @see AbstractEntity#getId()
- */
- V updateIfChanged(V value, Predicate shouldPut);
-
/**
* Instructs this transaction to delete a value associated with the identifier {@code key} from the underlying store
* on commit.
@@ -122,6 +86,6 @@ public interface MapKeycloakTransaction, M> exten
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return number of removed objects (might return {@code -1} if not supported)
*/
- long delete(K artificialKey, QueryParameters queryParameters);
+ long delete(QueryParameters queryParameters);
}
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 fb2b2d3dfe..646ceb00c2 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
@@ -20,8 +20,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.map.common.AbstractEntity;
import java.util.stream.Stream;
-import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
-
/**
* Implementation of this interface interacts with a persistence storage storing various entities, e.g. users, realms.
* It contains basic object CRUD operations as well as bulk {@link #read(org.keycloak.models.map.storage.QueryParameters)}
@@ -41,10 +39,10 @@ import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public interface MapStorage, M> {
/**
- * Creates an object in the store identified. The ID of the {@code value} should be non-{@code null}.
- * If the ID is {@code null}, then the {@code value}'s ID will be returned
+ * Creates an object in the store. ID of the {@code value} may be prescribed in id of the {@code value}.
+ * If the id is {@code null}, then the {@code value}'s ID will be generated and returned in the id of the return value.
* @param value Entity to create in the store
- * @throws NullPointerException if object or its {@code key} is {@code null}
+ * @throws NullPointerException if {@code value} is {@code null}
* @see AbstractEntity#getId()
* @return Entity representing the {@code value} in the store. It may or may not be the same instance as {@code value}
*/
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 cb5f4b92c5..4683bd5c05 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,13 +27,14 @@ import org.keycloak.provider.Provider;
public interface MapStorageProvider extends Provider {
/**
- * Returns a key-value storage implementation for the particular types.
+ * Returns a key-value storage implementation for the given types.
* @param type of the primary key
* @param type of the value
- * @param name Name of the storage
- * @param flags
+ * @param type of the corresponding model (e.g. {@code UserModel})
+ * @param modelType Model type
+ * @param flags Flags of the returned storage. Best effort, flags may be not honored by underlying implementation
* @return
* @throws IllegalArgumentException If some of the types is not supported by the underlying implementation.
*/
- , M> MapStorage getStorage(Class valueType, Class modelType, Flag... flags);
+ , M> MapStorage getStorage(Class modelType, Flag... flags);
}
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
index 6813a94e78..abf930d718 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
@@ -17,6 +17,8 @@
package org.keycloak.models.map.storage.chm;
import org.keycloak.models.map.common.AbstractEntity;
+import org.keycloak.models.map.common.Serialization;
+import org.keycloak.models.map.common.UpdatableEntity;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -33,7 +35,7 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.QueryParameters;
import org.keycloak.utils.StreamsUtil;
-public class ConcurrentHashMapKeycloakTransaction, M> implements MapKeycloakTransaction {
+public class ConcurrentHashMapKeycloakTransaction & UpdatableEntity, M> implements MapKeycloakTransaction {
private final static Logger log = Logger.getLogger(ConcurrentHashMapKeycloakTransaction.class);
@@ -98,16 +100,37 @@ public class ConcurrentHashMapKeycloakTransaction
tasks.merge(taskKey, task, MapTaskCompose::new);
}
+ /**
+ * Returns a deep clone of an entity. If the clone is already in the transaction, returns this one.
+ *
+ * Usually used before giving an entity from a source back to the caller,
+ * to prevent changing it directly in the data store, but to keep transactional properties.
+ * @param origEntity Original entity
+ * @return
+ */
+ public V registerEntityForChanges(V origEntity) {
+ final K key = origEntity.getId();
+ // If the entity is listed in the transaction already, return it directly
+ if (tasks.containsKey(key)) {
+ MapTaskWithValue current = tasks.get(key);
+ return current.getValue();
+ }
+ // Else enlist its copy in the transaction. Never return direct reference to the underlying map
+ final V res = Serialization.from(origEntity);
+ return updateIfChanged(res, e -> e.isUpdated());
+ }
+
@Override
public V read(K key) {
- try { // TODO: Consider using Optional rather than handling NPE
- return read(key, map::read);
+ try {
+ // TODO: Consider using Optional rather than handling NPE
+ final V entity = read(key, map::read);
+ return registerEntityForChanges(entity);
} catch (NullPointerException ex) {
return null;
}
}
- @Override
public V read(K key, Function defaultValueFunc) {
MapTaskWithValue current = tasks.get(key);
// If the key exists, then it has entered the "tasks" after bulk delete that could have
@@ -152,7 +175,8 @@ public class ConcurrentHashMapKeycloakTransaction
Stream updatedAndNotRemovedObjectsStream = this.map.read(queryParameters)
.filter(filterOutAllBulkDeletedObjects)
.map(this::getUpdated) // If the object has been removed, tx.get will return null, otherwise it will return me.getValue()
- .filter(Objects::nonNull);
+ .filter(Objects::nonNull)
+ .map(this::registerEntityForChanges);
// In case of created values stored in MapKeycloakTransaction, we need filter those according to the filter
MapModelCriteriaBuilder mapMcb = mcb.unwrap(MapModelCriteriaBuilder.class);
@@ -176,19 +200,11 @@ public class ConcurrentHashMapKeycloakTransaction
return read(queryParameters).count();
}
- @Override
- public V getUpdated(V orig) {
+ private V getUpdated(V orig) {
MapTaskWithValue current = orig == null ? null : tasks.get(orig.getId());
return current == null ? orig : current.getValue();
}
- @Override
- public V update(V value) {
- K key = value.getId();
- addTask(key, new UpdateOperation(value));
- return value;
- }
-
@Override
public V create(V value) {
K key = value.getId();
@@ -196,7 +212,6 @@ public class ConcurrentHashMapKeycloakTransaction
return value;
}
- @Override
public V updateIfChanged(V value, Predicate shouldPut) {
K key = value.getId();
log.tracef("Adding operation UPDATE_IF_CHANGED for %s @ %08x", key, System.identityHashCode(value));
@@ -222,9 +237,11 @@ public class ConcurrentHashMapKeycloakTransaction
@Override
- public long delete(K artificialKey, QueryParameters queryParameters) {
+ public long delete(QueryParameters queryParameters) {
log.tracef("Adding operation DELETE_BULK");
+ K artificialKey = map.getKeyConvertor().yieldNewUniqueKey();
+
// Remove all tasks that create / update / delete objects deleted by the bulk removal.
final BulkDeleteOperation bdo = new BulkDeleteOperation(queryParameters);
Predicate filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects();
@@ -344,15 +361,6 @@ public class ConcurrentHashMapKeycloakTransaction
@Override public MapOperation getOperation() { return MapOperation.CREATE; }
}
- private class UpdateOperation extends MapTaskWithValue {
- public UpdateOperation(V value) {
- super(value);
- }
-
- @Override public void execute() { map.update(getValue()); }
- @Override public MapOperation getOperation() { return MapOperation.UPDATE; }
- }
-
private class DeleteOperation extends MapTaskWithValue {
private final K key;
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 9856cedf19..2b07e39947 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
@@ -19,6 +19,7 @@ package org.keycloak.models.map.storage.chm;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.common.AbstractEntity;
+import org.keycloak.models.map.common.UpdatableEntity;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.QueryParameters;
@@ -42,7 +43,7 @@ import static org.keycloak.utils.StreamsUtil.paginatedStream;
*
* @author hmlnarik
*/
-public class ConcurrentHashMapStorage, M> implements MapStorage {
+public class ConcurrentHashMapStorage & UpdatableEntity, M> implements MapStorage {
private final ConcurrentMap store = new ConcurrentHashMap<>();
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 2fdd7f79e0..18cc6ba390 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
@@ -17,6 +17,7 @@
package org.keycloak.models.map.storage.chm;
import org.keycloak.models.map.common.AbstractEntity;
+import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.MapStorageProviderFactory.Flag;
@@ -37,8 +38,9 @@ public class ConcurrentHashMapStorageProvider implements MapStorageProvider {
}
@Override
- public , M> ConcurrentHashMapStorage getStorage(
- Class valueType, Class modelType, Flag... flags) {
- return factory.getStorage(valueType, modelType, flags);
+ @SuppressWarnings("unchecked")
+ public , M> MapStorage getStorage(Class modelType, Flag... flags) {
+ ConcurrentHashMapStorage storage = factory.getStorage(modelType, flags);
+ return (MapStorage) storage;
}
}
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 4a5473ed2c..4f716a8950 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
@@ -46,6 +46,7 @@ import org.keycloak.models.map.client.MapClientEntityImpl;
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.common.Serialization;
+import org.keycloak.models.map.common.UpdatableEntity;
import org.keycloak.models.map.group.MapGroupEntity;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
import org.keycloak.models.map.realm.MapRealmEntity;
@@ -114,7 +115,28 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
MODEL_TO_NAME.put(Resource.class, "authz-resources");
MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes");
}
+
+ public static final Map, Class extends AbstractEntity>> MODEL_TO_VALUE_TYPE = new HashMap<>();
+ static {
+ MODEL_TO_VALUE_TYPE.put(AuthenticatedClientSessionModel.class, MapAuthenticatedClientSessionEntity.class);
+ MODEL_TO_VALUE_TYPE.put(ClientScopeModel.class, MapClientScopeEntity.class);
+ MODEL_TO_VALUE_TYPE.put(ClientModel.class, MapClientEntity.class);
+ MODEL_TO_VALUE_TYPE.put(GroupModel.class, MapGroupEntity.class);
+ MODEL_TO_VALUE_TYPE.put(RealmModel.class, MapRealmEntity.class);
+ MODEL_TO_VALUE_TYPE.put(RoleModel.class, MapRoleEntity.class);
+ MODEL_TO_VALUE_TYPE.put(RootAuthenticationSessionModel.class, MapRootAuthenticationSessionEntity.class);
+ MODEL_TO_VALUE_TYPE.put(UserLoginFailureModel.class, MapUserLoginFailureEntity.class);
+ MODEL_TO_VALUE_TYPE.put(UserModel.class, MapUserEntity.class);
+ MODEL_TO_VALUE_TYPE.put(UserSessionModel.class, MapUserSessionEntity.class);
+ // authz
+ MODEL_TO_VALUE_TYPE.put(PermissionTicket.class, MapPermissionTicketEntity.class);
+ MODEL_TO_VALUE_TYPE.put(Policy.class, MapPolicyEntity.class);
+ MODEL_TO_VALUE_TYPE.put(ResourceServer.class, MapResourceServerEntity.class);
+ MODEL_TO_VALUE_TYPE.put(Resource.class, MapResourceEntity.class);
+ MODEL_TO_VALUE_TYPE.put(org.keycloak.authorization.model.Scope.class, MapScopeEntity.class);
+ }
+
public static final Map, Class>> INTERFACE_TO_IMPL = new HashMap<>();
static {
INTERFACE_TO_IMPL.put(MapClientEntity.class, MapClientEntityImpl.class);
@@ -219,16 +241,16 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
}
}
- private , M> ConcurrentHashMapStorage loadMap(String mapName,
- Class valueType, Class modelType, EnumSet flags) {
+ private & UpdatableEntity, M> ConcurrentHashMapStorage loadMap(String mapName,
+ Class modelType, EnumSet flags) {
final StringKeyConvertor kc = keyConvertors.getOrDefault(mapName, defaultKeyConvertor);
-
+ Class> valueType = MODEL_TO_VALUE_TYPE.get(modelType);
LOG.debugf("Initializing new map storage: %s", mapName);
@SuppressWarnings("unchecked")
ConcurrentHashMapStorage store;
if (modelType == UserSessionModel.class) {
- ConcurrentHashMapStorage clientSessionStore = getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+ ConcurrentHashMapStorage clientSessionStore = getStorage(AuthenticatedClientSessionModel.class);
store = new UserSessionConcurrentHashMapStorage(clientSessionStore, kc) {
@Override
public String toString() {
@@ -269,8 +291,8 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
}
@SuppressWarnings("unchecked")
- public , M> ConcurrentHashMapStorage getStorage(
- Class valueType, Class modelType, Flag... flags) {
+ public & UpdatableEntity, M> ConcurrentHashMapStorage getStorage(
+ 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:
@@ -282,9 +304,9 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
* to prepare clientSessionStore outside computeIfAbsent, otherwise deadlock occurs.
*/
if (modelType == UserSessionModel.class) {
- getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
+ getStorage(AuthenticatedClientSessionModel.class, flags);
}
- return (ConcurrentHashMapStorage) storages.computeIfAbsent(name, n -> loadMap(name, valueType, modelType, f));
+ return (ConcurrentHashMapStorage) storages.computeIfAbsent(name, n -> loadMap(name, modelType, f));
}
private File getFile(String fileName) {
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java
index d6c48d8b15..0966d4ada0 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java
@@ -66,6 +66,7 @@ import java.util.function.Predicate;
import java.util.stream.Stream;
import org.keycloak.models.map.storage.CriterionNotSupportedException;
+import java.util.IdentityHashMap;
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
/**
@@ -92,7 +93,7 @@ public class MapFieldPredicates {
@SuppressWarnings("unchecked")
private static final Map, Map> PREDICATES = new HashMap<>();
- private static final Map, Comparator>> COMPARATORS = new HashMap<>();
+ private static final Map, Comparator>> COMPARATORS = new IdentityHashMap<>();
static {
put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, MapRealmEntity::getName);
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 99eeb98731..858cdcbfb4 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
@@ -52,19 +52,19 @@ public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapSto
}
@Override
- public long delete(K artificialKey, QueryParameters queryParameters) {
+ public long delete(QueryParameters queryParameters) {
ModelCriteriaBuilder mcb = queryParameters.getModelCriteriaBuilder();
Set ids = read(queryParameters).map(AbstractEntity::getId).collect(Collectors.toSet());
ModelCriteriaBuilder csMcb = clientSessionStore.createCriteriaBuilder().compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, Operator.IN, ids);
- clientSessionTr.delete(artificialKey, withCriteria(csMcb));
- return super.delete(artificialKey, queryParameters);
+ clientSessionTr.delete(withCriteria(csMcb));
+ return super.delete(queryParameters);
}
@Override
public boolean delete(K key) {
ModelCriteriaBuilder csMcb = clientSessionStore.createCriteriaBuilder().compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, Operator.EQ, key);
- clientSessionTr.delete(key, withCriteria(csMcb));
+ clientSessionTr.delete(withCriteria(csMcb));
return super.delete(key);
}
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 1c39d216c1..c418969826 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
@@ -19,6 +19,7 @@ package org.keycloak.models.map.user;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.map.common.AbstractEntity;
+import org.keycloak.models.map.common.UpdatableEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.Collection;
@@ -39,7 +40,7 @@ import java.util.stream.Stream;
*
* @author mhajas
*/
-public class MapUserEntity implements AbstractEntity {
+public class MapUserEntity implements AbstractEntity, UpdatableEntity {
private final K id;
private final String realmId;
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 b45f00facc..94e88b06a4 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
@@ -69,7 +69,6 @@ import static org.keycloak.models.UserModel.EMAIL_VERIFIED;
import static org.keycloak.models.UserModel.FIRST_NAME;
import static org.keycloak.models.UserModel.LAST_NAME;
import static org.keycloak.models.UserModel.USERNAME;
-import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
@@ -89,7 +88,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
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(tx, origEntity)) {
+ return origEntity -> new MapUserAdapter(session, realm, origEntity) {
@Override
public String getId() {
return userStore.getKeyConvertor().keyToString(entity.getId());
@@ -127,9 +126,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
}
}
- private MapUserEntity getRegisteredEntityByIdOrThrow(RealmModel realm, String id) {
+ private MapUserEntity getEntityByIdOrThrow(RealmModel realm, String id) {
return getEntityById(realm, id)
- .map(e -> registerEntityForChanges(tx, e))
.orElseThrow(this::userDoesntExistException);
}
@@ -142,10 +140,6 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
return Optional.empty();
}
- private Optional> getRegisteredEntityById(RealmModel realm, String id) {
- return getEntityById(realm, id).map(e -> registerEntityForChanges(tx, e));
- }
-
@Override
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
if (user == null || user.getId() == null) {
@@ -153,7 +147,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
}
LOG.tracef("addFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialLink.getIdentityProvider(), getShortStackTrace());
- getRegisteredEntityById(realm, user.getId())
+ getEntityById(realm, user.getId())
.ifPresent(userEntity ->
userEntity.addFederatedIdentity(UserFederatedIdentityEntity.fromModel(socialLink)));
}
@@ -161,7 +155,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
@Override
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
LOG.tracef("removeFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialProvider, getShortStackTrace());
- return getRegisteredEntityById(realm, user.getId())
+ return getEntityById(realm, user.getId())
.map(entity -> entity.removeFederatedIdentity(socialProvider))
.orElse(false);
}
@@ -175,14 +169,13 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.IDP_AND_USER, Operator.EQ, socialProvider);
tx.read(withCriteria(mcb))
- .map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeFederatedIdentity(socialProvider));
}
@Override
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
LOG.tracef("updateFederatedIdentity(%s, %s, %s)%s", realm, federatedUser.getId(), federatedIdentityModel.getIdentityProvider(), getShortStackTrace());
- getRegisteredEntityById(realm, federatedUser.getId())
+ getEntityById(realm, federatedUser.getId())
.ifPresent(entity -> entity.updateFederatedIdentity(UserFederatedIdentityEntity.fromModel(federatedIdentityModel)));
}
@@ -229,7 +222,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
public void addConsent(RealmModel realm, String userId, UserConsentModel consent) {
LOG.tracef("addConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
- getRegisteredEntityByIdOrThrow(realm, userId)
+ getEntityByIdOrThrow(realm, userId)
.addUserConsent(UserConsentEntity.fromModel(consent));
}
@@ -255,7 +248,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
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 = getEntityByIdOrThrow(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 + "]");
@@ -273,7 +266,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
@Override
public boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId) {
LOG.tracef("revokeConsentForClient(%s, %s, %s)%s", realm, userId, clientInternalId, getShortStackTrace());
- return getRegisteredEntityById(realm, userId)
+ return getEntityById(realm, userId)
.map(userEntity -> userEntity.removeUserConsent(clientInternalId))
.orElse(false);
}
@@ -281,7 +274,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
@Override
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
LOG.tracef("setNotBeforeForUser(%s, %s, %d)%s", realm, user.getId(), notBefore, getShortStackTrace());
- getRegisteredEntityByIdOrThrow(realm, user.getId()).setNotBefore(notBefore);
+ getEntityByIdOrThrow(realm, user.getId()).setNotBefore(notBefore);
}
@Override
@@ -363,7 +356,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
ModelCriteriaBuilder mcb = userStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
- tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
+ tx.delete(withCriteria(mcb));
}
@Override
@@ -373,7 +366,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
- tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
+ tx.delete(withCriteria(mcb));
}
@Override
@@ -384,8 +377,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
try (Stream> s = tx.read(withCriteria(mcb))) {
- s.map(e -> registerEntityForChanges(tx, e))
- .forEach(userEntity -> userEntity.setFederationLink(null));
+ s.forEach(userEntity -> userEntity.setFederationLink(null));
}
}
@@ -398,8 +390,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId);
try (Stream> s = tx.read(withCriteria(mcb))) {
- s.map(e -> registerEntityForChanges(tx, e))
- .forEach(userEntity -> userEntity.removeRolesMembership(roleId));
+ s.forEach(userEntity -> userEntity.removeRolesMembership(roleId));
}
}
@@ -412,8 +403,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId);
try (Stream> s = tx.read(withCriteria(mcb))) {
- s.map(e -> registerEntityForChanges(tx, e))
- .forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
+ s.forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
}
}
@@ -426,8 +416,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId);
try (Stream> s = tx.read(withCriteria(mcb))) {
- s.map(e -> registerEntityForChanges(tx, e))
- .forEach(userEntity -> userEntity.removeUserConsent(clientId));
+ s.forEach(userEntity -> userEntity.removeUserConsent(clientId));
}
}
@@ -478,7 +467,6 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.collect(Collectors.toList());
if (! consentClientIds.isEmpty()) {
- userEntity = registerEntityForChanges(tx, userEntity);
consentClientIds.forEach(userEntity::removeUserConsent);
}
};
@@ -492,8 +480,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
try (Stream> s = tx.read(withCriteria(mcb))) {
- s.map(e -> registerEntityForChanges(tx, e))
- .forEach(entity -> entity.addRolesMembership(roleId));
+ s.forEach(entity -> entity.addRolesMembership(roleId));
}
}
@@ -535,7 +522,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS
throw new ModelDuplicateException("Multiple users with email '" + email + "' exist in Keycloak.");
}
- MapUserEntity userEntity = registerEntityForChanges(tx, usersWithEmail.get(0));
+ MapUserEntity