Enhance Map authz entities with REALM_ID (ResourceServer with CLIENT_ID) searchable field

Co-authored-by Michal Hajas <mhajas@redhat.com>

Closes #10883
This commit is contained in:
vramik 2022-03-23 08:46:01 +01:00 committed by Hynek Mlnařík
parent 1b1cf266eb
commit 0d83b51b20
42 changed files with 477 additions and 235 deletions

View file

@ -42,7 +42,7 @@ public class PermissionTicketAdapter implements PermissionTicket, CachedModel<Pe
@Override @Override
public PermissionTicket getDelegateForUpdate() { public PermissionTicket getDelegateForUpdate() {
if (updated == null) { if (updated == null) {
ResourceServer resourceServer = cacheSession.getResourceServerStoreDelegate().findById(cached.getResourceServerId()); ResourceServer resourceServer = cacheSession.getResourceServerStoreDelegate().findById(null, cached.getResourceServerId());
updated = cacheSession.getPermissionTicketStoreDelegate().findById(resourceServer, cached.getId()); updated = cacheSession.getPermissionTicketStoreDelegate().findById(resourceServer, cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
cacheSession.registerPermissionTicketInvalidation(cached.getId(), cached.getOwner(), cached.getRequester(), cached.getResourceId(), updated.getResource().getName(), cached.getScopeId(), cached.getResourceServerId()); cacheSession.registerPermissionTicketInvalidation(cached.getId(), cached.getOwner(), cached.getRequester(), cached.getResourceId(), updated.getResource().getName(), cached.getScopeId(), cached.getResourceServerId());
@ -70,7 +70,7 @@ public class PermissionTicketAdapter implements PermissionTicket, CachedModel<Pe
protected boolean isUpdated() { protected boolean isUpdated() {
if (updated != null) return true; if (updated != null) return true;
if (!invalidated) return false; if (!invalidated) return false;
ResourceServer resourceServer = cacheSession.getResourceServerStoreDelegate().findById(cached.getResourceServerId()); ResourceServer resourceServer = cacheSession.getResourceServerStoreDelegate().findById(null, cached.getResourceServerId());
updated = cacheSession.getPermissionTicketStoreDelegate().findById(resourceServer, cached.getId()); updated = cacheSession.getPermissionTicketStoreDelegate().findById(resourceServer, cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
return true; return true;
@ -122,13 +122,13 @@ public class PermissionTicketAdapter implements PermissionTicket, CachedModel<Pe
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); return cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId());
} }
@Override @Override
public Policy getPolicy() { public Policy getPolicy() {
if (isUpdated()) return updated.getPolicy(); if (isUpdated()) return updated.getPolicy();
return cacheSession.getPolicyStore().findById(cacheSession.getResourceServerStore().findById(cached.getResourceServerId()), cached.getPolicy()); return cacheSession.getPolicyStore().findById(cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId()), cached.getPolicy());
} }
@Override @Override

View file

@ -85,7 +85,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
protected boolean isUpdated() { protected boolean isUpdated() {
if (updated != null) return true; if (updated != null) return true;
if (!invalidated) return false; if (!invalidated) return false;
updated = cacheSession.getPolicyStoreDelegate().findById(cacheSession.getResourceServerStore().findById(cached.getResourceServerId()), cached.getId()); updated = cacheSession.getPolicyStoreDelegate().findById(cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId()), cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
return true; return true;
} }
@ -112,7 +112,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); return cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId());
} }
@Override @Override
@ -208,7 +208,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
PolicyStore policyStore = cacheSession.getPolicyStore(); PolicyStore policyStore = cacheSession.getPolicyStore();
String resourceServerId = cached.getResourceServerId(); String resourceServerId = cached.getResourceServerId();
for (String id : cached.getAssociatedPoliciesIds(modelSupplier)) { for (String id : cached.getAssociatedPoliciesIds(modelSupplier)) {
Policy policy = policyStore.findById(cacheSession.getResourceServerStore().findById(resourceServerId), id); Policy policy = policyStore.findById(cacheSession.getResourceServerStore().findById(null, resourceServerId), id);
cacheSession.cachePolicy(policy); cacheSession.cachePolicy(policy);
associatedPolicies.add(policy); associatedPolicies.add(policy);
} }
@ -325,6 +325,6 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
} }
private Policy getPolicyModel() { private Policy getPolicyModel() {
return cacheSession.getPolicyStoreDelegate().findById(cacheSession.getResourceServerStore().findById(cached.getResourceServerId()), cached.getId()); return cacheSession.getPolicyStoreDelegate().findById(cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId()), cached.getId());
} }
} }

View file

@ -134,7 +134,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStoreDelegate().findById(cached.getResourceServerId()); return cacheSession.getResourceServerStoreDelegate().findById(null, cached.getResourceServerId());
} }
@Override @Override

View file

@ -18,6 +18,8 @@ package org.keycloak.models.cache.infinispan.authorization;
import org.keycloak.authorization.model.CachedModel; import org.keycloak.authorization.model.CachedModel;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer; import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
@ -40,7 +42,7 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
public ResourceServer getDelegateForUpdate() { public ResourceServer getDelegateForUpdate() {
if (updated == null) { if (updated == null) {
cacheSession.registerResourceServerInvalidation(cached.getId()); cacheSession.registerResourceServerInvalidation(cached.getId());
updated = cacheSession.getResourceServerStoreDelegate().findById(cached.getId()); updated = cacheSession.getResourceServerStoreDelegate().findById(null, cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
} }
return updated; return updated;
@ -67,7 +69,7 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
protected boolean isUpdated() { protected boolean isUpdated() {
if (updated != null) return true; if (updated != null) return true;
if (!invalidated) return false; if (!invalidated) return false;
updated = cacheSession.getResourceServerStoreDelegate().findById(cached.getId()); updated = cacheSession.getResourceServerStoreDelegate().findById(null, cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database"); if (updated == null) throw new IllegalStateException("Not found in database");
return true; return true;
} }
@ -116,6 +118,16 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
updated.setDecisionStrategy(decisionStrategy); updated.setDecisionStrategy(decisionStrategy);
} }
@Override
public String getClientId() {
return getId();
}
@Override
public RealmModel getRealm() {
return getDelegateForUpdate().getRealm();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View file

@ -118,7 +118,7 @@ public class ScopeAdapter implements Scope, CachedModel<Scope> {
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStore().findById(cached.getResourceServerId()); return cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId());
} }
@Override @Override

View file

@ -47,6 +47,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPermissionTicket; import org.keycloak.models.cache.infinispan.authorization.entities.CachedPermissionTicket;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy; import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
@ -309,7 +310,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
return Collections.emptySet(); return Collections.emptySet();
} }
ResourceServer resourceServer = getResourceServerStore().findById(serverId); ResourceServer resourceServer = getResourceServerStore().findById(null, serverId);
return resources.stream().map(resourceId -> { return resources.stream().map(resourceId -> {
Resource resource = getResourceStore().findById(resourceServer, resourceId); Resource resource = getResourceStore().findById(resourceServer, resourceId);
String type = resource.getType(); String type = resource.getType();
@ -450,7 +451,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
public void delete(ClientModel client) { public void delete(ClientModel client) {
String id = client.getId(); String id = client.getId();
if (id == null) return; if (id == null) return;
ResourceServer server = findById(id); ResourceServer server = findById(null, id);
if (server == null) return; if (server == null) return;
cache.invalidateObject(id); cache.invalidateObject(id);
@ -461,7 +462,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
} }
@Override @Override
public ResourceServer findById(String id) { public ResourceServer findById(RealmModel realm, String id) {
if (id == null) return null; if (id == null) return null;
CachedResourceServer cached = cache.get(id, CachedResourceServer.class); CachedResourceServer cached = cache.get(id, CachedResourceServer.class);
if (cached != null) { if (cached != null) {
@ -471,7 +472,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
if (cached == null) { if (cached == null) {
Long loaded = cache.getCurrentRevision(id); Long loaded = cache.getCurrentRevision(id);
if (! modelMightExist(id)) return null; if (! modelMightExist(id)) return null;
ResourceServer model = getResourceServerStoreDelegate().findById(id); ResourceServer model = getResourceServerStoreDelegate().findById(realm, id);
if (model == null) { if (model == null) {
setModelDoesNotExists(id, loaded); setModelDoesNotExists(id, loaded);
return null; return null;
@ -480,7 +481,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
cached = new CachedResourceServer(loaded, model); cached = new CachedResourceServer(loaded, model);
cache.addRevisioned(cached, startupRevision); cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) { } else if (invalidations.contains(id)) {
return getResourceServerStoreDelegate().findById(id); return getResourceServerStoreDelegate().findById(realm, id);
} else if (managedResourceServers.containsKey(id)) { } else if (managedResourceServers.containsKey(id)) {
return managedResourceServers.get(id); return managedResourceServers.get(id);
} }
@ -491,7 +492,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override @Override
public ResourceServer findByClient(ClientModel client) { public ResourceServer findByClient(ClientModel client) {
return findById(client.getId()); return findById(null, client.getId());
} }
} }
@ -1238,13 +1239,13 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
} }
@Override @Override
public List<Resource> findGrantedResources(String requester, String name, Integer first, Integer max) { public List<Resource> findGrantedResources(RealmModel realm, String requester, String name, Integer first, Integer max) {
return getPermissionTicketStoreDelegate().findGrantedResources(requester, name, first, max); return getPermissionTicketStoreDelegate().findGrantedResources(realm, requester, name, first, max);
} }
@Override @Override
public List<Resource> findGrantedOwnerResources(String owner, Integer firstResult, Integer maxResults) { public List<Resource> findGrantedOwnerResources(RealmModel realm, String owner, Integer firstResult, Integer maxResults) {
return getPermissionTicketStoreDelegate().findGrantedOwnerResources(owner, firstResult, maxResults); return getPermissionTicketStoreDelegate().findGrantedOwnerResources(realm, owner, firstResult, maxResults);
} }
@Override @Override

View file

@ -41,6 +41,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PermissionTicketStore; import org.keycloak.authorization.store.PermissionTicketStore;
import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.LockModeType; import javax.persistence.LockModeType;
@ -290,7 +291,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
} }
@Override @Override
public List<Resource> findGrantedResources(String requester, String name, Integer first, Integer max) { public List<Resource> findGrantedResources(RealmModel realm, String requester, String name, Integer first, Integer max) {
TypedQuery<String> query = name == null ? TypedQuery<String> query = name == null ?
entityManager.createNamedQuery("findGrantedResources", String.class) : entityManager.createNamedQuery("findGrantedResources", String.class) :
entityManager.createNamedQuery("findGrantedResourcesByName", String.class); entityManager.createNamedQuery("findGrantedResourcesByName", String.class);
@ -318,7 +319,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
} }
@Override @Override
public List<Resource> findGrantedOwnerResources(String owner, Integer firstResult, Integer maxResults) { public List<Resource> findGrantedOwnerResources(RealmModel realm, String owner, Integer firstResult, Integer maxResults) {
TypedQuery<String> query = entityManager.createNamedQuery("findGrantedOwnerResources", String.class); TypedQuery<String> query = entityManager.createNamedQuery("findGrantedOwnerResources", String.class);
query.setFlushMode(FlushModeType.COMMIT); query.setFlushMode(FlushModeType.COMMIT);

View file

@ -26,6 +26,7 @@ import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId; import org.keycloak.storage.StorageId;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@ -58,7 +59,7 @@ public class JPAResourceServerStore implements ResourceServerStore {
this.entityManager.persist(entity); this.entityManager.persist(entity);
return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory()); return new ResourceServerAdapter(client.getRealm(), entity, entityManager, provider.getStoreFactory());
} }
@Override @Override
@ -122,14 +123,14 @@ public class JPAResourceServerStore implements ResourceServerStore {
} }
@Override @Override
public ResourceServer findById(String id) { public ResourceServer findById(RealmModel realm, String id) {
ResourceServerEntity entity = entityManager.find(ResourceServerEntity.class, id); ResourceServerEntity entity = entityManager.find(ResourceServerEntity.class, id);
if (entity == null) return null; if (entity == null) return null;
return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory()); return new ResourceServerAdapter(provider.getRealm(), entity, entityManager, provider.getStoreFactory());
} }
@Override @Override
public ResourceServer findByClient(ClientModel client) { public ResourceServer findByClient(ClientModel client) {
return findById(client.getId()); return findById(null, client.getId());
} }
} }

