KEYCLOAK-18370 Introduce QueryParameters

This commit is contained in:
mhajas 2021-06-30 16:14:16 +02:00 committed by Hynek Mlnařík
parent 7d26b245de
commit dc1c9b944f
26 changed files with 520 additions and 352 deletions

View file

@ -40,6 +40,7 @@ import java.util.function.Predicate;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
/**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
@ -144,7 +145,7 @@ public class MapRootAuthenticationSessionProvider<K> implements AuthenticationSe
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.TIMESTAMP, Operator.LT, expired);
long deletedCount = tx.delete(sessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
long deletedCount = tx.delete(sessionStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
LOG.debugf("Removed %d expired authentication sessions for realm '%s'", deletedCount, realm.getName());
}
@ -155,7 +156,7 @@ public class MapRootAuthenticationSessionProvider<K> implements AuthenticationSe
ModelCriteriaBuilder<RootAuthenticationSessionModel> mcb = sessionStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
sessionStore.delete(mcb);
sessionStore.delete(withCriteria(mcb));
}
@Override

View file

@ -35,7 +35,6 @@ 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;
@ -45,6 +44,8 @@ import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
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;
import static org.keycloak.utils.StreamsUtil.distinctByKey;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
@ -90,7 +91,7 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
.toArray(ModelCriteriaBuilder[]::new)
);
return tx.getCount(mcb);
return tx.getCount(withCriteria(mcb));
}
@Override
@ -109,7 +110,7 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
mcb = mcb.compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId);
}
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Permission ticket for resource server: '" + resourceServer.getId()
+ ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists.");
}
@ -142,8 +143,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
public PermissionTicket findById(String id, String resourceServerId) {
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(PermissionTicket.SearchableFields.ID, Operator.EQ, id))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -153,7 +154,7 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
public List<PermissionTicket> findByResourceServer(String resourceServerId) {
LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId))
return tx.read(withCriteria(forResourceServer(resourceServerId)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -162,8 +163,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
LOG.tracef("findByOwner(%s, %s)%s", owner, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.OWNER, Operator.EQ, owner))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.OWNER, Operator.EQ, owner)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -172,8 +173,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
public List<PermissionTicket> findByResource(String resourceId, String resourceServerId) {
LOG.tracef("findByResource(%s, %s)%s", resourceId, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -182,8 +183,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
public List<PermissionTicket> findByScope(String scopeId, String resourceServerId) {
LOG.tracef("findByScope(%s, %s)%s", scopeId, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -212,9 +213,7 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
.toArray(ModelCriteriaBuilder[]::new)
);
Comparator<? super MapPermissionTicketEntity<K>> c = Comparator.comparing(MapPermissionTicketEntity::getId);
return paginatedStream(tx.read(mcb)
.sorted(c), firstResult, maxResult)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.ID))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -297,9 +296,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
.findById(ticket.getResourceId(), ticket.getResourceServerId());
}
return paginatedStream(tx.read(mcb)
return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING))
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId))
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID)
.map(ticketResourceMapper)
.filter(Objects::nonNull), first, max)
.collect(Collectors.toList());
@ -310,9 +308,8 @@ public class MapPermissionTicketStore<K extends Comparable<K>> implements Permis
ModelCriteriaBuilder<PermissionTicket> mcb = permissionTicketStore.createCriteriaBuilder()
.compare(SearchableFields.OWNER, Operator.EQ, owner);
return paginatedStream(tx.read(mcb)
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId))
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID), first, max)
return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING))
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId)), first, max)
.map(ticket -> authorizationProvider.getStoreFactory().getResourceStore()
.findById(ticket.getResourceId(), ticket.getResourceServerId()))
.collect(Collectors.toList());

View file

@ -42,7 +42,7 @@ import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public class MapPolicyStore<K> implements PolicyStore {
@ -86,7 +86,7 @@ public class MapPolicyStore<K> implements PolicyStore {
ModelCriteriaBuilder<Policy> mcb = forResourceServer(resourceServer.getId())
.compare(SearchableFields.NAME, Operator.EQ, representation.getName());
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Policy with name '" + representation.getName() + "' for " + resourceServer.getId() + " already exists");
}
@ -111,8 +111,8 @@ public class MapPolicyStore<K> implements PolicyStore {
public Policy findById(String id, String resourceServerId) {
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -122,8 +122,8 @@ public class MapPolicyStore<K> implements PolicyStore {
public Policy findByName(String name, String resourceServerId) {
LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.NAME, Operator.EQ, name))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -133,7 +133,7 @@ public class MapPolicyStore<K> implements PolicyStore {
public List<Policy> findByResourceServer(String id) {
LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace());
return tx.read(forResourceServer(id))
return tx.read(withCriteria(forResourceServer(id)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -153,11 +153,11 @@ public class MapPolicyStore<K> implements PolicyStore {
mcb = mcb.compare(SearchableFields.OWNER, Operator.NOT_EXISTS);
}
return paginatedStream(tx.read(mcb)
.sorted(MapPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.NAME))
.map(MapPolicyEntity<K>::getId)
.map(K::toString)
.map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)) // We need to go through cache
.map(policyStore.getKeyConvertor()::keyToString)
// We need to go through cache
.map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, resourceServerId))
.collect(Collectors.toList());
}
@ -205,24 +205,24 @@ public class MapPolicyStore<K> implements PolicyStore {
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer) {
LOG.tracef("findByResource(%s, %s, %s)%s", resourceId, resourceServerId, consumer, getShortStackTrace());
tx.read(forResourceServer(resourceServerId)
.compare(Policy.SearchableFields.RESOURCE_ID, Operator.EQ, resourceId))
tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId)))
.map(this::entityToAdapter)
.forEach(consumer);
}
@Override
public void findByResourceType(String type, String resourceServerId, Consumer<Policy> policyConsumer) {
tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[] {"defaultResourceType", type}))
tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[]{"defaultResourceType", type})))
.map(this::entityToAdapter)
.forEach(policyConsumer);
}
@Override
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopeIds))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopeIds)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -241,21 +241,21 @@ public class MapPolicyStore<K> implements PolicyStore {
.compare(SearchableFields.CONFIG, Operator.NOT_EXISTS, (Object[]) new String[] {"defaultResourceType"});
}
tx.read(mcb).map(this::entityToAdapter).forEach(consumer);
tx.read(withCriteria(mcb)).map(this::entityToAdapter).forEach(consumer);
}
@Override
public List<Policy> findByType(String type, String resourceServerId) {
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.TYPE, Operator.EQ, type))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@Override
public List<Policy> findDependentPolicies(String id, String resourceServerId) {
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}

View file

@ -33,7 +33,6 @@ 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;
@ -42,7 +41,7 @@ import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public class MapResourceStore<K extends Comparable<K>> implements ResourceStore {
@ -86,7 +85,7 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
.compare(SearchableFields.NAME, Operator.EQ, name)
.compare(SearchableFields.OWNER, Operator.EQ, owner);
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Resource with name '" + name + "' for " + resourceServer.getId() + " already exists for request owner " + owner);
}
@ -113,8 +112,8 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
public Resource findById(String id, String resourceServerId) {
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -127,11 +126,10 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer<Resource> consumer, int firstResult, int maxResult) {
LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace());
Comparator<? super MapResourceEntity<K>> c = Comparator.comparing(MapResourceEntity::getId);
paginatedStream(tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.OWNER, Operator.EQ, ownerId))
.sorted(c), firstResult, maxResult)
.map(this::entityToAdapter)
tx.read(withCriteria(forResourceServer(resourceServerId).compare(SearchableFields.OWNER, Operator.EQ, ownerId))
.pagination(firstResult, maxResult, SearchableFields.ID)
).map(this::entityToAdapter)
.forEach(consumer);
}
@ -148,8 +146,8 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
public List<Resource> findByUri(String uri, String resourceServerId) {
LOG.tracef("findByUri(%s, %s)%s", uri, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.URI, Operator.EQ, uri))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.URI, Operator.EQ, uri)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -158,7 +156,7 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
public List<Resource> findByResourceServer(String resourceServerId) {
LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId))
return tx.read(withCriteria(forResourceServer(resourceServerId)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -172,8 +170,7 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
.toArray(ModelCriteriaBuilder[]::new)
);
return paginatedStream(tx.read(mcb)
.sorted(MapResourceEntity.COMPARE_BY_NAME), firstResult, maxResult)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.NAME))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -210,8 +207,8 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer) {
LOG.tracef("findByScope(%s, %s, %s)%s", scopes, resourceServerId, consumer, getShortStackTrace());
tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes))
tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes)))
.map(this::entityToAdapter)
.forEach(consumer);
}
@ -224,9 +221,9 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
@Override
public Resource findByName(String name, String ownerId, String resourceServerId) {
LOG.tracef("findByName(%s, %s, %s)%s", name, ownerId, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.OWNER, Operator.EQ, ownerId)
.compare(SearchableFields.NAME, Operator.EQ, name))
.compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -235,8 +232,8 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
@Override
public void findByType(String type, String resourceServerId, Consumer<Resource> consumer) {
LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace());
tx.read(forResourceServer(resourceServerId)
.compare(SearchableFields.TYPE, Operator.EQ, type))
tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.forEach(consumer);
}
@ -252,7 +249,7 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
mcb = mcb.compare(SearchableFields.OWNER, Operator.EQ, owner);
}
tx.read(mcb)
tx.read(withCriteria(mcb))
.map(this::entityToAdapter)
.forEach(consumer);
}
@ -260,9 +257,9 @@ public class MapResourceStore<K extends Comparable<K>> implements ResourceStore
@Override
public void findByTypeInstance(String type, String resourceServerId, Consumer<Resource> consumer) {
LOG.tracef("findByTypeInstance(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace());
tx.read(forResourceServer(resourceServerId)
tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.OWNER, Operator.NE, resourceServerId)
.compare(SearchableFields.TYPE, Operator.EQ, type))
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.forEach(consumer);
}

View file

@ -39,7 +39,7 @@ import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public class MapScopeStore<K> implements ScopeStore {
@ -84,7 +84,7 @@ public class MapScopeStore<K> implements ScopeStore {
ModelCriteriaBuilder<Scope> mcb = forResourceServer(resourceServer.getId())
.compare(SearchableFields.NAME, Operator.EQ, name);
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Scope with name '" + name + "' for " + resourceServer.getId() + " already exists");
}
@ -109,8 +109,8 @@ public class MapScopeStore<K> implements ScopeStore {
public Scope findById(String id, String resourceServerId) {
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId)
.compare(Scope.SearchableFields.ID, Operator.EQ, id))
return tx.read(withCriteria(forResourceServer(resourceServerId)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -120,8 +120,8 @@ public class MapScopeStore<K> implements ScopeStore {
public Scope findByName(String name, String resourceServerId) {
LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace());
return tx.read(forResourceServer(resourceServerId).compare(Scope.SearchableFields.NAME,
Operator.EQ, name))
return tx.read(withCriteria(forResourceServer(resourceServerId).compare(SearchableFields.NAME,
Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.orElse(null);
@ -131,7 +131,7 @@ public class MapScopeStore<K> implements ScopeStore {
public List<Scope> findByResourceServer(String id) {
LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace());
return tx.read(forResourceServer(id))
return tx.read(withCriteria(forResourceServer(id)))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
@ -155,7 +155,8 @@ public class MapScopeStore<K> implements ScopeStore {
}
}
return paginatedStream(tx.read(mcb).map(this::entityToAdapter), firstResult, maxResult)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.NAME))
.map(this::entityToAdapter)
.collect(Collectors.toList());
}
}

View file

@ -24,8 +24,6 @@ import java.util.Objects;
public class MapPermissionTicketEntity<K> implements AbstractEntity<K> {
public static final Comparator<MapPermissionTicketEntity<?>> COMPARE_BY_RESOURCE_ID = Comparator.comparing(MapPermissionTicketEntity::getResourceId);
private final K id;
private String owner;
private String requester;

View file

@ -30,8 +30,6 @@ import java.util.Objects;
public class MapPolicyEntity<K> implements AbstractEntity<K> {
public static final Comparator<MapPolicyEntity<?>> COMPARE_BY_NAME = Comparator.comparing(MapPolicyEntity::getName);
private final K id;
private String name;
private String description;

View file

@ -29,8 +29,6 @@ import java.util.Set;
public class MapResourceEntity<K> implements AbstractEntity<K> {
public static final Comparator<MapResourceEntity<?>> COMPARE_BY_NAME = Comparator.comparing(MapResourceEntity::getName);
private final K id;
private String name;
private String displayName;

View file

@ -28,7 +28,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -45,9 +45,11 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import org.keycloak.models.ClientScopeModel;
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;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import java.util.HashSet;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
public class MapClientProvider<K> implements ClientProvider {
@ -57,8 +59,6 @@ public class MapClientProvider<K> implements ClientProvider {
private final MapStorage<K, MapClientEntity<K>, ClientModel> clientStore;
private final ConcurrentMap<K, ConcurrentMap<String, Integer>> clientRegisteredNodesStore;
private static final Comparator<MapClientEntity> COMPARE_BY_CLIENT_ID = Comparator.comparing(MapClientEntity::getClientId);
public MapClientProvider(KeycloakSession session, MapStorage<K, MapClientEntity<K>, ClientModel> clientStore, ConcurrentMap<K, ConcurrentMap<String, Integer>> clientRegisteredNodesStore) {
this.session = session;
this.clientStore = clientStore;
@ -126,7 +126,11 @@ public class MapClientProvider<K> implements ClientProvider {
@Override
public Stream<ClientModel> getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return paginatedStream(getClientsStream(realm), firstResult, maxResults);
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.CLIENT_ID))
.map(entityToAdapterFunc(realm));
}
@Override
@ -134,10 +138,8 @@ public class MapClientProvider<K> implements ClientProvider {
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
return tx.read(mcb)
.sorted(COMPARE_BY_CLIENT_ID)
.map(entityToAdapterFunc(realm))
;
return tx.read(withCriteria(mcb).orderBy(SearchableFields.CLIENT_ID, ASCENDING))
.map(entityToAdapterFunc(realm));
}
@Override
@ -220,7 +222,7 @@ public class MapClientProvider<K> implements ClientProvider {
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
return this.clientStore.getCount(mcb);
return this.clientStore.getCount(withCriteria(mcb));
}
@Override
@ -248,7 +250,7 @@ public class MapClientProvider<K> implements ClientProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CLIENT_ID, Operator.ILIKE, clientId);
return tx.read(mcb)
return tx.read(withCriteria(mcb))
.map(entityToAdapterFunc(realm))
.findFirst()
.orElse(null)
@ -265,10 +267,8 @@ public class MapClientProvider<K> implements ClientProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CLIENT_ID, Operator.ILIKE, "%" + clientId + "%");
Stream<MapClientEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_CLIENT_ID);
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.CLIENT_ID))
.map(entityToAdapterFunc(realm));
}
@Override
@ -280,10 +280,8 @@ public class MapClientProvider<K> implements ClientProvider {
mcb = mcb.compare(SearchableFields.ATTRIBUTE, Operator.EQ, entry.getKey(), entry.getValue());
}
Stream<MapClientEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_CLIENT_ID);
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.CLIENT_ID))
.map(entityToAdapterFunc(realm));
}
@Override
@ -344,7 +342,7 @@ public class MapClientProvider<K> implements ClientProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ENABLED, Operator.EQ, Boolean.TRUE);
try (Stream<MapClientEntity<K>> st = tx.read(mcb)) {
try (Stream<MapClientEntity<K>> st = tx.read(withCriteria(mcb))) {
return st
.filter(mce -> mce.getRedirectUris() != null && ! mce.getRedirectUris().isEmpty())
.collect(Collectors.toMap(
@ -358,7 +356,7 @@ public class MapClientProvider<K> implements ClientProvider {
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.SCOPE_MAPPING_ROLE, Operator.EQ, role.getId());
try (Stream<MapClientEntity<K>> toRemove = tx.read(mcb)) {
try (Stream<MapClientEntity<K>> toRemove = tx.read(withCriteria(mcb))) {
toRemove
.map(clientEntity -> session.clients().getClientById(realm, clientEntity.getId().toString()))
.filter(Objects::nonNull)

View file

@ -17,7 +17,6 @@
package org.keycloak.models.map.clientscope;
import java.util.Comparator;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
@ -38,6 +37,8 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.utils.KeycloakModelUtils;
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;
public class MapClientScopeProvider<K> implements ClientScopeProvider {
@ -46,8 +47,6 @@ public class MapClientScopeProvider<K> implements ClientScopeProvider {
private final MapKeycloakTransaction<K, MapClientScopeEntity<K>, ClientScopeModel> tx;
private final MapStorage<K, MapClientScopeEntity<K>, ClientScopeModel> clientScopeStore;
private static final Comparator<MapClientScopeEntity> COMPARE_BY_NAME = Comparator.comparing(MapClientScopeEntity::getName);
public MapClientScopeProvider(KeycloakSession session, MapStorage<K, MapClientScopeEntity<K>, ClientScopeModel> clientScopeStore) {
this.session = session;
this.clientScopeStore = clientScopeStore;
@ -79,8 +78,7 @@ public class MapClientScopeProvider<K> implements ClientScopeProvider {
ModelCriteriaBuilder<ClientScopeModel> mcb = clientScopeStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
return tx.read(mcb)
.sorted(COMPARE_BY_NAME)
return tx.read(withCriteria(mcb).orderBy(SearchableFields.NAME, ASCENDING))
.map(entityToAdapterFunc(realm));
}
@ -91,7 +89,7 @@ public class MapClientScopeProvider<K> implements ClientScopeProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.NAME, Operator.EQ, name);
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Client scope with name '" + name + "' in realm " + realm.getName());
}

View file

@ -30,15 +30,18 @@ import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import java.util.Comparator;
import org.keycloak.models.map.storage.QueryParameters;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public class MapGroupProvider<K> implements GroupProvider {
@ -88,10 +91,10 @@ public class MapGroupProvider<K> implements GroupProvider {
@Override
public Stream<GroupModel> getGroupsStream(RealmModel realm) {
return getGroupsStreamInternal(realm, null);
return getGroupsStreamInternal(realm, null, null);
}
private Stream<GroupModel> getGroupsStreamInternal(RealmModel realm, UnaryOperator<ModelCriteriaBuilder<GroupModel>> modifier) {
private Stream<GroupModel> getGroupsStreamInternal(RealmModel realm, UnaryOperator<ModelCriteriaBuilder<GroupModel>> modifier, UnaryOperator<QueryParameters<GroupModel>> queryParametersModifier) {
LOG.tracef("getGroupsStream(%s)%s", realm, getShortStackTrace());
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
@ -100,9 +103,13 @@ public class MapGroupProvider<K> implements GroupProvider {
mcb = modifier.apply(mcb);
}
return tx.read(mcb)
QueryParameters<GroupModel> queryParameters = withCriteria(mcb).orderBy(SearchableFields.NAME, ASCENDING);
if (queryParametersModifier != null) {
queryParameters = queryParametersModifier.apply(queryParameters);
}
return tx.read(queryParameters)
.map(entityToAdapterFunc(realm))
.sorted(GroupModel.COMPARE_BY_NAME)
;
}
@ -116,11 +123,8 @@ public class MapGroupProvider<K> implements GroupProvider {
mcb = mcb.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
}
Stream<GroupModel> groupModelStream = tx.read(mcb)
.map(entityToAdapterFunc(realm))
.sorted(Comparator.comparing(GroupModel::getName));
return paginatedStream(groupModelStream, first, max);
return tx.read(withCriteria(mcb).pagination(first, max, SearchableFields.NAME))
.map(entityToAdapterFunc(realm));
}
@Override
@ -133,7 +137,7 @@ public class MapGroupProvider<K> implements GroupProvider {
mcb = mcb.compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null);
}
return tx.getCount(mcb);
return tx.getCount(withCriteria(mcb));
}
@Override
@ -142,45 +146,50 @@ public class MapGroupProvider<K> implements GroupProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
return tx.getCount(mcb);
return tx.getCount(withCriteria(mcb));
}
@Override
public Stream<GroupModel> getGroupsByRoleStream(RealmModel realm, RoleModel role, Integer firstResult, Integer maxResults) {
LOG.tracef("getGroupsByRole(%s, %s, %d, %d)%s", realm, role, firstResult, maxResults, getShortStackTrace());
Stream<GroupModel> groupModelStream = getGroupsStreamInternal(realm,
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId())
return getGroupsStreamInternal(realm,
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId()),
qp -> qp.offset(firstResult).limit(maxResults)
);
return paginatedStream(groupModelStream, firstResult, maxResults);
}
@Override
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm) {
LOG.tracef("getTopLevelGroupsStream(%s)%s", realm, getShortStackTrace());
return getGroupsStreamInternal(realm,
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null)
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.PARENT_ID, Operator.NOT_EXISTS),
null
);
}
@Override
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm, Integer firstResult, Integer maxResults) {
Stream<GroupModel> groupModelStream = getTopLevelGroupsStream(realm);
return paginatedStream(groupModelStream, firstResult, maxResults);
LOG.tracef("getTopLevelGroupsStream(%s, %s, %s)%s", realm, firstResult, maxResults, getShortStackTrace());
return getGroupsStreamInternal(realm,
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.PARENT_ID, Operator.NOT_EXISTS),
qp -> qp.offset(firstResult).limit(maxResults)
);
}
@Override
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
LOG.tracef("searchForGroupByNameStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace());
Stream<GroupModel> groupModelStream = getGroupsStreamInternal(realm,
(ModelCriteriaBuilder<GroupModel> mcb) -> mcb.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%")
);
final Stream<String> groups = paginatedStream(groupModelStream.map(GroupModel::getId), firstResult, maxResults);
return groups.map(id -> {
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
.map(MapGroupEntity::getId)
.map(groupStore.getKeyConvertor()::keyToString)
.map(id -> {
GroupModel groupById = session.groups().getGroupById(realm, id);
while (Objects.nonNull(groupById.getParentId())) {
groupById = session.groups().getGroupById(realm, groupById.getParentId());
@ -201,7 +210,7 @@ public class MapGroupProvider<K> implements GroupProvider {
.compare(SearchableFields.PARENT_ID, Operator.EQ, parentId)
.compare(SearchableFields.NAME, Operator.EQ, name);
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("Group with name '" + name + "' in realm " + realm.getName() + " already exists for requested parent" );
}
@ -243,7 +252,7 @@ public class MapGroupProvider<K> implements GroupProvider {
session.users().preRemove(realm, group);
realm.removeDefaultGroup(group);
group.getSubGroupsStream().forEach(subGroup -> session.groups().removeGroup(realm, subGroup));
group.getSubGroupsStream().collect(Collectors.toSet()).forEach(subGroup -> session.groups().removeGroup(realm, subGroup));
// TODO: ^^^^^^^ Up to here
@ -268,7 +277,7 @@ public class MapGroupProvider<K> implements GroupProvider {
.compare(SearchableFields.PARENT_ID, Operator.EQ, parentId)
.compare(SearchableFields.NAME, Operator.EQ, group.getName());
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.read(mcb)) {
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.read(withCriteria(mcb))) {
if (possibleSiblings.findAny().isPresent()) {
throw new ModelDuplicateException("Parent already contains subgroup named '" + group.getName() + "'");
}
@ -290,7 +299,7 @@ public class MapGroupProvider<K> implements GroupProvider {
.compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null)
.compare(SearchableFields.NAME, Operator.EQ, subGroup.getName());
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.read(mcb)) {
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.read(withCriteria(mcb))) {
if (possibleSiblings.findAny().isPresent()) {
throw new ModelDuplicateException("There is already a top level group named '" + subGroup.getName() + "'");
}
@ -304,7 +313,7 @@ public class MapGroupProvider<K> implements GroupProvider {
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId());
try (Stream<MapGroupEntity<K>> toRemove = tx.read(mcb)) {
try (Stream<MapGroupEntity<K>> toRemove = tx.read(withCriteria(mcb))) {
toRemove
.map(groupEntity -> session.groups().getGroupById(realm, groupEntity.getId().toString()))
.forEach(groupModel -> groupModel.deleteRoleMapping(role));

View file

@ -29,6 +29,7 @@ import java.util.function.Function;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
/**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
@ -66,7 +67,7 @@ public class MapUserLoginFailureProvider<K> implements UserLoginFailureProvider
LOG.tracef("getUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
return userLoginFailureTx.read(mcb)
return userLoginFailureTx.read(withCriteria(mcb))
.findFirst()
.map(userLoginFailureEntityToAdapterFunc(realm))
.orElse(null);
@ -80,7 +81,7 @@ public class MapUserLoginFailureProvider<K> implements UserLoginFailureProvider
LOG.tracef("addUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
MapUserLoginFailureEntity<K> userLoginFailureEntity = userLoginFailureTx.read(mcb).findFirst().orElse(null);
MapUserLoginFailureEntity<K> userLoginFailureEntity = userLoginFailureTx.read(withCriteria(mcb)).findFirst().orElse(null);
if (userLoginFailureEntity == null) {
userLoginFailureEntity = new MapUserLoginFailureEntity<>(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), realm.getId(), userId);
@ -99,7 +100,7 @@ public class MapUserLoginFailureProvider<K> implements UserLoginFailureProvider
LOG.tracef("removeUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -109,7 +110,7 @@ public class MapUserLoginFailureProvider<K> implements UserLoginFailureProvider
LOG.tracef("removeAllUserLoginFailures(%s)%s", realm, getShortStackTrace());
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override

View file

@ -38,6 +38,8 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.utils.KeycloakModelUtils;
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;
public class MapRealmProvider<K> implements RealmProvider {
@ -110,7 +112,7 @@ public class MapRealmProvider<K> implements RealmProvider {
ModelCriteriaBuilder<RealmModel> mcb = realmStore.createCriteriaBuilder()
.compare(SearchableFields.NAME, Operator.EQ, name);
K realmId = tx.read(mcb)
K realmId = tx.read(withCriteria(mcb))
.findFirst()
.map(MapRealmEntity<K>::getId)
.orElse(null);
@ -132,9 +134,8 @@ public class MapRealmProvider<K> implements RealmProvider {
}
private Stream<RealmModel> getRealmsStream(ModelCriteriaBuilder<RealmModel> mcb) {
return tx.read(mcb)
.map(this::entityToAdapter)
.sorted(RealmModel.COMPARE_BY_NAME);
return tx.read(withCriteria(mcb).orderBy(SearchableFields.NAME, ASCENDING))
.map(this::entityToAdapter);
}
@Override
@ -174,7 +175,7 @@ public class MapRealmProvider<K> implements RealmProvider {
ModelCriteriaBuilder<RealmModel> mcb = realmStore.createCriteriaBuilder()
.compare(SearchableFields.CLIENT_INITIAL_ACCESS, Operator.EXISTS);
tx.read(mcb)
tx.read(withCriteria(mcb))
.map(e -> registerEntityForChanges(tx, e))
.forEach(MapRealmEntity<K>::removeExpiredClientInitialAccesses);
}

View file

@ -25,14 +25,15 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import java.util.Comparator;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
import org.keycloak.models.map.storage.MapStorage;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel.SearchableFields;
@ -47,19 +48,6 @@ public class MapRoleProvider<K> implements RoleProvider {
final MapKeycloakTransaction<K, MapRoleEntity<K>, RoleModel> tx;
private final MapStorage<K, MapRoleEntity<K>, RoleModel> roleStore;
private static final Comparator<MapRoleEntity<?>> COMPARE_BY_NAME = new Comparator<MapRoleEntity<?>>() {
@Override
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
: r1 == null ? -1
: r2 == null ? 1
: r1.compareTo(r2);
}
};
public MapRoleProvider(KeycloakSession session, MapStorage<K, MapRoleEntity<K>, RoleModel> roleStore) {
this.session = session;
this.roleStore = roleStore;
@ -99,7 +87,12 @@ public class MapRoleProvider<K> implements RoleProvider {
@Override
public Stream<RoleModel> getRealmRolesStream(RealmModel realm, Integer first, Integer max) {
return paginatedStream(getRealmRolesStream(realm), first, max);
ModelCriteriaBuilder<RoleModel> mcb = roleStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.IS_CLIENT_ROLE, Operator.NE, true);
return tx.read(withCriteria(mcb).pagination(first, max, SearchableFields.NAME))
.map(entityToAdapterFunc(realm));
}
@Override
@ -108,8 +101,7 @@ public class MapRoleProvider<K> implements RoleProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.IS_CLIENT_ROLE, Operator.NE, true);
return tx.read(mcb)
.sorted(COMPARE_BY_NAME)
return tx.read(withCriteria(mcb).orderBy(SearchableFields.NAME, ASCENDING))
.map(entityToAdapterFunc(realm));
}
@ -136,7 +128,12 @@ public class MapRoleProvider<K> implements RoleProvider {
@Override
public Stream<RoleModel> getClientRolesStream(ClientModel client, Integer first, Integer max) {
return paginatedStream(getClientRolesStream(client), first, max);
ModelCriteriaBuilder<RoleModel> mcb = roleStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId())
.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId());
return tx.read(withCriteria(mcb).pagination(first, max, SearchableFields.NAME))
.map(entityToAdapterFunc(client.getRealm()));
}
@Override
@ -145,8 +142,7 @@ public class MapRoleProvider<K> implements RoleProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId())
.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId());
return tx.read(mcb)
.sorted(COMPARE_BY_NAME)
return tx.read(withCriteria(mcb).orderBy(SearchableFields.NAME, ASCENDING))
.map(entityToAdapterFunc(client.getRealm()));
}
@Override
@ -197,7 +193,7 @@ public class MapRoleProvider<K> implements RoleProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.NAME, Operator.ILIKE, name);
String roleId = tx.read(mcb)
String roleId = tx.read(withCriteria(mcb))
.map(entityToAdapterFunc(realm))
.map(RoleModel::getId)
.findFirst()
@ -218,7 +214,7 @@ public class MapRoleProvider<K> implements RoleProvider {
.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId())
.compare(SearchableFields.NAME, Operator.ILIKE, name);
String roleId = tx.read(mcb)
String roleId = tx.read(withCriteria(mcb))
.map(entityToAdapterFunc(client.getRealm()))
.map(RoleModel::getId)
.findFirst()
@ -254,10 +250,8 @@ public class MapRoleProvider<K> implements RoleProvider {
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
);
Stream<MapRoleEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_NAME);
return paginatedStream(s.map(entityToAdapterFunc(realm)), first, max);
return tx.read(withCriteria(mcb).pagination(first, max, SearchableFields.NAME))
.map(entityToAdapterFunc(realm));
}
@Override
@ -272,10 +266,8 @@ public class MapRoleProvider<K> implements RoleProvider {
roleStore.createCriteriaBuilder().compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"),
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
);
Stream<MapRoleEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_NAME);
return paginatedStream(s,first, max).map(entityToAdapterFunc(client.getRealm()));
return tx.read(withCriteria(mcb).pagination(first, max, SearchableFields.NAME))
.map(entityToAdapterFunc(client.getRealm()));
}
@Override

View file

@ -58,20 +58,19 @@ public interface MapKeycloakTransaction<K, V extends AbstractEntity<K>, M> exten
* transaction by methods {@link MapKeycloakTransaction#create}, {@link MapKeycloakTransaction#update},
* {@link MapKeycloakTransaction#delete}, etc.
*
* @param mcb criteria to filter values
* @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
*/
Stream<V> read(ModelCriteriaBuilder<M> mcb);
Stream<V> read(QueryParameters<M> queryParameters);
/**
* Returns a number of values present in the underlying storage that fulfill the given criteria with respect to
* changes done in the current transaction.
*
* @param mcb criteria to filter values
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return number of values present in the storage that fulfill the given criteria
*/
long getCount(ModelCriteriaBuilder<M> mcb);
long getCount(QueryParameters<M> queryParameters);
/**
* Instructs this transaction to force-update the {@code value} associated with the identifier {@code value.getId()} in the
@ -116,8 +115,9 @@ public interface MapKeycloakTransaction<K, V extends AbstractEntity<K>, M> exten
*
* @param artificialKey key to record the transaction with, must be a key that does not exist in this transaction to
* prevent collisions with other operations in this transaction
* @param mcb criteria to delete values
* @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, ModelCriteriaBuilder<M> mcb);
long delete(K artificialKey, QueryParameters<M> queryParameters);
}

View file

@ -20,12 +20,14 @@ 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.ModelCriteriaBuilder)}
* and bulk {@link #delete(org.keycloak.models.map.storage.ModelCriteriaBuilder)} operations,
* It contains basic object CRUD operations as well as bulk {@link #read(org.keycloak.models.map.storage.QueryParameters)}
* and bulk {@link #delete(org.keycloak.models.map.storage.QueryParameters)} operations,
* and operation for determining the number of the objects satisfying given criteria
* ({@link #getCount(org.keycloak.models.map.storage.ModelCriteriaBuilder)}).
* ({@link #getCount(org.keycloak.models.map.storage.QueryParameters)}).
*
* @author hmlnarik
* @param <K> Type of the primary key. Various storages can
@ -62,29 +64,27 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
* Returns stream of objects satisfying given {@code criteria} from the storage.
* The criteria are specified in the given criteria builder based on model properties.
*
* @param criteria Criteria filtering out the object, originally obtained
* from {@link #createCriteriaBuilder()} method of this object.
* If {@code null}, it returns an empty stream.
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return Stream of objects. Never returns {@code null}.
* @throws IllegalStateException If {@code criteria} is not compatible, i.e. has not been originally created
* by the {@link #createCriteriaBuilder()} method of this object.
*/
Stream<V> read(ModelCriteriaBuilder<M> criteria);
Stream<V> read(QueryParameters<M> queryParameters);
/**
* Returns the number of objects satisfying given {@code criteria} from the storage.
* The criteria are specified in the given criteria builder based on model properties.
*
* @param criteria
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return Number of objects. Never returns {@code null}.
* @throws IllegalStateException If {@code criteria} is not compatible, i.e. has not been originally created
* by the {@link #createCriteriaBuilder()} method of this object.
*/
long getCount(ModelCriteriaBuilder<M> criteria);
long getCount(QueryParameters<M> queryParameters);
/**
* Updates the object with the key of the {@code value}'s ID in the storage if it already exists.
* @param key Primary key of the object to update
*
* @param value Updated value
* @throws NullPointerException if the object or its {@code id} is {@code null}
* @see AbstractEntity#getId()
@ -100,12 +100,12 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
/**
* Deletes objects that match the given criteria.
* @param criteria
* @param queryParameters parameters for the query like firstResult, maxResult, requested ordering, etc.
* @return Number of removed objects (might return {@code -1} if not supported)
* @throws IllegalStateException If {@code criteria} is not compatible, i.e. has not been originally created
* by the {@link #createCriteriaBuilder()} method of this object.
*/
long delete(ModelCriteriaBuilder<M> criteria);
long delete(QueryParameters<M> queryParameters);
/**
@ -123,7 +123,6 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
*/
ModelCriteriaBuilder<M> createCriteriaBuilder();
/**
* Creates a {@code MapKeycloakTransaction} object that tracks a new transaction related to this storage.
* In case of JPA or similar, the transaction object might be supplied by the container (via JTA) or

View file

@ -0,0 +1,139 @@
package org.keycloak.models.map.storage;
import org.keycloak.storage.SearchableModelField;
import java.util.LinkedList;
import java.util.List;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
/**
* Wraps together parameters for querying storage e.g. number of results to return, requested order or filtering criteria
*
* @param <M> Provide entity specific type checking, for example, when we create {@code QueryParameters}
* instance for Users, M is equal to UserModel, hence we are not able, for example, to order result by a
* {@link SearchableModelField} defined for clients in {@link org.keycloak.models.ClientModel}.
*/
public class QueryParameters<M> {
private Integer offset;
private Integer limit;
private final List<OrderBy<M>> orderBy = new LinkedList<>();
private ModelCriteriaBuilder<M> mcb;
public QueryParameters() {
}
public QueryParameters(ModelCriteriaBuilder<M> mcb) {
this.mcb = mcb;
}
/**
* Creates a new {@code QueryParameters} instance initialized with {@link ModelCriteriaBuilder}
*
* @param mcb filtering criteria
* @param <M> model type
* @return a new {@code QueryParameters} instance
*/
public static <M> QueryParameters<M> withCriteria(ModelCriteriaBuilder<M> mcb) {
return new QueryParameters<>(mcb);
}
/**
* Sets pagination (offset, limit and orderBy) parameters to {@code QueryParameters}
*
* @param offset
* @param limit
* @param orderByAscField
* @return this object
*/
public QueryParameters<M> pagination(Integer offset, Integer limit, SearchableModelField<M> orderByAscField) {
this.offset = offset;
this.limit = limit;
this.orderBy.add(new OrderBy<>(orderByAscField, ASCENDING));
return this;
}
/**
* Sets orderBy parameter; can be called repeatedly; fields are stored in a list where the first field has highest
* priority when determining order; e.g. the second field is compared only when values for the first field are equal
*
* @param searchableModelField
* @return this object
*/
public QueryParameters<M> orderBy(SearchableModelField<M> searchableModelField, Order order) {
orderBy.add(new OrderBy<>(searchableModelField, order));
return this;
}
/**
* Sets offset parameter
*
* @param offset
* @return
*/
public QueryParameters<M> offset(Integer offset) {
this.offset = offset;
return this;
}
/**
* Sets limit parameter
*
* @param limit
* @return
*/
public QueryParameters<M> limit(Integer limit) {
this.limit = limit;
return this;
}
public Integer getOffset() {
return offset;
}
public Integer getLimit() {
return limit;
}
public ModelCriteriaBuilder<M> getModelCriteriaBuilder() {
return mcb;
}
public List<OrderBy<M>> getOrderBy() {
return orderBy;
}
/**
* Enum for ascending or descending ordering
*/
public enum Order {
ASCENDING,
DESCENDING
}
/**
* Wrapper class for a field with its {@code Order}, ascending or descending
*
* @param <M>
*/
public static class OrderBy<M> {
private final SearchableModelField<M> modelField;
private final Order order;
public OrderBy(SearchableModelField<M> modelField, Order order) {
this.modelField = modelField;
this.order = order;
}
public SearchableModelField<M> getModelField() {
return modelField;
}
public Order getOrder() {
return order;
}
}
}