View file

@ -20,7 +20,6 @@ import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.PermissionTicketEntity; import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
import org.keycloak.authorization.jpa.entities.PolicyEntity; import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity; import org.keycloak.authorization.jpa.entities.ScopeEntity;
@ -91,7 +90,7 @@ public class PermissionTicketAdapter implements PermissionTicket, JpaModel<Permi
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); return storeFactory.getResourceServerStore().findById(null, entity.getResourceServer().getId());
} }
@Override @Override
@ -102,7 +101,7 @@ public class PermissionTicketAdapter implements PermissionTicket, JpaModel<Permi
return null; return null;
} }
ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(null, entity.getResourceServer().getId());
return storeFactory.getPolicyStore().findById(resourceServer, policy.getId()); return storeFactory.getPolicyStore().findById(resourceServer, policy.getId());
} }

View file

@ -153,7 +153,7 @@ public class PolicyAdapter extends AbstractAuthorizationModel implements Policy,
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); return storeFactory.getResourceServerStore().findById(null, entity.getResourceServer().getId());
} }
@Override @Override

View file

@ -138,7 +138,7 @@ public class ResourceAdapter extends AbstractAuthorizationModel implements Resou
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServer()); return storeFactory.getResourceServerStore().findById(null, entity.getResourceServer());
} }
@Override @Override

View file

@ -20,6 +20,7 @@ import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
import org.keycloak.authorization.model.AbstractAuthorizationModel; import org.keycloak.authorization.model.AbstractAuthorizationModel;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.jpa.JpaModel; import org.keycloak.models.jpa.JpaModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
@ -35,7 +36,7 @@ public class ResourceServerAdapter extends AbstractAuthorizationModel implements
private EntityManager em; private EntityManager em;
private StoreFactory storeFactory; private StoreFactory storeFactory;
public ResourceServerAdapter(ResourceServerEntity entity, EntityManager em, StoreFactory storeFactory) { public ResourceServerAdapter(RealmModel realm, ResourceServerEntity entity, EntityManager em, StoreFactory storeFactory) {
super(storeFactory); super(storeFactory);
this.entity = entity; this.entity = entity;
this.em = em; this.em = em;
@ -87,6 +88,16 @@ public class ResourceServerAdapter extends AbstractAuthorizationModel implements
entity.setDecisionStrategy(decisionStrategy); entity.setDecisionStrategy(decisionStrategy);
} }
@Override
public String getClientId() {
return getId();
}
@Override
public RealmModel getRealm() {
return null;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View file

@ -88,7 +88,7 @@ public class ScopeAdapter extends AbstractAuthorizationModel implements Scope, J
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId()); return storeFactory.getResourceServerStore().findById(null, entity.getResourceServer().getId());
} }
public static ScopeEntity toEntity(EntityManager em, Scope scope) { public static ScopeEntity toEntity(EntityManager em, Scope scope) {

View file

@ -18,23 +18,8 @@
package org.keycloak.models.map.authorization; package org.keycloak.models.map.authorization;
import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PermissionTicketStore;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
@ -43,13 +28,14 @@ import org.keycloak.models.map.storage.MapStorage;
*/ */
public class MapAuthorizationStore implements StoreFactory { public class MapAuthorizationStore implements StoreFactory {
private final PolicyStore policyStore; private final MapPolicyStore policyStore;
private final ResourceServerStore resourceServerStore; private final MapResourceServerStore resourceServerStore;
private final ResourceStore resourceStore; private final MapResourceStore resourceStore;
private final ScopeStore scopeStore; private final MapScopeStore scopeStore;
private final PermissionTicketStore permissionTicketStore; private final MapPermissionTicketStore permissionTicketStore;
private boolean readOnly; private boolean readOnly;
@SuppressWarnings("unchecked")
public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) { public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) {
this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider); this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider);
this.policyStore = new MapPolicyStore(session, policyStore, provider); this.policyStore = new MapPolicyStore(session, policyStore, provider);
@ -59,27 +45,27 @@ public class MapAuthorizationStore implements StoreFactory {
} }
@Override @Override
public ResourceStore getResourceStore() { public MapResourceStore getResourceStore() {
return resourceStore; return resourceStore;
} }
@Override @Override
public ResourceServerStore getResourceServerStore() { public MapResourceServerStore getResourceServerStore() {
return resourceServerStore; return resourceServerStore;
} }
@Override @Override
public ScopeStore getScopeStore() { public MapScopeStore getScopeStore() {
return scopeStore; return scopeStore;
} }
@Override @Override
public PolicyStore getPolicyStore() { public MapPolicyStore getPolicyStore() {
return policyStore; return policyStore;
} }
@Override @Override
public PermissionTicketStore getPermissionTicketStore() { public MapPermissionTicketStore getPermissionTicketStore() {
return permissionTicketStore; return permissionTicketStore;
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.models.map.authorization; package org.keycloak.models.map.authorization;
import java.util.concurrent.atomic.AtomicInteger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket; import org.keycloak.authorization.model.PermissionTicket;
@ -29,49 +30,48 @@ import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.component.AmphibianProviderFactory; import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.map.common.AbstractMapProviderFactory;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.MapStorageProvider; import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.MapStorageProviderFactory; import org.keycloak.models.map.storage.MapStorageProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi; import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.InvalidationHandler;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.RESOURCE_SERVER_BEFORE_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.uniqueCounter;
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory; import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
/** /**
* @author mhajas * @author mhajas
*/ */
public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<StoreFactory>, AuthorizationStoreFactory, EnvironmentDependentProviderFactory { public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<StoreFactory>, AuthorizationStoreFactory, EnvironmentDependentProviderFactory, InvalidationHandler {
public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID; public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID;
private Config.Scope storageConfigScope; private Config.Scope storageConfigScope;
private final String uniqueKey = MapAuthorizationStoreFactory.class.getName() + uniqueCounter.incrementAndGet();
@Override @Override
public StoreFactory create(KeycloakSession session) { public StoreFactory create(KeycloakSession session) {
MapAuthorizationStore authzStore = session.getAttribute(uniqueKey, MapAuthorizationStore.class);
if (authzStore != null) return authzStore;
MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(), MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME); MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session); final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session);
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class); AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
MapStorage permissionTicketStore = mapStorageProvider.getStorage(PermissionTicket.class);
MapStorage policyStore = mapStorageProvider.getStorage(Policy.class);
MapStorage resourceServerStore = mapStorageProvider.getStorage(ResourceServer.class);
MapStorage resourceStore = mapStorageProvider.getStorage(Resource.class);
MapStorage scopeStore = mapStorageProvider.getStorage(Scope.class);
MapStorage permissionTicketStore; authzStore = new MapAuthorizationStore(session,
MapStorage policyStore;
MapStorage resourceServerStore;
MapStorage resourceStore;
MapStorage scopeStore;
permissionTicketStore = mapStorageProvider.getStorage(PermissionTicket.class);
policyStore = mapStorageProvider.getStorage(Policy.class);
resourceServerStore = mapStorageProvider.getStorage(ResourceServer.class);
resourceStore = mapStorageProvider.getStorage(Resource.class);
scopeStore = mapStorageProvider.getStorage(Scope.class);
return new MapAuthorizationStore(session,
permissionTicketStore, permissionTicketStore,
policyStore, policyStore,
resourceServerStore, resourceServerStore,
@ -79,6 +79,9 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
scopeStore, scopeStore,
provider provider
); );
session.setAttribute(uniqueKey, authzStore);
return authzStore;
} }
@Override @Override
@ -86,11 +89,6 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
this.storageConfigScope = config.scope("storage"); this.storageConfigScope = config.scope("storage");
} }
@Override
public void close() {
}
@Override @Override
public String getId() { public String getId() {
return PROVIDER_ID; return PROVIDER_ID;
@ -105,4 +103,26 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
public boolean isSupported() { public boolean isSupported() {
return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE); return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
} }
@Override
public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) {
if (type == REALM_BEFORE_REMOVE) {
MapAuthorizationStore authorizationStore = (MapAuthorizationStore) session.getProvider(StoreFactory.class);
RealmModel realm = (RealmModel) params[0];
authorizationStore.getScopeStore().preRemove(realm);
authorizationStore.getPolicyStore().preRemove(realm);
authorizationStore.getResourceStore().preRemove(realm);
authorizationStore.getPermissionTicketStore().preRemove(realm);
authorizationStore.getResourceServerStore().preRemove(realm);
} else if (type == RESOURCE_SERVER_BEFORE_REMOVE) {
MapAuthorizationStore authorizationStore = (MapAuthorizationStore) session.getProvider(StoreFactory.class);
ResourceServer resourceServer = (ResourceServer) params[0];
authorizationStore.getScopeStore().preRemove(resourceServer);
authorizationStore.getPolicyStore().preRemove(resourceServer);
authorizationStore.getResourceStore().preRemove(resourceServer);
authorizationStore.getPermissionTicketStore().preRemove(resourceServer);
}
}
} }

View file

@ -31,14 +31,15 @@ import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter; import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity; import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntityImpl; import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List; import java.util.List;
@ -59,17 +60,22 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class); private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class);
private final AuthorizationProvider authorizationProvider; private final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapPermissionTicketEntity, PermissionTicket> tx; final MapKeycloakTransaction<MapPermissionTicketEntity, PermissionTicket> tx;
private final KeycloakSession session;
public MapPermissionTicketStore(KeycloakSession session, MapStorage<MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) { public MapPermissionTicketStore(KeycloakSession session, MapStorage<MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) {
this.authorizationProvider = provider; this.authorizationProvider = provider;
this.tx = permissionTicketStore.createTransaction(session); this.tx = permissionTicketStore.createTransaction(session);
session.getTransactionManager().enlist(tx); session.getTransactionManager().enlist(tx);
this.session = session;
} }
private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) { private Function<MapPermissionTicketEntity, PermissionTicket> entityToAdapterFunc(ResourceServer resourceServer) {
if (origEntity == null) return null; return origEntity -> new MapPermissionTicketAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, origEntity, authorizationProvider.getStoreFactory());
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller }
return new MapPermissionTicketAdapter(origEntity, authorizationProvider.getStoreFactory());
private ResourceServer findResourceServer(MapPermissionTicketEntity entity) {
RealmModel realm = session.realms().getRealm(entity.getRealmId());
return authorizationProvider.getStoreFactory().getResourceServerStore().findById(realm, entity.getResourceServerId());
} }
private DefaultModelCriteria<PermissionTicket> forResourceServer(ResourceServer resourceServer) { private DefaultModelCriteria<PermissionTicket> forResourceServer(ResourceServer resourceServer) {
@ -124,10 +130,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
entity.setOwner(owner); entity.setOwner(owner);
entity.setResourceServerId(resourceServer.getId()); entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity); entity = tx.create(entity);
return entityToAdapter(entity); return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
} }
@Override @Override
@ -148,7 +155,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id))) .compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -157,7 +164,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace()); LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer))) return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -167,7 +174,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.OWNER, Operator.EQ, owner))) .compare(SearchableFields.OWNER, Operator.EQ, owner)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -177,7 +184,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId()))) .compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId())))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -187,7 +194,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.EQ, scope.getId()))) .compare(SearchableFields.SCOPE_ID, Operator.EQ, scope.getId())))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -216,7 +223,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
); );
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.ID)) return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.ID))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -272,10 +279,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
} }
@Override @Override
public List<Resource> findGrantedResources(String requester, String name, Integer first, Integer max) { public List<Resource> findGrantedResources(RealmModel realm, String requester, String name, Integer first, Integer max) {
DefaultModelCriteria<PermissionTicket> mcb = criteria(); DefaultModelCriteria<PermissionTicket> mcb = criteria();
mcb = mcb.compare(SearchableFields.REQUESTER, Operator.EQ, requester) mcb = mcb.compare(SearchableFields.REQUESTER, Operator.EQ, requester)
.compare(SearchableFields.GRANTED_TIMESTAMP, Operator.EXISTS); .compare(SearchableFields.GRANTED_TIMESTAMP, Operator.EXISTS)
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
Function<MapPermissionTicketEntity, Resource> ticketResourceMapper; Function<MapPermissionTicketEntity, Resource> ticketResourceMapper;
@ -288,13 +296,13 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId()}); filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId()});
filterOptionMap.put(Resource.FilterOption.NAME, new String[] {name}); filterOptionMap.put(Resource.FilterOption.NAME, new String[] {name});
List<Resource> resource = resourceStore.findByResourceServer(resourceServerStore.findById(ticket.getResourceServerId()), filterOptionMap, -1, 1); List<Resource> resource = resourceStore.findByResourceServer(resourceServerStore.findById(realm, ticket.getResourceServerId()), filterOptionMap, -1, 1);
return resource.isEmpty() ? null : resource.get(0); return resource.isEmpty() ? null : resource.get(0);
}; };
} else { } else {
ticketResourceMapper = ticket -> resourceStore ticketResourceMapper = ticket -> resourceStore
.findById(resourceServerStore.findById(ticket.getResourceServerId()), ticket.getResourceId()); .findById(resourceServerStore.findById(realm, ticket.getResourceServerId()), ticket.getResourceId());
} }
return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING)) return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING))
@ -305,16 +313,32 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
} }
@Override @Override
public List<Resource> findGrantedOwnerResources(String owner, Integer firstResult, Integer maxResults) { public List<Resource> findGrantedOwnerResources(RealmModel realm, String owner, Integer firstResult, Integer maxResults) {
DefaultModelCriteria<PermissionTicket> mcb = criteria(); DefaultModelCriteria<PermissionTicket> mcb = criteria();
mcb = mcb.compare(SearchableFields.OWNER, Operator.EQ, owner); mcb = mcb.compare(SearchableFields.OWNER, Operator.EQ, owner)
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore(); ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
ResourceServerStore resourceServerStore = authorizationProvider.getStoreFactory().getResourceServerStore(); ResourceServerStore resourceServerStore = authorizationProvider.getStoreFactory().getResourceServerStore();
return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING)) return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING))
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId)), firstResult, maxResults) .filter(distinctByKey(MapPermissionTicketEntity::getResourceId)), firstResult, maxResults)
.map(ticket -> resourceStore.findById(resourceServerStore.findById(ticket.getResourceServerId()), ticket.getResourceId())) .map(ticket -> resourceStore.findById(resourceServerStore.findById(realm, ticket.getResourceServerId()), ticket.getResourceId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public void preRemove(RealmModel realm) {
LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace());
DefaultModelCriteria<PermissionTicket> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(withCriteria(mcb));
}
public void preRemove(ResourceServer resourceServer) {
LOG.tracef("preRemove(%s)%s", resourceServer, getShortStackTrace());
tx.delete(withCriteria(forResourceServer(resourceServer)));
}
} }

View file