View file

@ -30,6 +30,8 @@ import org.jboss.logging.Logger;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.QueryParameters;
import org.keycloak.utils.StreamsUtil;
public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>, M> implements MapKeycloakTransaction<K, V, M> {
@ -133,11 +135,11 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
* 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.
*
* @param mcb
* @param queryParameters
* @return
*/
@Override
public Stream<V> read(ModelCriteriaBuilder<M> mcb) {
public Stream<V> read(QueryParameters<M> queryParameters) {
Predicate<? super V> filterOutAllBulkDeletedObjects = tasks.values().stream()
.filter(BulkDeleteOperation.class::isInstance)
.map(BulkDeleteOperation.class::cast)
@ -145,7 +147,9 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
.reduce(Predicate::and)
.orElse(v -> true);
Stream<V> updatedAndNotRemovedObjectsStream = this.map.read(mcb)
ModelCriteriaBuilder<M> mcb = queryParameters.getModelCriteriaBuilder();
Stream<V> 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);
@ -159,12 +163,17 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
updatedAndNotRemovedObjectsStream
);
return res;
if (!queryParameters.getOrderBy().isEmpty()) {
res = res.sorted(MapFieldPredicates.getComparator(queryParameters.getOrderBy().stream()));
}
return StreamsUtil.paginatedStream(res, queryParameters.getOffset(), queryParameters.getLimit());
}
@Override
public long getCount(ModelCriteriaBuilder<M> mcb) {
return read(mcb).count();
public long getCount(QueryParameters<M> queryParameters) {
return read(queryParameters).count();
}
@Override
@ -210,11 +219,11 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
@Override
public long delete(K artificialKey, ModelCriteriaBuilder<M> mcb) {
public long delete(K artificialKey, QueryParameters<M> queryParameters) {
log.tracef("Adding operation DELETE_BULK");
// Remove all tasks that create / update / delete objects deleted by the bulk removal.
final BulkDeleteOperation bdo = new BulkDeleteOperation(mcb);
final BulkDeleteOperation bdo = new BulkDeleteOperation(queryParameters);
Predicate<V> filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects();
long res = 0;
for (Iterator<Entry<K, MapTaskWithValue>> it = tasks.entrySet().iterator(); it.hasNext();) {
@ -355,29 +364,29 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
private class BulkDeleteOperation extends MapTaskWithValue {
private final ModelCriteriaBuilder<M> mcb;
private final QueryParameters<M> queryParameters;
public BulkDeleteOperation(ModelCriteriaBuilder<M> mcb) {
public BulkDeleteOperation(QueryParameters<M> queryParameters) {
super(null);
this.mcb = mcb;
this.queryParameters = queryParameters;
}
@Override
@SuppressWarnings("unchecked")
public void execute() {
map.delete(mcb);
map.delete(queryParameters);
}
public Predicate<V> getFilterForNonDeletedObjects() {
if (! (mcb instanceof MapModelCriteriaBuilder)) {
if (! (queryParameters.getModelCriteriaBuilder() instanceof MapModelCriteriaBuilder)) {
return t -> true;
}
@SuppressWarnings("unchecked")
final MapModelCriteriaBuilder<K, V, M> mmcb = (MapModelCriteriaBuilder<K, V, M>) mcb;
final MapModelCriteriaBuilder<K, V, M> mmcb = (MapModelCriteriaBuilder<K, V, M>) queryParameters.getModelCriteriaBuilder();
Predicate<? super V> entityFilter = mmcb.getEntityFilter();
Predicate<? super K> keyFilter = ((MapModelCriteriaBuilder) mcb).getKeyFilter();
Predicate<? super K> keyFilter = mmcb.getKeyFilter();
return v -> v == null || ! (keyFilter.test(v.getId()) && entityFilter.test(v));
}
@ -387,7 +396,7 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity<K>
}
private long getCount() {
return map.getCount(mcb);
return map.getCount(queryParameters);
}
}
}

View file

@ -21,18 +21,23 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.QueryParameters;
import org.keycloak.storage.SearchableModelField;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder.UpdatePredicatesFunc;
import org.keycloak.models.map.storage.StringKeyConvertor;
import java.util.Iterator;
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder.UpdatePredicatesFunc;
import java.util.Objects;
import java.util.function.Predicate;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
/**
*
* @author hmlnarik
@ -74,10 +79,11 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
}
@Override
public long delete(ModelCriteriaBuilder<M> criteria) {
long res;
public long delete(QueryParameters<M> queryParameters) {
ModelCriteriaBuilder<M> criteria = queryParameters.getModelCriteriaBuilder();
if (criteria == null) {
res = store.size();
long res = store.size();
store.clear();
return res;
}
@ -88,15 +94,21 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
}
Predicate<? super K> keyFilter = b.getKeyFilter();
Predicate<? super V> entityFilter = b.getEntityFilter();
res = 0;
for (Iterator<Entry<K, V>> iterator = store.entrySet().iterator(); iterator.hasNext();) {
Entry<K, V> next = iterator.next();
if (keyFilter.test(next.getKey()) && entityFilter.test(next.getValue())) {
res++;
iterator.remove();
Stream<Entry<K, V>> storeStream = store.entrySet().stream();
final AtomicLong res = new AtomicLong(0);
if (!queryParameters.getOrderBy().isEmpty()) {
Comparator<V> comparator = MapFieldPredicates.getComparator(queryParameters.getOrderBy().stream());
storeStream = storeStream.sorted((entry1, entry2) -> comparator.compare(entry1.getValue(), entry2.getValue()));
}
}
return res;
paginatedStream(storeStream.filter(next -> keyFilter.test(next.getKey()) && entityFilter.test(next.getValue()))
, queryParameters.getOffset(), queryParameters.getLimit())
.peek(item -> {res.incrementAndGet();})
.map(Entry::getKey)
.forEach(store::remove);
return res.get();
}
@Override
@ -117,7 +129,9 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
}
@Override
public Stream<V> read(ModelCriteriaBuilder<M> criteria) {
public Stream<V> read(QueryParameters<M> queryParameters) {
ModelCriteriaBuilder<M> criteria = queryParameters.getModelCriteriaBuilder();
if (criteria == null) {
return Stream.empty();
}
@ -135,8 +149,8 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
}
@Override
public long getCount(ModelCriteriaBuilder<M> criteria) {
return read(criteria).count();
public long getCount(QueryParameters<M> queryParameters) {
return read(queryParameters).count();
}
}

View file

@ -57,6 +57,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
/**
*
* @author hmlnarik
@ -171,7 +173,7 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
LOG.debugf("Storing contents to %s", f.getCanonicalPath());
@SuppressWarnings("unchecked")
final ModelCriteriaBuilder readAllCriteria = store.createCriteriaBuilder();
Serialization.MAPPER.writeValue(f, store.read(readAllCriteria));
Serialization.MAPPER.writeValue(f, store.read(withCriteria(readAllCriteria)));
} else {
LOG.debugf("Not storing contents of %s because directory not set", mapName);
}

View file

@ -43,7 +43,10 @@ import org.keycloak.models.map.group.MapGroupEntity;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
import org.keycloak.models.map.realm.MapRealmEntity;
import org.keycloak.models.map.role.MapRoleEntity;
import org.keycloak.models.map.storage.QueryParameters;
import org.keycloak.storage.SearchableModelField;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder.UpdatePredicatesFunc;
@ -56,11 +59,11 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.storage.StorageId;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.keycloak.models.map.storage.CriterionNotSupportedException;
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
@ -89,10 +92,11 @@ public class MapFieldPredicates {
@SuppressWarnings("unchecked")
private static final Map<Class<?>, Map> PREDICATES = new HashMap<>();
private static final Map<SearchableModelField<?>, Comparator<?>> COMPARATORS = new HashMap<>();
static {
put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, MapRealmEntity::getName);
put(REALM_PREDICATES, RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, MapRealmEntity::getClientInitialAccesses);
putIncomparable(REALM_PREDICATES, RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, MapRealmEntity::getClientInitialAccesses);
put(REALM_PREDICATES, RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, MapFieldPredicates::checkRealmsWithComponentType);
put(CLIENT_PREDICATES, ClientModel.SearchableFields.REALM_ID, MapClientEntity::getRealmId);
@ -136,7 +140,7 @@ public class MapFieldPredicates {
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, MapResourceServerEntity::getId);
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, predicateForKeyField(MapResourceServerEntity::getId));
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(MapResourceEntity::getId));
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, MapResourceEntity::getName);
@ -207,7 +211,14 @@ public class MapFieldPredicates {
PREDICATES.put(UserLoginFailureModel.class, USER_LOGIN_FAILURE_PREDICATES);
}
private static <K, V extends AbstractEntity<K>, M> void put(
private static <K, V extends AbstractEntity<K>, M, L extends Comparable<L>> void put(
Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> map,
SearchableModelField<M> field, Function<V, L> extractor) {
COMPARATORS.put(field, Comparator.comparing(extractor));
map.put(field, (mcb, op, values) -> mcb.fieldCompare(op, extractor, values));
}
private static <K, V extends AbstractEntity<K>, M> void putIncomparable(
Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> map,
SearchableModelField<M> field, Function<V, Object> extractor) {
map.put(field, (mcb, op, values) -> mcb.fieldCompare(op, extractor, values));
@ -219,7 +230,7 @@ public class MapFieldPredicates {
map.put(field, function);
}
private static <V extends AbstractEntity<?>> Function<V, Object> predicateForKeyField(Function<V, Object> extractor) {
private static <V extends AbstractEntity<?>> Function<V, String> predicateForKeyField(Function<V, Object> extractor) {
return entity -> {
Object o = extractor.apply(entity);
return o == null ? null : o.toString();
@ -483,10 +494,35 @@ public class MapFieldPredicates {
protected static <K, V extends AbstractEntity<K>, M> Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> basePredicates(SearchableModelField<M> idField) {
Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> fieldPredicates = new HashMap<>();
fieldPredicates.put(idField, (o, op, values) -> o.idCompare(op, values));
fieldPredicates.put(idField, MapModelCriteriaBuilder::idCompare);
return fieldPredicates;
}
public static <K, V extends AbstractEntity<K>, M> Comparator<V> getComparator(QueryParameters.OrderBy<M> orderBy) {
SearchableModelField<M> searchableModelField = orderBy.getModelField();
QueryParameters.Order order = orderBy.getOrder();
@SuppressWarnings("unchecked")
Comparator<V> comparator = (Comparator<V>) COMPARATORS.get(searchableModelField);
if (comparator == null) {
throw new IllegalArgumentException("Comparator for field " + searchableModelField.getName() + " is not configured.");
}
if (order == QueryParameters.Order.DESCENDING) {
return comparator.reversed();
}
return comparator;
}
@SuppressWarnings("unchecked")
public static <K, V extends AbstractEntity<K>, M> Comparator<V> getComparator(Stream<QueryParameters.OrderBy<M>> ordering) {
return (Comparator<V>) ordering.map(MapFieldPredicates::getComparator)
.reduce(Comparator::thenComparing)
.orElseThrow(() -> new IllegalArgumentException("Cannot create comparator for " + ordering));
}
@SuppressWarnings("unchecked")
public static <K, V extends AbstractEntity<K>, M> Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> getPredicates(Class<M> clazz) {
return PREDICATES.get(clazz);

View file

@ -23,12 +23,15 @@ 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.storage.QueryParameters;
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;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
/**
* User session storage with a naive implementation of referential integrity in client to user session relation, restricted to
* ON DELETE CASCADE functionality.
@ -49,17 +52,19 @@ public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapSto
}
@Override
public long delete(K artificialKey, ModelCriteriaBuilder<UserSessionModel> mcb) {
Set<K> ids = read(mcb).map(AbstractEntity::getId).collect(Collectors.toSet());
public long delete(K artificialKey, QueryParameters<UserSessionModel> queryParameters) {
ModelCriteriaBuilder<UserSessionModel> mcb = queryParameters.getModelCriteriaBuilder();
Set<K> ids = read(queryParameters).map(AbstractEntity::getId).collect(Collectors.toSet());
ModelCriteriaBuilder<AuthenticatedClientSessionModel> csMcb = clientSessionStore.createCriteriaBuilder().compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, Operator.IN, ids);
clientSessionTr.delete(artificialKey, csMcb);
return super.delete(artificialKey, mcb);
clientSessionTr.delete(artificialKey, withCriteria(csMcb));
return super.delete(artificialKey, queryParameters);
}
@Override
public void delete(K key) {
ModelCriteriaBuilder<AuthenticatedClientSessionModel> csMcb = clientSessionStore.createCriteriaBuilder().compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, Operator.EQ, key);
clientSessionTr.delete(key, csMcb);
clientSessionTr.delete(key, withCriteria(csMcb));
super.delete(key);
}

View file

@ -65,8 +65,6 @@ public class MapUserEntity<K> implements AbstractEntity<K> {
private String serviceAccountClientLink;
private int notBefore;
static Comparator<MapUserEntity<?>> COMPARE_BY_USERNAME = Comparator.comparing(MapUserEntity::getUsername);
/**
* Flag signalizing that any of the setters has been meaningfully used.
*/

View file

@ -70,7 +70,8 @@ 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.utils.StreamsUtil.paginatedStream;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialStore.Streams {
@ -173,7 +174,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.IDP_AND_USER, Operator.EQ, socialProvider);
tx.read(mcb)
tx.read(withCriteria(mcb))
.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeFederatedIdentity(socialProvider));
}
@ -209,7 +210,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.IDP_AND_USER, Operator.EQ, socialLink.getIdentityProvider(), socialLink.getUserId());
return tx.read(mcb)
return tx.read(withCriteria(mcb))
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> {
@ -298,7 +299,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId())
.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.EQ, client.getId());
return tx.read(mcb)
return tx.read(withCriteria(mcb))
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> {
@ -321,7 +322,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.USERNAME, Operator.EQ, username);
if (tx.getCount(mcb) > 0) {
if (tx.getCount(withCriteria(mcb)) > 0) {
throw new ModelDuplicateException("User with username '" + username + "' in realm " + realm.getName() + " already exists" );
}
@ -362,7 +363,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
ModelCriteriaBuilder<UserModel> mcb = userStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -372,7 +373,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -382,7 +383,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.setFederationLink(null));
}
@ -396,7 +397,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeRolesMembership(roleId));
}
@ -410,7 +411,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
}
@ -424,7 +425,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeUserConsent(clientId));
}
@ -444,7 +445,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, clientScope.getRealm().getId())
.compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.flatMap(MapUserEntity::getUserConsents)
.forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId));
}
@ -462,7 +463,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, Operator.EQ, componentId);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
String providerIdS = new StorageId(componentId, "").getId();
s.forEach(removeConsentsForExternalClient(providerIdS));
}
@ -490,7 +491,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
ModelCriteriaBuilder<UserModel> mcb = userStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(entity -> entity.addRolesMembership(roleId));
}
@ -510,7 +511,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.USERNAME, Operator.ILIKE, username);
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(withCriteria(mcb))) {
return s.findFirst()
.map(entityToAdapterFunc(realm)).orElse(null);
}
@ -523,7 +524,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.EMAIL, Operator.EQ, email);
List<MapUserEntity<K>> usersWithEmail = tx.read(mcb)
List<MapUserEntity<K>> usersWithEmail = tx.read(withCriteria(mcb))
.filter(userEntity -> Objects.equals(userEntity.getEmail(), email))
.collect(Collectors.toList());
if (usersWithEmail.isEmpty()) return null;
@ -571,7 +572,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
mcb = mcb.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS);
}
return (int) tx.getCount(mcb);
return (int) tx.getCount(withCriteria(mcb));
}
@Override
@ -584,8 +585,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
mcb = mcb.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS);
}
return paginatedStream(tx.read(mcb)
.sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.USERNAME))
.map(entityToAdapterFunc(realm));
}
@ -701,10 +701,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
mcb = mcb.compare(SearchableFields.ASSIGNED_GROUP, Operator.IN, authorizedGroups);
}
Stream<MapUserEntity<K>> usersStream = tx.read(mcb)
.sorted(MapUserEntity.COMPARE_BY_USERNAME); // Sort before paginating
return paginatedStream(usersStream, firstResult, maxResults) // paginate if necessary
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.USERNAME))
.map(entityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -716,7 +713,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, group.getId());
return paginatedStream(tx.read(mcb).sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.USERNAME))
.map(entityToAdapterFunc(realm));
}
@ -727,8 +724,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ATTRIBUTE, Operator.EQ, attrName, attrValue);
return tx.read(mcb)
.sorted(MapUserEntity.COMPARE_BY_USERNAME)
return tx.read(withCriteria(mcb).orderBy(SearchableFields.USERNAME, ASCENDING))
.map(entityToAdapterFunc(realm));
}
@ -756,8 +752,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId());
return paginatedStream(tx.read(mcb)
.sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults)
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.USERNAME))
.map(entityToAdapterFunc(realm));
}