@ -27,6 +27,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter; import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity; import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntityImpl; import org.keycloak.models.map.authorization.entity.MapPolicyEntityImpl;
@ -41,6 +42,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.common.util.StackUtil.getShortStackTrace;
@ -52,17 +54,22 @@ public class MapPolicyStore implements PolicyStore {
private static final Logger LOG = Logger.getLogger(MapPolicyStore.class); private static final Logger LOG = Logger.getLogger(MapPolicyStore.class);
private final AuthorizationProvider authorizationProvider; private final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapPolicyEntity, Policy> tx; final MapKeycloakTransaction<MapPolicyEntity, Policy> tx;
private final KeycloakSession session;
public MapPolicyStore(KeycloakSession session, MapStorage<MapPolicyEntity, Policy> policyStore, AuthorizationProvider provider) { public MapPolicyStore(KeycloakSession session, MapStorage<MapPolicyEntity, Policy> policyStore, AuthorizationProvider provider) {
this.authorizationProvider = provider; this.authorizationProvider = provider;
this.tx = policyStore.createTransaction(session); this.tx = policyStore.createTransaction(session);
session.getTransactionManager().enlist(tx); session.getTransactionManager().enlist(tx);
this.session = session;
} }
private Policy entityToAdapter(MapPolicyEntity origEntity) { private Function<MapPolicyEntity, Policy> entityToAdapterFunc(ResourceServer resourceServer) {
if (origEntity == null) return null; return origEntity -> new MapPolicyAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, origEntity, authorizationProvider.getStoreFactory());
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller }
return new MapPolicyAdapter(origEntity, authorizationProvider.getStoreFactory());
private ResourceServer findResourceServer(MapPolicyEntity entity) {
RealmModel realm = session.realms().getRealm(entity.getRealmId());
return authorizationProvider.getStoreFactory().getResourceServerStore().findById(realm, entity.getResourceServerId());
} }
private DefaultModelCriteria<Policy> forResourceServer(ResourceServer resourceServer) { private DefaultModelCriteria<Policy> forResourceServer(ResourceServer resourceServer) {
@ -92,10 +99,11 @@ public class MapPolicyStore implements PolicyStore {
entity.setType(representation.getType()); entity.setType(representation.getType());
entity.setName(representation.getName()); entity.setName(representation.getName());
entity.setResourceServerId(resourceServer.getId()); entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity); entity = tx.create(entity);
return entityToAdapter(entity); return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
} }
@Override @Override
@ -111,7 +119,7 @@ public class MapPolicyStore implements PolicyStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id))) .compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -122,7 +130,7 @@ public class MapPolicyStore implements PolicyStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.NAME, Operator.EQ, name))) .compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -131,7 +139,7 @@ public class MapPolicyStore implements PolicyStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace()); LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer))) return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -201,7 +209,7 @@ public class MapPolicyStore implements PolicyStore {
tx.read(withCriteria(forResourceServer(resourceServer) tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId()))) .compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId())))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
@ -209,7 +217,7 @@ public class MapPolicyStore implements PolicyStore {
public void findByResourceType(ResourceServer resourceServer, String type, Consumer<Policy> policyConsumer) { public void findByResourceType(ResourceServer resourceServer, String type, Consumer<Policy> policyConsumer) {
tx.read(withCriteria(forResourceServer(resourceServer) tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[]{"defaultResourceType", type}))) .compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[]{"defaultResourceType", type})))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(policyConsumer); .forEach(policyConsumer);
} }
@ -217,7 +225,7 @@ public class MapPolicyStore implements PolicyStore {
public List<Policy> findByScopes(ResourceServer resourceServer, List<Scope> scopes) { public List<Policy> findByScopes(ResourceServer resourceServer, List<Scope> scopes) {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId)))) .compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId))))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -235,14 +243,14 @@ public class MapPolicyStore implements PolicyStore {
.compare(SearchableFields.CONFIG, Operator.NOT_EXISTS, (Object[]) new String[] {"defaultResourceType"}); .compare(SearchableFields.CONFIG, Operator.NOT_EXISTS, (Object[]) new String[] {"defaultResourceType"});
} }
tx.read(withCriteria(mcb)).map(this::entityToAdapter).forEach(consumer); tx.read(withCriteria(mcb)).map(entityToAdapterFunc(resourceServer)).forEach(consumer);
} }
@Override @Override
public List<Policy> findByType(ResourceServer resourceServer, String type) { public List<Policy> findByType(ResourceServer resourceServer, String type) {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.TYPE, Operator.EQ, type))) .compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -250,7 +258,22 @@ public class MapPolicyStore implements PolicyStore {
public List<Policy> findDependentPolicies(ResourceServer resourceServer, String id) { public List<Policy> findDependentPolicies(ResourceServer resourceServer, String id) {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id))) .compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public void preRemove(RealmModel realm) {
LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace());
DefaultModelCriteria<Policy> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(withCriteria(mcb));
}
public void preRemove(ResourceServer resourceServer) {
LOG.tracef("preRemove(%s)%s", resourceServer, getShortStackTrace());
tx.delete(withCriteria(forResourceServer(resourceServer)));
}
} }

View file

@ -19,28 +19,31 @@ package org.keycloak.models.map.authorization;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.ResourceServer.SearchableFields;
import org.keycloak.authorization.store.PermissionTicketStore;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapResourceServerAdapter; import org.keycloak.models.map.authorization.adapter.MapResourceServerAdapter;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity; import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntityImpl; import org.keycloak.models.map.authorization.entity.MapResourceServerEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import org.keycloak.storage.StorageId; import org.keycloak.storage.StorageId;
import java.util.Objects;
import java.util.function.Function;
import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.RESOURCE_SERVER_AFTER_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.RESOURCE_SERVER_BEFORE_REMOVE;
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
public class MapResourceServerStore implements ResourceServerStore { public class MapResourceServerStore implements ResourceServerStore {
@ -54,10 +57,8 @@ public class MapResourceServerStore implements ResourceServerStore {
session.getTransactionManager().enlist(tx); session.getTransactionManager().enlist(tx);
} }
private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) { private Function<MapResourceServerEntity, ResourceServer> entityToAdapterFunc(RealmModel realmModel) {
if (origEntity == null) return null; return origEntity -> new MapResourceServerAdapter(realmModel, origEntity, authorizationProvider.getStoreFactory());
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapResourceServerAdapter(origEntity, authorizationProvider.getStoreFactory());
} }
@Override @Override
@ -76,47 +77,29 @@ public class MapResourceServerStore implements ResourceServerStore {
} }
MapResourceServerEntity entity = new MapResourceServerEntityImpl(); MapResourceServerEntity entity = new MapResourceServerEntityImpl();
entity.setId(clientId); entity.setClientId(clientId);
entity.setRealmId(client.getRealm().getId());
entity = tx.create(entity); entity = tx.create(entity);
return entityToAdapter(entity); return entity == null ? null : entityToAdapterFunc(client.getRealm()).apply(entity);
} }
@Override @Override
public void delete(ClientModel client) { public void delete(ClientModel client) {
LOG.tracef("delete(%s, %s)%s", client.getClientId(), getShortStackTrace()); LOG.tracef("delete(%s)%s", client.getClientId(), getShortStackTrace());
ResourceServer resourceServer = findByClient(client); ResourceServer resourceServer = findByClient(client);
if (resourceServer == null) return; if (resourceServer == null) return;
String id = resourceServer.getId(); authorizationProvider.getKeycloakSession().invalidate(RESOURCE_SERVER_BEFORE_REMOVE, resourceServer);
// TODO: Simplify the following, ideally by leveraging triggers, stored procedures or ref integrity tx.delete(resourceServer.getId());
PolicyStore policyStore = authorizationProvider.getStoreFactory().getPolicyStore();
policyStore.findByResourceServer(resourceServer).stream()
.map(Policy::getId)
.forEach(policyStore::delete);
PermissionTicketStore permissionTicketStore = authorizationProvider.getStoreFactory().getPermissionTicketStore(); authorizationProvider.getKeycloakSession().invalidate(RESOURCE_SERVER_AFTER_REMOVE, resourceServer);
permissionTicketStore.findByResourceServer(resourceServer).stream()
.map(PermissionTicket::getId)
.forEach(permissionTicketStore::delete);
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
resourceStore.findByResourceServer(resourceServer).stream()
.map(Resource::getId)
.forEach(resourceStore::delete);
ScopeStore scopeStore = authorizationProvider.getStoreFactory().getScopeStore();
scopeStore.findByResourceServer(resourceServer).stream()
.map(Scope::getId)
.forEach(scopeStore::delete);
tx.delete(id);
} }
@Override @Override
public ResourceServer findById(String id) { public ResourceServer findById(RealmModel realm, String id) {
LOG.tracef("findById(%s)%s", id, getShortStackTrace()); LOG.tracef("findById(%s)%s", id, getShortStackTrace());
if (id == null) { if (id == null) {
@ -124,11 +107,29 @@ public class MapResourceServerStore implements ResourceServerStore {
} }
MapResourceServerEntity entity = tx.read(id); MapResourceServerEntity entity = tx.read(id);
return entityToAdapter(entity); return (entity == null || !Objects.equals(realm.getId(), entity.getRealmId())) ? null : entityToAdapterFunc(realm).apply(entity);
} }
@Override @Override
public ResourceServer findByClient(ClientModel client) { public ResourceServer findByClient(ClientModel client) {
return findById(client.getId()); LOG.tracef("findByClient(%s) in realm(%s)%s", client.getClientId(), client.getRealm().getName(), getShortStackTrace());
DefaultModelCriteria<ResourceServer> mcb = criteria();
mcb = mcb.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId());
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId());
return tx.read(withCriteria(mcb))
.map(entityToAdapterFunc(client.getRealm()))
.findFirst()
.orElse(null);
}
public void preRemove(RealmModel realm) {
LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace());
DefaultModelCriteria<ResourceServer> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(withCriteria(mcb));
} }
} }

View file

@ -26,20 +26,22 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapResourceAdapter; import org.keycloak.models.map.authorization.adapter.MapResourceAdapter;
import org.keycloak.models.map.authorization.entity.MapResourceEntity; import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntityImpl; import org.keycloak.models.map.authorization.entity.MapResourceEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.common.util.StackUtil.getShortStackTrace;
@ -51,17 +53,22 @@ public class MapResourceStore implements ResourceStore {
private static final Logger LOG = Logger.getLogger(MapResourceStore.class); private static final Logger LOG = Logger.getLogger(MapResourceStore.class);
private final AuthorizationProvider authorizationProvider; private final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapResourceEntity, Resource> tx; final MapKeycloakTransaction<MapResourceEntity, Resource> tx;
private final KeycloakSession session;
public MapResourceStore(KeycloakSession session, MapStorage<MapResourceEntity, Resource> resourceStore, AuthorizationProvider provider) { public MapResourceStore(KeycloakSession session, MapStorage<MapResourceEntity, Resource> resourceStore, AuthorizationProvider provider) {
this.tx = resourceStore.createTransaction(session); this.tx = resourceStore.createTransaction(session);
session.getTransactionManager().enlist(tx); session.getTransactionManager().enlist(tx);
authorizationProvider = provider; authorizationProvider = provider;
this.session = session;
} }
private Resource entityToAdapter(MapResourceEntity origEntity) { private Function<MapResourceEntity, Resource> entityToAdapterFunc(final ResourceServer resourceServer) {
if (origEntity == null) return null; return origEntity -> new MapResourceAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, origEntity, authorizationProvider.getStoreFactory());
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller }
return new MapResourceAdapter(origEntity, authorizationProvider.getStoreFactory());
private ResourceServer findResourceServer(MapResourceEntity entity) {
RealmModel realm = session.realms().getRealm(entity.getRealmId());
return authorizationProvider.getStoreFactory().getResourceServerStore().findById(realm, entity.getResourceServerId());
} }
private DefaultModelCriteria<Resource> forResourceServer(ResourceServer resourceServer) { private DefaultModelCriteria<Resource> forResourceServer(ResourceServer resourceServer) {
@ -90,10 +97,11 @@ public class MapResourceStore implements ResourceStore {
entity.setName(name); entity.setName(name);
entity.setResourceServerId(resourceServer.getId()); entity.setResourceServerId(resourceServer.getId());
entity.setOwner(owner); entity.setOwner(owner);
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity); entity = tx.create(entity);
return entityToAdapter(entity); return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
} }
@Override @Override
@ -110,7 +118,7 @@ public class MapResourceStore implements ResourceStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id))) .compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -124,7 +132,7 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.OWNER, Operator.EQ, ownerId)) tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.OWNER, Operator.EQ, ownerId))
.pagination(firstResult, maxResult, SearchableFields.ID) .pagination(firstResult, maxResult, SearchableFields.ID)
).map(this::entityToAdapter) ).map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
@ -143,7 +151,7 @@ public class MapResourceStore implements ResourceStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.URI, Operator.EQ, uri))) .compare(SearchableFields.URI, Operator.EQ, uri)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -152,7 +160,7 @@ public class MapResourceStore implements ResourceStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace()); LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer))) return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -166,7 +174,7 @@ public class MapResourceStore implements ResourceStore {
); );
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME)) return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -203,7 +211,7 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer) tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId)))) .compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId))))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
@ -214,7 +222,7 @@ public class MapResourceStore implements ResourceStore {
.compare(SearchableFields.OWNER, Operator.EQ, ownerId) .compare(SearchableFields.OWNER, Operator.EQ, ownerId)
.compare(SearchableFields.NAME, Operator.EQ, name))) .compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -223,7 +231,7 @@ public class MapResourceStore implements ResourceStore {
LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServer, consumer, getShortStackTrace()); LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServer, consumer, getShortStackTrace());
tx.read(withCriteria(forResourceServer(resourceServer) tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.TYPE, Operator.EQ, type))) .compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
@ -239,7 +247,7 @@ public class MapResourceStore implements ResourceStore {
} }
tx.read(withCriteria(mcb)) tx.read(withCriteria(mcb))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
@ -249,7 +257,22 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer) tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.OWNER, Operator.NE, resourceServer.getClientId()) .compare(SearchableFields.OWNER, Operator.NE, resourceServer.getClientId())
.compare(SearchableFields.TYPE, Operator.EQ, type))) .compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.forEach(consumer); .forEach(consumer);
} }
public void preRemove(RealmModel realm) {
LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace());
DefaultModelCriteria<Resource> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(withCriteria(mcb));
}
public void preRemove(ResourceServer resourceServer) {
LOG.tracef("preRemove(%s)%s", resourceServer, getShortStackTrace());
tx.delete(withCriteria(forResourceServer(resourceServer)));
}
} }

View file

@ -25,17 +25,19 @@ import org.keycloak.authorization.model.Scope.SearchableFields;
import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapScopeAdapter; import org.keycloak.models.map.authorization.adapter.MapScopeAdapter;
import org.keycloak.models.map.authorization.entity.MapScopeEntity; import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntityImpl; import org.keycloak.models.map.authorization.entity.MapScopeEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.keycloak.common.util.StackUtil.getShortStackTrace; import static org.keycloak.common.util.StackUtil.getShortStackTrace;
@ -47,17 +49,22 @@ public class MapScopeStore implements ScopeStore {
private static final Logger LOG = Logger.getLogger(MapScopeStore.class); private static final Logger LOG = Logger.getLogger(MapScopeStore.class);
private final AuthorizationProvider authorizationProvider; private final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapScopeEntity, Scope> tx; final MapKeycloakTransaction<MapScopeEntity, Scope> tx;
private final KeycloakSession session;
public MapScopeStore(KeycloakSession session, MapStorage<MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) { public MapScopeStore(KeycloakSession session, MapStorage<MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
this.authorizationProvider = provider; this.authorizationProvider = provider;
this.tx = scopeStore.createTransaction(session); this.tx = scopeStore.createTransaction(session);
session.getTransactionManager().enlist(tx); session.getTransactionManager().enlist(tx);
this.session = session;
} }
private Scope entityToAdapter(MapScopeEntity origEntity) { private Function<MapScopeEntity, Scope> entityToAdapterFunc(ResourceServer resourceServer) {
if (origEntity == null) return null; return origEntity -> new MapScopeAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, origEntity, authorizationProvider.getStoreFactory());
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller }
return new MapScopeAdapter(origEntity, authorizationProvider.getStoreFactory());
private ResourceServer findResourceServer(MapScopeEntity entity) {
RealmModel realm = session.realms().getRealm(entity.getRealmId());
return authorizationProvider.getStoreFactory().getResourceServerStore().findById(realm, entity.getResourceServerId());
} }
private DefaultModelCriteria<Scope> forResourceServer(ResourceServer resourceServer) { private DefaultModelCriteria<Scope> forResourceServer(ResourceServer resourceServer) {
@ -86,10 +93,11 @@ public class MapScopeStore implements ScopeStore {
entity.setId(id); entity.setId(id);
entity.setName(name); entity.setName(name);
entity.setResourceServerId(resourceServer.getId()); entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity); entity = tx.create(entity);
return entityToAdapter(entity); return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
} }
@Override @Override
@ -105,7 +113,7 @@ public class MapScopeStore implements ScopeStore {
return tx.read(withCriteria(forResourceServer(resourceServer) return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id))) .compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -116,7 +124,7 @@ public class MapScopeStore implements ScopeStore {
return tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.NAME, return tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.NAME,
Operator.EQ, name))) Operator.EQ, name)))
.findFirst() .findFirst()
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.orElse(null); .orElse(null);
} }
@ -125,7 +133,7 @@ public class MapScopeStore implements ScopeStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace()); LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer))) return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -149,7 +157,22 @@ public class MapScopeStore implements ScopeStore {
} }
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME)) return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
.map(this::entityToAdapter) .map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public void preRemove(RealmModel realm) {
LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace());
DefaultModelCriteria<Scope> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
tx.delete(withCriteria(mcb));
}
public void preRemove(ResourceServer resourceServer) {
LOG.tracef("preRemove(%s)%s", resourceServer, getShortStackTrace());
tx.delete(withCriteria(forResourceServer(resourceServer)));
}
} }

View file