View file

@ -33,7 +33,6 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -48,9 +47,9 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
import static org.keycloak.models.UserSessionModel.SessionPersistenceState.TRANSIENT;
import static org.keycloak.models.map.common.MapStorageUtils.registerEntityForChanges;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
import static org.keycloak.models.map.userSession.SessionExpiration.setClientSessionExpiration;
import static org.keycloak.models.map.userSession.SessionExpiration.setUserSessionExpiration;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
/**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
@ -195,7 +194,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
.compare(AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, ModelCriteriaBuilder.Operator.EQ, client.getId())
.compare(AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, ModelCriteriaBuilder.Operator.EQ, offline);
return clientSessionTx.read(mcb)
return clientSessionTx.read(withCriteria(mcb))
.findFirst()
.map(clientEntityToAdapterFunc(client.getRealm(), client, userSession))
.orElse(null);
@ -258,7 +257,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
ModelCriteriaBuilder<UserSessionModel> mcb = realmAndOfflineCriteriaBuilder(realm, false)
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid);
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.findFirst()
.map(userEntityToAdapterFunc(realm))
.orElse(null);
@ -271,7 +270,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getUserSessionsStream(%s, %s)%s", realm, user, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -283,7 +282,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getUserSessionsStream(%s, %s)%s", realm, client, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -291,8 +290,16 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
@Override
public Stream<UserSessionModel> getUserSessionsStream(RealmModel realm, ClientModel client,
Integer firstResult, Integer maxResults) {
return paginatedStream(getUserSessionsStream(realm, client)
.sorted(Comparator.comparing(UserSessionModel::getLastSessionRefresh)), firstResult, maxResults);
LOG.tracef("getUserSessionsStream(%s, %s, %s, %s)%s", realm, client, firstResult, maxResults, getShortStackTrace());
ModelCriteriaBuilder<UserSessionModel> mcb = realmAndOfflineCriteriaBuilder(realm, false)
.compare(UserSessionModel.SearchableFields.CLIENT_ID, ModelCriteriaBuilder.Operator.EQ, client.getId());
return userSessionTx.read(withCriteria(mcb).pagination(firstResult, maxResults,
UserSessionModel.SearchableFields.LAST_SESSION_REFRESH))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@Override
@ -302,7 +309,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getUserSessionByBrokerUserIdStream(%s, %s)%s", realm, brokerUserId, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -314,7 +321,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getUserSessionByBrokerSessionId(%s, %s)%s", realm, brokerSessionId, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.findFirst()
.map(userEntityToAdapterFunc(realm))
.orElse(null);
@ -347,7 +354,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getActiveUserSessions(%s, %s)%s", realm, client, getShortStackTrace());
return userSessionTx.getCount(mcb);
return userSessionTx.getCount(withCriteria(mcb));
}
@Override
@ -356,7 +363,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getActiveClientSessionStats(%s, %s)%s", realm, offline, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull)
.map(UserSessionModel::getAuthenticatedClientSessions)
@ -375,7 +382,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("removeUserSession(%s, %s)%s", realm, session, getShortStackTrace());
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -386,7 +393,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("removeUserSessions(%s, %s)%s", realm, user, getShortStackTrace());
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -405,7 +412,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("removeUserSessions(%s)%s", realm, getShortStackTrace());
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
}
@Override
@ -462,7 +469,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
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);
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), withCriteria(mcb));
userSession.removeNote(CORRESPONDING_SESSION_ID);
}
}
@ -496,7 +503,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getOfflineUserSessionsStream(%s, %s)%s", realm, user, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -508,7 +515,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getOfflineUserSessionByBrokerSessionId(%s, %s)%s", realm, brokerSessionId, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.findFirst()
.map(userEntityToAdapterFunc(realm))
.orElse(null);
@ -521,7 +528,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getOfflineUserSessionByBrokerUserIdStream(%s, %s)%s", realm, brokerUserId, getShortStackTrace());
return userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull);
}
@ -533,7 +540,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getOfflineSessionsCount(%s, %s)%s", realm, client, getShortStackTrace());
return userSessionTx.getCount(mcb);
return userSessionTx.getCount(withCriteria(mcb));
}
@Override
@ -544,10 +551,10 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
LOG.tracef("getOfflineUserSessionsStream(%s, %s, %s, %s)%s", realm, client, firstResult, maxResults, getShortStackTrace());
return paginatedStream(userSessionTx.read(mcb)
return userSessionTx.read(withCriteria(mcb).pagination(firstResult, maxResults,
UserSessionModel.SearchableFields.LAST_SESSION_REFRESH))
.map(userEntityToAdapterFunc(realm))
.filter(Objects::nonNull)
.sorted(Comparator.comparing(UserSessionModel::getLastSessionRefresh)), firstResult, maxResults);
.filter(Objects::nonNull);
}
@Override
@ -595,7 +602,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid);
// check if it's an offline user session
MapUserSessionEntity<UK> userSessionEntity = userSessionTx.read(mcb).findFirst().orElse(null);
MapUserSessionEntity<UK> userSessionEntity = userSessionTx.read(withCriteria(mcb)).findFirst().orElse(null);
if (userSessionEntity != null) {
if (userSessionEntity.isOffline()) {
return Stream.of(userSessionEntity);
@ -604,7 +611,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
// no session found by the given ID, try to find by corresponding session ID
mcb = realmAndOfflineCriteriaBuilder(realm, true)
.compare(UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, ModelCriteriaBuilder.Operator.EQ, userSessionId);
return userSessionTx.read(mcb);
return userSessionTx.read(withCriteria(mcb));
}
// it's online user session so lookup offline user session by corresponding session id reference
@ -613,7 +620,7 @@ public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
UK uk = userSessionStore.getKeyConvertor().fromStringSafe(offlineUserSessionId);
mcb = realmAndOfflineCriteriaBuilder(realm, true)
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
return userSessionTx.read(mcb);
return userSessionTx.read(withCriteria(mcb));
}
return Stream.empty();

View file

@ -40,31 +40,6 @@ public class SearchableModelField<M> {
return fieldClass;
}
@Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + Objects.hashCode(this.name);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SearchableModelField<?> other = (SearchableModelField<?>) obj;
if ( ! Objects.equals(this.name, other.name)) {
return false;
}
return true;
}
@Override
public String toString() {
return "SearchableModelField " + name + " @ " + getClass().getTypeParameters()[0].getTypeName();