@ -18,6 +18,7 @@
package org.keycloak.models.map.authorization.adapter; package org.keycloak.models.map.authorization.adapter;
import java.util.Objects;
import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
@ -30,8 +31,12 @@ import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy;
public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<MapPermissionTicketEntity> { public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<MapPermissionTicketEntity> {
public MapPermissionTicketAdapter(MapPermissionTicketEntity entity, StoreFactory storeFactory) { private final ResourceServer resourceServer;
public MapPermissionTicketAdapter(ResourceServer resourceServer, MapPermissionTicketEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory); super(entity, storeFactory);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
} }
@Override @Override
@ -51,13 +56,13 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
@Override @Override
public Resource getResource() { public Resource getResource() {
return storeFactory.getResourceStore().findById(getResourceServer(), entity.getResourceId()); return storeFactory.getResourceStore().findById(resourceServer, entity.getResourceId());
} }
@Override @Override
public Scope getScope() { public Scope getScope() {
if (entity.getScopeId() == null) return null; if (entity.getScopeId() == null) return null;
return storeFactory.getScopeStore().findById(getResourceServer(), entity.getScopeId()); return storeFactory.getScopeStore().findById(resourceServer, entity.getScopeId());
} }
@Override @Override
@ -83,13 +88,12 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId()); return resourceServer;
} }
@Override @Override
public Policy getPolicy() { public Policy getPolicy() {
if (entity.getPolicyId() == null) return null; if (entity.getPolicyId() == null) return null;
ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
return storeFactory.getPolicyStore().findById(resourceServer, entity.getPolicyId()); return storeFactory.getPolicyStore().findById(resourceServer, entity.getPolicyId());
} }

View file

@ -28,13 +28,18 @@ import org.keycloak.representations.idm.authorization.Logic;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> { public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
public MapPolicyAdapter(MapPolicyEntity entity, StoreFactory storeFactory) { private final ResourceServer resourceServer;
public MapPolicyAdapter(ResourceServer resourceServer, MapPolicyEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory); super(entity, storeFactory);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
} }
@Override @Override
@ -119,21 +124,19 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId()); return resourceServer;
} }
@Override @Override
public Set<Policy> getAssociatedPolicies() { public Set<Policy> getAssociatedPolicies() {
String resourceServerId = entity.getResourceServerId();
Set<String> ids = entity.getAssociatedPolicyIds(); Set<String> ids = entity.getAssociatedPolicyIds();
return ids == null ? Collections.emptySet() : ids.stream() return ids == null ? Collections.emptySet() : ids.stream()
.map(policyId -> storeFactory.getPolicyStore().findById(storeFactory.getResourceServerStore().findById(resourceServerId), policyId)) .map(policyId -> storeFactory.getPolicyStore().findById(resourceServer, policyId))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@Override @Override
public Set<Resource> getResources() { public Set<Resource> getResources() {
ResourceServer resourceServer = getResourceServer();
Set<String> ids = entity.getResourceIds(); Set<String> ids = entity.getResourceIds();
return ids == null ? Collections.emptySet() : ids.stream() return ids == null ? Collections.emptySet() : ids.stream()
.map(resourceId -> storeFactory.getResourceStore().findById(resourceServer, resourceId)) .map(resourceId -> storeFactory.getResourceStore().findById(resourceServer, resourceId))
@ -142,7 +145,6 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
@Override @Override
public Set<Scope> getScopes() { public Set<Scope> getScopes() {
ResourceServer resourceServer = getResourceServer();
Set<String> ids = entity.getScopeIds(); Set<String> ids = entity.getScopeIds();
return ids == null ? Collections.emptySet() : ids.stream() return ids == null ? Collections.emptySet() : ids.stream()
.map(scopeId -> storeFactory.getScopeStore().findById(resourceServer, scopeId)) .map(scopeId -> storeFactory.getScopeStore().findById(resourceServer, scopeId))

View file

@ -29,13 +29,18 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity> { public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity> {
public MapResourceAdapter(MapResourceEntity entity, StoreFactory storeFactory) { private final ResourceServer resourceServer;
public MapResourceAdapter(ResourceServer resourceServer, MapResourceEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory); super(entity, storeFactory);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
} }
@Override @Override
@ -111,7 +116,7 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId()); return resourceServer;
} }
@Override @Override
@ -143,13 +148,13 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
// The scope^ was removed from the Resource // The scope^ was removed from the Resource
// Remove permission tickets based on the scope // Remove permission tickets based on the scope
List<PermissionTicket> permissions = permissionStore.findByScope(getResourceServer(), scope); List<PermissionTicket> permissions = permissionStore.findByScope(resourceServer, scope);
for (PermissionTicket permission : permissions) { for (PermissionTicket permission : permissions) {
permissionStore.delete(permission.getId()); permissionStore.delete(permission.getId());
} }
// Remove the scope from each Policy for this Resource // Remove the scope from each Policy for this Resource
policyStore.findByResource(getResourceServer(), this, policy -> policy.removeScope(scope)); policyStore.findByResource(resourceServer, this, policy -> policy.removeScope(scope));
} }
} }

View file

@ -18,15 +18,21 @@
package org.keycloak.models.map.authorization.adapter; package org.keycloak.models.map.authorization.adapter;
import java.util.Objects;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity; import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
public class MapResourceServerAdapter extends AbstractResourceServerModel<MapResourceServerEntity> { public class MapResourceServerAdapter extends AbstractResourceServerModel<MapResourceServerEntity> {
public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) { private final RealmModel realmModel;
public MapResourceServerAdapter(RealmModel realmModel, MapResourceServerEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory); super(entity, storeFactory);
Objects.requireNonNull(realmModel);
this.realmModel = realmModel;
} }
@Override @Override
@ -70,6 +76,16 @@ public class MapResourceServerAdapter extends AbstractResourceServerModel<MapRes
return ds == null ? DecisionStrategy.UNANIMOUS : ds; return ds == null ? DecisionStrategy.UNANIMOUS : ds;
} }
@Override
public String getClientId() {
return entity.getClientId();
}
@Override
public RealmModel getRealm() {
return realmModel;
}
@Override @Override
public String toString() { public String toString() {
return String.format("%s@%08x", getId(), System.identityHashCode(this)); return String.format("%s@%08x", getId(), System.identityHashCode(this));

View file

@ -18,14 +18,19 @@
package org.keycloak.models.map.authorization.adapter; package org.keycloak.models.map.authorization.adapter;
import java.util.Objects;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.map.authorization.entity.MapScopeEntity; import org.keycloak.models.map.authorization.entity.MapScopeEntity;
public class MapScopeAdapter extends AbstractScopeModel<MapScopeEntity> { public class MapScopeAdapter extends AbstractScopeModel<MapScopeEntity> {
public MapScopeAdapter(MapScopeEntity entity, StoreFactory storeFactory) { private final ResourceServer resourceServer;
public MapScopeAdapter(ResourceServer resourceServer, MapScopeEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory); super(entity, storeFactory);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
} }
@Override @Override
@ -68,7 +73,7 @@ public class MapScopeAdapter extends AbstractScopeModel<MapScopeEntity> {
@Override @Override
public ResourceServer getResourceServer() { public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId()); return resourceServer;
} }
@Override @Override

View file

@ -45,6 +45,9 @@ public interface MapPermissionTicketEntity extends UpdatableEntity, AbstractEnti
} }
} }
String getRealmId();
void setRealmId(String realmId);
String getOwner(); String getOwner();
void setOwner(String owner); void setOwner(String owner);

View file

@ -50,6 +50,9 @@ public interface MapPolicyEntity extends UpdatableEntity, AbstractEntity {
} }
} }
String getRealmId();
void setRealmId(String realmId);
String getName(); String getName();
void setName(String name); void setName(String name);

View file

@ -48,6 +48,9 @@ public interface MapResourceEntity extends UpdatableEntity, AbstractEntity, Enti
} }
} }
String getRealmId();
void setRealmId(String realmId);
String getName(); String getName();
void setName(String name); void setName(String name);

View file

@ -47,6 +47,12 @@ public interface MapResourceServerEntity extends UpdatableEntity, AbstractEntity
} }
} }
String getRealmId();
void setRealmId(String realmId);
String getClientId();
void setClientId(String clientId);
Boolean isAllowRemoteResourceManagement(); Boolean isAllowRemoteResourceManagement();
void setAllowRemoteResourceManagement(Boolean allowRemoteResourceManagement); void setAllowRemoteResourceManagement(Boolean allowRemoteResourceManagement);

View file

@ -45,6 +45,9 @@ public interface MapScopeEntity extends UpdatableEntity, AbstractEntity {
} }
} }
String getRealmId();
void setRealmId(String realmId);
String getName(); String getName();
void setName(String name); void setName(String name);

View file

@ -67,6 +67,8 @@ public abstract class AbstractMapProviderFactory<T extends Provider, V extends A
GROUP_AFTER_REMOVE, GROUP_AFTER_REMOVE,
REALM_BEFORE_REMOVE, REALM_BEFORE_REMOVE,
REALM_AFTER_REMOVE, REALM_AFTER_REMOVE,
RESOURCE_SERVER_BEFORE_REMOVE,
RESOURCE_SERVER_AFTER_REMOVE,
ROLE_BEFORE_REMOVE, ROLE_BEFORE_REMOVE,
ROLE_AFTER_REMOVE, ROLE_AFTER_REMOVE,
USER_BEFORE_REMOVE, USER_BEFORE_REMOVE,

View file

@ -144,6 +144,8 @@ public class MapFieldPredicates {
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.EXPIRATION, MapRootAuthenticationSessionEntity::getExpiration); put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.EXPIRATION, MapRootAuthenticationSessionEntity::getExpiration);
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, predicateForKeyField(MapResourceServerEntity::getId)); put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, predicateForKeyField(MapResourceServerEntity::getId));
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.CLIENT_ID, MapResourceServerEntity::getClientId);
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.REALM_ID, MapResourceServerEntity::getRealmId);
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(MapResourceEntity::getId)); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(MapResourceEntity::getId));
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, MapResourceEntity::getName); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, MapResourceEntity::getName);
@ -153,10 +155,12 @@ public class MapFieldPredicates {
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.URI, MapFieldPredicates::checkResourceUri); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.URI, MapFieldPredicates::checkResourceUri);
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.SCOPE_ID, MapFieldPredicates::checkResourceScopes); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.SCOPE_ID, MapFieldPredicates::checkResourceScopes);
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, MapResourceEntity::isOwnerManagedAccess); put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, MapResourceEntity::isOwnerManagedAccess);
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.REALM_ID, MapResourceEntity::getRealmId);
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(MapScopeEntity::getId)); put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(MapScopeEntity::getId));
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, MapScopeEntity::getResourceServerId); put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, MapScopeEntity::getResourceServerId);
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, MapScopeEntity::getName); put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, MapScopeEntity::getName);
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.REALM_ID, MapScopeEntity::getRealmId);
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(MapPermissionTicketEntity::getId)); put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(MapPermissionTicketEntity::getId));
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, MapPermissionTicketEntity::getOwner); put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, MapPermissionTicketEntity::getOwner);
@ -166,12 +170,14 @@ public class MapFieldPredicates {
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(MapPermissionTicketEntity::getScopeId)); put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(MapPermissionTicketEntity::getScopeId));
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(MapPermissionTicketEntity::getPolicyId)); put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(MapPermissionTicketEntity::getPolicyId));
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, MapPermissionTicketEntity::getGrantedTimestamp); put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, MapPermissionTicketEntity::getGrantedTimestamp);
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REALM_ID, MapPermissionTicketEntity::getRealmId);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(MapPolicyEntity::getId)); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(MapPolicyEntity::getId));
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, MapPolicyEntity::getName); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, MapPolicyEntity::getName);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, MapPolicyEntity::getOwner); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, MapPolicyEntity::getOwner);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, MapPolicyEntity::getType); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, MapPolicyEntity::getType);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, MapPolicyEntity::getResourceServerId); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, MapPolicyEntity::getResourceServerId);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.REALM_ID, MapPolicyEntity::getRealmId);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig); put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig);

View file

@ -137,7 +137,6 @@ public final class AuthorizationProvider implements Provider {
* Returns a {@link PolicyProviderFactory} given a <code>type</code>. * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
* *
* @param type the type of the policy provider * @param type the type of the policy provider
* @param <F> the expected type of the provider
* @return a {@link PolicyProviderFactory} with the given <code>type</code> * @return a {@link PolicyProviderFactory} with the given <code>type</code>
*/ */
public PolicyProviderFactory getProviderFactory(String type) { public PolicyProviderFactory getProviderFactory(String type) {

View file

@ -32,6 +32,7 @@ public interface PermissionTicket {
public static final SearchableModelField<PermissionTicket> SCOPE_ID = new SearchableModelField<>("scopeId", String.class); public static final SearchableModelField<PermissionTicket> SCOPE_ID = new SearchableModelField<>("scopeId", String.class);
public static final SearchableModelField<PermissionTicket> POLICY_ID = new SearchableModelField<>("policyId", String.class); public static final SearchableModelField<PermissionTicket> POLICY_ID = new SearchableModelField<>("policyId", String.class);
public static final SearchableModelField<PermissionTicket> GRANTED_TIMESTAMP = new SearchableModelField<>("grantedTimestamp", String.class); public static final SearchableModelField<PermissionTicket> GRANTED_TIMESTAMP = new SearchableModelField<>("grantedTimestamp", String.class);
public static final SearchableModelField<PermissionTicket> REALM_ID = new SearchableModelField<>("realmId", String.class);
} }
public static enum FilterOption { public static enum FilterOption {

View file

@ -42,6 +42,7 @@ public interface Policy {
public static final SearchableModelField<Policy> OWNER = new SearchableModelField<>("owner", String.class); public static final SearchableModelField<Policy> OWNER = new SearchableModelField<>("owner", String.class);
public static final SearchableModelField<Policy> CONFIG = new SearchableModelField<>("config", String.class); public static final SearchableModelField<Policy> CONFIG = new SearchableModelField<>("config", String.class);
public static final SearchableModelField<Policy> ASSOCIATED_POLICY_ID = new SearchableModelField<>("associatedPolicyId", String.class); public static final SearchableModelField<Policy> ASSOCIATED_POLICY_ID = new SearchableModelField<>("associatedPolicyId", String.class);
public static final SearchableModelField<Policy> REALM_ID = new SearchableModelField<>("realmId", String.class);
} }
public static enum FilterOption { public static enum FilterOption {
@ -100,7 +101,7 @@ public interface Policy {
/** /**
* Sets the {DecisionStrategy} for this policy. * Sets the {DecisionStrategy} for this policy.
* *
* @return the decision strategy for this policy * @param decisionStrategy for this policy
*/ */
void setDecisionStrategy(DecisionStrategy decisionStrategy); void setDecisionStrategy(DecisionStrategy decisionStrategy);
@ -114,7 +115,7 @@ public interface Policy {
/** /**
* Sets the {Logic} for this policy. * Sets the {Logic} for this policy.
* *
* @return the decision strategy for this policy * @param logic for this policy
*/ */
void setLogic(Logic logic); void setLogic(Logic logic);
@ -129,7 +130,7 @@ public interface Policy {
/** /**
* Sets a {@link Map} with string-based key/value pairs representing any additional configuration for this policy. * Sets a {@link Map} with string-based key/value pairs representing any additional configuration for this policy.
* *
* @return a map with any additional configuration for this policy. * @param config a map with any additional configuration for this policy.
*/ */
void setConfig(Map<String, String> config); void setConfig(Map<String, String> config);

View file

@ -37,6 +37,7 @@ public interface Resource {
public static final SearchableModelField<Resource> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class); public static final SearchableModelField<Resource> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
public static final SearchableModelField<Resource> OWNER = new SearchableModelField<>("owner", String.class); public static final SearchableModelField<Resource> OWNER = new SearchableModelField<>("owner", String.class);
public static final SearchableModelField<Resource> TYPE = new SearchableModelField<>("type", String.class); public static final SearchableModelField<Resource> TYPE = new SearchableModelField<>("type", String.class);
public static final SearchableModelField<Resource> REALM_ID = new SearchableModelField<>("realmId", String.class);
public static final SearchableModelField<Resource> URI = new SearchableModelField<>("uris", String.class); public static final SearchableModelField<Resource> URI = new SearchableModelField<>("uris", String.class);
public static final SearchableModelField<Resource> SCOPE_ID = new SearchableModelField<>("scope", String.class); public static final SearchableModelField<Resource> SCOPE_ID = new SearchableModelField<>("scope", String.class);
@ -133,7 +134,7 @@ public interface Resource {
/** /**
* Sets a string representing the type of this resource. * Sets a string representing the type of this resource.
* *
* @return the type of this resource or null if not defined * @param type the type of this resource or null if not defined
*/ */
void setType(String type); void setType(String type);
@ -154,7 +155,7 @@ public interface Resource {
/** /**
* Sets an icon {@link java.net.URI} for this resource. * Sets an icon {@link java.net.URI} for this resource.
* *
* @return a uri for an icon * @param iconUri an uri for an icon
*/ */
void setIconUri(String iconUri); void setIconUri(String iconUri);
@ -203,6 +204,7 @@ public interface Resource {
/** /**
* Returns the first value of an attribute with the given <code>name</code> * Returns the first value of an attribute with the given <code>name</code>
* *
* @param name of the attribute
* @return the first value of an attribute * @return the first value of an attribute
*/ */
String getSingleAttribute(String name); String getSingleAttribute(String name);
@ -210,6 +212,7 @@ public interface Resource {
/** /**
* Returns the values of an attribute with the given <code>name</code> * Returns the values of an attribute with the given <code>name</code>
* *
* @param name of the attribute
* @return the values of an attribute * @return the values of an attribute
*/ */
List<String> getAttribute(String name); List<String> getAttribute(String name);
@ -218,8 +221,7 @@ public interface Resource {
* Sets an attribute with the given <code>name</code> and <code>values</code>. * Sets an attribute with the given <code>name</code> and <code>values</code>.
* *
* @param name the attribute name * @param name the attribute name
* @param value the attribute values * @param values the attribute values
* @return a map holding the attributes associated with this resource
*/ */
void setAttribute(String name, List<String> values); void setAttribute(String name, List<String> values);

View file

@ -18,18 +18,11 @@
package org.keycloak.authorization.model; package org.keycloak.authorization.model;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import org.keycloak.storage.SearchableModelField; import org.keycloak.storage.SearchableModelField;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/** /**
* Represents a resource server, whose resources are managed and protected. A resource server is basically an existing * Represents a resource server, whose resources are managed and protected. A resource server is basically an existing
@ -41,6 +34,9 @@ public interface ResourceServer {
public static class SearchableFields { public static class SearchableFields {
public static final SearchableModelField<ResourceServer> ID = new SearchableModelField<>("id", String.class); public static final SearchableModelField<ResourceServer> ID = new SearchableModelField<>("id", String.class);
/** ID of the client (not the clientId) associated with resource server*/
public static final SearchableModelField<ResourceServer> CLIENT_ID = new SearchableModelField<>("clientId", String.class);
public static final SearchableModelField<ResourceServer> REALM_ID = new SearchableModelField<>("realmId", String.class);
} }
/** /**
@ -53,7 +49,7 @@ public interface ResourceServer {
/** /**
* Indicates if the resource server is allowed to manage its own resources remotely using the Protection API. * Indicates if the resource server is allowed to manage its own resources remotely using the Protection API.
* *
* {@code true} if the resource server is allowed to managed them remotely * @return {@code true} if the resource server is allowed to managed them remotely
*/ */
boolean isAllowRemoteResourceManagement(); boolean isAllowRemoteResourceManagement();
@ -95,8 +91,14 @@ public interface ResourceServer {
/** /**
* Returns id of a client that this {@link ResourceServer} is associated with * Returns id of a client that this {@link ResourceServer} is associated with
* @return id of client
*/ */
default String getClientId() { String getClientId();
return getId();
} /**
* Returns reference of a realm that this {@link ResourceServer} belongs to.
*
* @return reference of a realm
*/
RealmModel getRealm();
} }

View file

@ -32,6 +32,7 @@ public interface Scope {
public static final SearchableModelField<Scope> ID = new SearchableModelField<>("id", String.class); public static final SearchableModelField<Scope> ID = new SearchableModelField<>("id", String.class);
public static final SearchableModelField<Scope> NAME = new SearchableModelField<>("name", String.class); public static final SearchableModelField<Scope> NAME = new SearchableModelField<>("name", String.class);
public static final SearchableModelField<Scope> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class); public static final SearchableModelField<Scope> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
public static final SearchableModelField<Scope> REALM_ID = new SearchableModelField<>("resourceServerId", String.class);
} }
public static enum FilterOption { public static enum FilterOption {

View file

@ -24,6 +24,7 @@ import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
import org.keycloak.models.RealmModel;
/** /**
* A {@link PermissionTicketStore} is responsible to manage the persistence of {@link org.keycloak.authorization.model.PermissionTicket} instances. * A {@link PermissionTicketStore} is responsible to manage the persistence of {@link org.keycloak.authorization.model.PermissionTicket} instances.
@ -145,21 +146,25 @@ public interface PermissionTicketStore {
/** /**
* Returns a list of {@link Resource} granted to the given {@code requester} * Returns a list of {@link Resource} granted to the given {@code requester}
* *
*
* @param realm
* @param requester the requester * @param requester the requester
* @param name the keyword to query resources by name or null if any resource * @param name the keyword to query resources by name or null if any resource
* @param firstResult first result to return. Ignored if negative or {@code null}. * @param firstResult first result to return. Ignored if negative or {@code null}.
* @param maxResults maximum number of results to return. Ignored if negative or {@code null}. * @param maxResults maximum number of results to return. Ignored if negative or {@code null}.
* @return a list of {@link Resource} granted to the given {@code requester} * @return a list of {@link Resource} granted to the given {@code requester}
*/ */
List<Resource> findGrantedResources(String requester, String name, Integer firstResult, Integer maxResults); List<Resource> findGrantedResources(RealmModel realm, String requester, String name, Integer firstResult, Integer maxResults);
/** /**
* Returns a list of {@link Resource} granted by the owner to other users * Returns a list of {@link Resource} granted by the owner to other users
* *
*
* @param realm
* @param owner the owner * @param owner the owner
* @param firstResult first result to return. Ignored if negative or {@code null}. * @param firstResult first result to return. Ignored if negative or {@code null}.
* @param maxResults maximum number of results to return. Ignored if negative or {@code null}. * @param maxResults maximum number of results to return. Ignored if negative or {@code null}.
* @return a list of {@link Resource} granted by the owner * @return a list of {@link Resource} granted by the owner
*/ */
List<Resource> findGrantedOwnerResources(String owner, Integer firstResult, Integer maxResults); List<Resource> findGrantedOwnerResources(RealmModel realm, String owner, Integer firstResult, Integer maxResults);
} }

View file

@ -20,6 +20,7 @@ package org.keycloak.authorization.store;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
/** /**
* A {@link ResourceServerStore} is responsible to manage the persistence of {@link ResourceServer} instances. * A {@link ResourceServerStore} is responsible to manage the persistence of {@link ResourceServer} instances.
@ -47,11 +48,13 @@ public interface ResourceServerStore {
/** /**
* Returns a {@link ResourceServer} instance based on its identifier. * Returns a {@link ResourceServer} instance based on its identifier.
* *
*
* @param realm
* @param id the identifier of an existing resource server instance * @param id the identifier of an existing resource server instance
* *
* @return the resource server instance with the given identifier or null if no instance was found * @return the resource server instance with the given identifier or null if no instance was found
*/ */
ResourceServer findById(String id); ResourceServer findById(RealmModel realm, String id);
/** /**
* Returns a {@link ResourceServer} instance based on a client. * Returns a {@link ResourceServer} instance based on a client.

View file

@ -90,7 +90,7 @@ public class ResourcesService extends AbstractResourceService {
public Response getSharedWithMe(@QueryParam("name") String name, public Response getSharedWithMe(@QueryParam("name") String name,
@QueryParam("first") Integer first, @QueryParam("first") Integer first,
@QueryParam("max") Integer max) { @QueryParam("max") Integer max) {
return queryResponse((f, m) -> toPermissions(ticketStore.findGrantedResources(auth.getUser().getId(), name, f, m), false) return queryResponse((f, m) -> toPermissions(ticketStore.findGrantedResources(auth.getRealm(), auth.getUser().getId(), name, f, m), false)
.stream(), first, max); .stream(), first, max);
} }
@ -108,7 +108,7 @@ public class ResourcesService extends AbstractResourceService {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getSharedWithOthers(@QueryParam("first") Integer first, @QueryParam("max") Integer max) { public Response getSharedWithOthers(@QueryParam("first") Integer first, @QueryParam("max") Integer max) {
return queryResponse( return queryResponse(
(f, m) -> toPermissions(ticketStore.findGrantedOwnerResources(auth.getUser().getId(), f, m), true) (f, m) -> toPermissions(ticketStore.findGrantedOwnerResources(auth.getRealm(), auth.getUser().getId(), f, m), true)
.stream(), first, max); .stream(), first, max);
} }

View file

@ -17,22 +17,31 @@
package org.keycloak.testsuite.model; package org.keycloak.testsuite.model;
import org.junit.Test; import org.junit.Test;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider; import org.keycloak.models.RealmProvider;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.notNullValue;
@RequireProvider(RealmProvider.class) @RequireProvider(RealmProvider.class)
public class RealmModelTest extends KeycloakModelTest { public class RealmModelTest extends KeycloakModelTest {
private String realmId; private String realmId;
private String realm1Id;
private String realm2Id;
@Override @Override
public void createEnvironment(KeycloakSession s) { public void createEnvironment(KeycloakSession s) {
@ -44,6 +53,8 @@ public class RealmModelTest extends KeycloakModelTest {
@Override @Override
public void cleanEnvironment(KeycloakSession s) { public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId); s.realms().removeRealm(realmId);
if (realm1Id != null) s.realms().removeRealm(realm1Id);
if (realm2Id != null) s.realms().removeRealm(realm2Id);
} }
@Test @Test
@ -93,6 +104,40 @@ public class RealmModelTest extends KeycloakModelTest {
return null; return null;
}); });
}
@Test
public void testRealmPreRemoveDoesntRemoveEntitiesFromOtherRealms() {
realm1Id = inComittedTransaction((Function<KeycloakSession, String>) session -> {
RealmModel realm = session.realms().createRealm("realm1");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
return realm.getId();
});
realm2Id = inComittedTransaction((Function<KeycloakSession, String>) session -> {
RealmModel realm = session.realms().createRealm("realm2");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
return realm.getId();
});
// Create client with resource server
String clientRealm1 = withRealm(realm1Id, (keycloakSession, realmModel) -> {
ClientModel clientRealm = realmModel.addClient("clientRealm1");
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
provider.getStoreFactory().getResourceServerStore().create(clientRealm);
return clientRealm.getId();
});
// Remove realm 2
inComittedTransaction( (Consumer<KeycloakSession>) keycloakSession -> keycloakSession.realms().removeRealm(realm2Id));
// ResourceServer in realm1 must still exist
ResourceServer resourceServer = withRealm(realm1Id, (keycloakSession, realmModel) -> {
ClientModel client1 = realmModel.getClientById(clientRealm1);
return keycloakSession.getProvider(AuthorizationProvider.class).getStoreFactory().getResourceServerStore().findByClient(client1);
});
assertThat(resourceServer, notNullValue());
} }
} }