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
public PermissionTicket getDelegateForUpdate() {
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());
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());
@ -70,7 +70,7 @@ public class PermissionTicketAdapter implements PermissionTicket, CachedModel<Pe
protected boolean isUpdated() {
if (updated != null) return true;
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());
if (updated == null) throw new IllegalStateException("Not found in database");
return true;
@ -122,13 +122,13 @@ public class PermissionTicketAdapter implements PermissionTicket, CachedModel<Pe
@Override
public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStore().findById(cached.getResourceServerId());
return cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId());
}
@Override
public Policy 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

View file

@ -85,7 +85,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
protected boolean isUpdated() {
if (updated != null) return true;
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");
return true;
}
@ -112,7 +112,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
@Override
public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStore().findById(cached.getResourceServerId());
return cacheSession.getResourceServerStore().findById(null, cached.getResourceServerId());
}
@Override
@ -208,7 +208,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
PolicyStore policyStore = cacheSession.getPolicyStore();
String resourceServerId = cached.getResourceServerId();
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);
associatedPolicies.add(policy);
}
@ -325,6 +325,6 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
}
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
public ResourceServer getResourceServer() {
return cacheSession.getResourceServerStoreDelegate().findById(cached.getResourceServerId());
return cacheSession.getResourceServerStoreDelegate().findById(null, cached.getResourceServerId());
}
@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.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
@ -40,7 +42,7 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
public ResourceServer getDelegateForUpdate() {
if (updated == null) {
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");
}
return updated;
@ -67,7 +69,7 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
protected boolean isUpdated() {
if (updated != null) return true;
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");
return true;
}
@ -116,6 +118,16 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
updated.setDecisionStrategy(decisionStrategy);
}
@Override
public String getClientId() {
return getId();
}
@Override
public RealmModel getRealm() {
return getDelegateForUpdate().getRealm();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

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

View file

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

View file

@ -41,6 +41,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PermissionTicketStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.util.Time;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.LockModeType;
@ -290,7 +291,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
}
@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 ?
entityManager.createNamedQuery("findGrantedResources", String.class) :
entityManager.createNamedQuery("findGrantedResourcesByName", String.class);
@ -318,7 +319,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
}
@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);
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.store.ResourceServerStore;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId;
import javax.persistence.EntityManager;
@ -58,7 +59,7 @@ public class JPAResourceServerStore implements ResourceServerStore {
this.entityManager.persist(entity);
return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory());
return new ResourceServerAdapter(client.getRealm(), entity, entityManager, provider.getStoreFactory());
}
@Override
@ -122,14 +123,14 @@ public class JPAResourceServerStore implements ResourceServerStore {
}
@Override
public ResourceServer findById(String id) {
public ResourceServer findById(RealmModel realm, String id) {
ResourceServerEntity entity = entityManager.find(ResourceServerEntity.class, id);
if (entity == null) return null;
return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory());
return new ResourceServerAdapter(provider.getRealm(), entity, entityManager, provider.getStoreFactory());
}
@Override
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 org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
@ -91,7 +90,7 @@ public class PermissionTicketAdapter implements PermissionTicket, JpaModel<Permi
@Override
public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId());
return storeFactory.getResourceServerStore().findById(null, entity.getResourceServer().getId());
}
@Override
@ -102,7 +101,7 @@ public class PermissionTicketAdapter implements PermissionTicket, JpaModel<Permi
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());
}

View file

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

View file

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

View file

@ -88,7 +88,7 @@ public class ScopeAdapter extends AbstractAuthorizationModel implements Scope, J
@Override
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) {

View file

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

View file

@ -17,6 +17,7 @@
package org.keycloak.models.map.authorization;
import java.util.concurrent.atomic.AtomicInteger;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket;
@ -29,56 +30,58 @@ import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.Profile;
import org.keycloak.component.AmphibianProviderFactory;
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.RealmModel;
import org.keycloak.models.map.common.AbstractMapProviderFactory;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.MapStorageProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
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;
/**
* @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;
private Config.Scope storageConfigScope;
private final String uniqueKey = MapAuthorizationStoreFactory.class.getName() + uniqueCounter.incrementAndGet();
@Override
public StoreFactory create(KeycloakSession session) {
MapAuthorizationStore authzStore = session.getAttribute(uniqueKey, MapAuthorizationStore.class);
if (authzStore != null) return authzStore;
MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session);
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
MapStorage permissionTicketStore = 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;
MapStorage policyStore;
MapStorage resourceServerStore;
MapStorage resourceStore;
MapStorage scopeStore;
authzStore = new MapAuthorizationStore(session,
permissionTicketStore,
policyStore,
resourceServerStore,
resourceStore,
scopeStore,
provider
);
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,
policyStore,
resourceServerStore,
resourceStore,
scopeStore,
provider
);
session.setAttribute(uniqueKey, authzStore);
return authzStore;
}
@Override
@ -86,11 +89,6 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
this.storageConfigScope = config.scope("storage");
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -105,4 +103,26 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
public boolean isSupported() {
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.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
@ -59,17 +60,22 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class);
private final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapPermissionTicketEntity, PermissionTicket> tx;
private final KeycloakSession session;
public MapPermissionTicketStore(KeycloakSession session, MapStorage<MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) {
this.authorizationProvider = provider;
this.tx = permissionTicketStore.createTransaction(session);
session.getTransactionManager().enlist(tx);
this.session = session;
}
private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) {
if (origEntity == null) return null;
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapPermissionTicketAdapter(origEntity, authorizationProvider.getStoreFactory());
private Function<MapPermissionTicketEntity, PermissionTicket> entityToAdapterFunc(ResourceServer resourceServer) {
return origEntity -> new MapPermissionTicketAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, 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) {
@ -124,10 +130,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
entity.setOwner(owner);
entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity);
return entityToAdapter(entity);
return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
}
@Override
@ -148,7 +155,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -157,7 +164,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -167,7 +174,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.OWNER, Operator.EQ, owner)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -177,7 +184,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId())))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -187,7 +194,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.EQ, scope.getId())))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -216,7 +223,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
);
return tx.read(withCriteria(mcb).pagination(firstResult, maxResult, SearchableFields.ID))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -272,10 +279,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
}
@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();
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;
@ -288,13 +296,13 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId()});
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);
};
} else {
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))
@ -305,16 +313,32 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
}
@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();
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();
ResourceServerStore resourceServerStore = authorizationProvider.getStoreFactory().getResourceServerStore();
return paginatedStream(tx.read(withCriteria(mcb).orderBy(SearchableFields.RESOURCE_ID, ASCENDING))
.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());
}
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.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntityImpl;
@ -41,6 +42,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
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 final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapPolicyEntity, Policy> tx;
private final KeycloakSession session;
public MapPolicyStore(KeycloakSession session, MapStorage<MapPolicyEntity, Policy> policyStore, AuthorizationProvider provider) {
this.authorizationProvider = provider;
this.tx = policyStore.createTransaction(session);
session.getTransactionManager().enlist(tx);
this.session = session;
}
private Policy entityToAdapter(MapPolicyEntity origEntity) {
if (origEntity == null) return null;
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapPolicyAdapter(origEntity, authorizationProvider.getStoreFactory());
private Function<MapPolicyEntity, Policy> entityToAdapterFunc(ResourceServer resourceServer) {
return origEntity -> new MapPolicyAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, 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) {
@ -92,10 +99,11 @@ public class MapPolicyStore implements PolicyStore {
entity.setType(representation.getType());
entity.setName(representation.getName());
entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity);
return entityToAdapter(entity);
return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
}
@Override
@ -111,7 +119,7 @@ public class MapPolicyStore implements PolicyStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -122,7 +130,7 @@ public class MapPolicyStore implements PolicyStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -131,7 +139,7 @@ public class MapPolicyStore implements PolicyStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -201,7 +209,7 @@ public class MapPolicyStore implements PolicyStore {
tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId())))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.forEach(consumer);
}
@ -209,7 +217,7 @@ public class MapPolicyStore implements PolicyStore {
public void findByResourceType(ResourceServer resourceServer, String type, Consumer<Policy> policyConsumer) {
tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[]{"defaultResourceType", type})))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.forEach(policyConsumer);
}
@ -217,7 +225,7 @@ public class MapPolicyStore implements PolicyStore {
public List<Policy> findByScopes(ResourceServer resourceServer, List<Scope> scopes) {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId))))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -235,14 +243,14 @@ public class MapPolicyStore implements PolicyStore {
.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
public List<Policy> findByType(ResourceServer resourceServer, String type) {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -250,7 +258,22 @@ public class MapPolicyStore implements PolicyStore {
public List<Policy> findDependentPolicies(ResourceServer resourceServer, String id) {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.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.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.model.ResourceServer.SearchableFields;
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.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapResourceServerAdapter;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
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 java.util.Objects;
import java.util.function.Function;
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 {
@ -54,10 +57,8 @@ public class MapResourceServerStore implements ResourceServerStore {
session.getTransactionManager().enlist(tx);
}
private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) {
if (origEntity == null) return null;
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapResourceServerAdapter(origEntity, authorizationProvider.getStoreFactory());
private Function<MapResourceServerEntity, ResourceServer> entityToAdapterFunc(RealmModel realmModel) {
return origEntity -> new MapResourceServerAdapter(realmModel, origEntity, authorizationProvider.getStoreFactory());
}
@Override
@ -76,47 +77,29 @@ public class MapResourceServerStore implements ResourceServerStore {
}
MapResourceServerEntity entity = new MapResourceServerEntityImpl();
entity.setId(clientId);
entity.setClientId(clientId);
entity.setRealmId(client.getRealm().getId());
entity = tx.create(entity);
return entityToAdapter(entity);
return entity == null ? null : entityToAdapterFunc(client.getRealm()).apply(entity);
}
@Override
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);
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
PolicyStore policyStore = authorizationProvider.getStoreFactory().getPolicyStore();
policyStore.findByResourceServer(resourceServer).stream()
.map(Policy::getId)
.forEach(policyStore::delete);
tx.delete(resourceServer.getId());
PermissionTicketStore permissionTicketStore = authorizationProvider.getStoreFactory().getPermissionTicketStore();
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);
authorizationProvider.getKeycloakSession().invalidate(RESOURCE_SERVER_AFTER_REMOVE, resourceServer);
}
@Override
public ResourceServer findById(String id) {
public ResourceServer findById(RealmModel realm, String id) {
LOG.tracef("findById(%s)%s", id, getShortStackTrace());
if (id == null) {
@ -124,11 +107,29 @@ public class MapResourceServerStore implements ResourceServerStore {
}
MapResourceServerEntity entity = tx.read(id);
return entityToAdapter(entity);
return (entity == null || !Objects.equals(realm.getId(), entity.getRealmId())) ? null : entityToAdapterFunc(realm).apply(entity);
}
@Override
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.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapResourceAdapter;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
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 final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapResourceEntity, Resource> tx;
private final KeycloakSession session;
public MapResourceStore(KeycloakSession session, MapStorage<MapResourceEntity, Resource> resourceStore, AuthorizationProvider provider) {
this.tx = resourceStore.createTransaction(session);
session.getTransactionManager().enlist(tx);
authorizationProvider = provider;
this.session = session;
}
private Resource entityToAdapter(MapResourceEntity origEntity) {
if (origEntity == null) return null;
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapResourceAdapter(origEntity, authorizationProvider.getStoreFactory());
private Function<MapResourceEntity, Resource> entityToAdapterFunc(final ResourceServer resourceServer) {
return origEntity -> new MapResourceAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, 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) {
@ -90,10 +97,11 @@ public class MapResourceStore implements ResourceStore {
entity.setName(name);
entity.setResourceServerId(resourceServer.getId());
entity.setOwner(owner);
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity);
return entityToAdapter(entity);
return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
}
@Override
@ -110,7 +118,7 @@ public class MapResourceStore implements ResourceStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -124,7 +132,7 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.OWNER, Operator.EQ, ownerId))
.pagination(firstResult, maxResult, SearchableFields.ID)
).map(this::entityToAdapter)
).map(entityToAdapterFunc(resourceServer))
.forEach(consumer);
}
@ -143,7 +151,7 @@ public class MapResourceStore implements ResourceStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.URI, Operator.EQ, uri)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -152,7 +160,7 @@ public class MapResourceStore implements ResourceStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -166,7 +174,7 @@ public class MapResourceStore implements ResourceStore {
);
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -203,7 +211,7 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes.stream().map(Scope::getId))))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.forEach(consumer);
}
@ -214,7 +222,7 @@ public class MapResourceStore implements ResourceStore {
.compare(SearchableFields.OWNER, Operator.EQ, ownerId)
.compare(SearchableFields.NAME, Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -223,7 +231,7 @@ public class MapResourceStore implements ResourceStore {
LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServer, consumer, getShortStackTrace());
tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.forEach(consumer);
}
@ -239,7 +247,7 @@ public class MapResourceStore implements ResourceStore {
}
tx.read(withCriteria(mcb))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.forEach(consumer);
}
@ -249,7 +257,22 @@ public class MapResourceStore implements ResourceStore {
tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.OWNER, Operator.NE, resourceServer.getClientId())
.compare(SearchableFields.TYPE, Operator.EQ, type)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.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.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.adapter.MapScopeAdapter;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntityImpl;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
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 final AuthorizationProvider authorizationProvider;
final MapKeycloakTransaction<MapScopeEntity, Scope> tx;
private final KeycloakSession session;
public MapScopeStore(KeycloakSession session, MapStorage<MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
this.authorizationProvider = provider;
this.tx = scopeStore.createTransaction(session);
session.getTransactionManager().enlist(tx);
this.session = session;
}
private Scope entityToAdapter(MapScopeEntity origEntity) {
if (origEntity == null) return null;
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return new MapScopeAdapter(origEntity, authorizationProvider.getStoreFactory());
private Function<MapScopeEntity, Scope> entityToAdapterFunc(ResourceServer resourceServer) {
return origEntity -> new MapScopeAdapter(resourceServer == null ? findResourceServer(origEntity) : resourceServer, 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) {
@ -86,10 +93,11 @@ public class MapScopeStore implements ScopeStore {
entity.setId(id);
entity.setName(name);
entity.setResourceServerId(resourceServer.getId());
entity.setRealmId(resourceServer.getRealm().getId());
entity = tx.create(entity);
return entityToAdapter(entity);
return entity == null ? null : entityToAdapterFunc(resourceServer).apply(entity);
}
@Override
@ -105,7 +113,7 @@ public class MapScopeStore implements ScopeStore {
return tx.read(withCriteria(forResourceServer(resourceServer)
.compare(SearchableFields.ID, Operator.EQ, id)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -116,7 +124,7 @@ public class MapScopeStore implements ScopeStore {
return tx.read(withCriteria(forResourceServer(resourceServer).compare(SearchableFields.NAME,
Operator.EQ, name)))
.findFirst()
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.orElse(null);
}
@ -125,7 +133,7 @@ public class MapScopeStore implements ScopeStore {
LOG.tracef("findByResourceServer(%s)%s", resourceServer, getShortStackTrace());
return tx.read(withCriteria(forResourceServer(resourceServer)))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.collect(Collectors.toList());
}
@ -149,7 +157,22 @@ public class MapScopeStore implements ScopeStore {
}
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
.map(this::entityToAdapter)
.map(entityToAdapterFunc(resourceServer))
.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;
import java.util.Objects;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
@ -30,8 +31,12 @@ import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy;
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);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
}
@Override
@ -51,13 +56,13 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
@Override
public Resource getResource() {
return storeFactory.getResourceStore().findById(getResourceServer(), entity.getResourceId());
return storeFactory.getResourceStore().findById(resourceServer, entity.getResourceId());
}
@Override
public Scope getScope() {
if (entity.getScopeId() == null) return null;
return storeFactory.getScopeStore().findById(getResourceServer(), entity.getScopeId());
return storeFactory.getScopeStore().findById(resourceServer, entity.getScopeId());
}
@Override
@ -83,13 +88,12 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
@Override
public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
return resourceServer;
}
@Override
public Policy getPolicy() {
if (entity.getPolicyId() == null) return null;
ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
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.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
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);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
}
@Override
@ -119,21 +124,19 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
@Override
public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
return resourceServer;
}
@Override
public Set<Policy> getAssociatedPolicies() {
String resourceServerId = entity.getResourceServerId();
Set<String> ids = entity.getAssociatedPolicyIds();
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());
}
@Override
public Set<Resource> getResources() {
ResourceServer resourceServer = getResourceServer();
Set<String> ids = entity.getResourceIds();
return ids == null ? Collections.emptySet() : ids.stream()
.map(resourceId -> storeFactory.getResourceStore().findById(resourceServer, resourceId))
@ -142,7 +145,6 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
@Override
public Set<Scope> getScopes() {
ResourceServer resourceServer = getResourceServer();
Set<String> ids = entity.getScopeIds();
return ids == null ? Collections.emptySet() : ids.stream()
.map(scopeId -> storeFactory.getScopeStore().findById(resourceServer, scopeId))

View file

@ -29,13 +29,18 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
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);
Objects.requireNonNull(resourceServer);
this.resourceServer = resourceServer;
}
@Override
@ -111,7 +116,7 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
@Override
public ResourceServer getResourceServer() {
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
return resourceServer;
}
@Override
@ -143,13 +148,13 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
// The scope^ was removed from the Resource
// 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) {
permissionStore.delete(permission.getId());
}
// 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;
import java.util.Objects;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
public class MapResourceServerAdapter extends AbstractResourceServerModel<MapResourceServerEntity> {
public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) {
private final RealmModel realmModel;
public MapResourceServerAdapter(RealmModel realmModel, MapResourceServerEntity entity, StoreFactory storeFactory) {
super(entity, storeFactory);
Objects.requireNonNull(realmModel);
this.realmModel = realmModel;
}
@Override
@ -70,6 +76,16 @@ public class MapResourceServerAdapter extends AbstractResourceServerModel<MapRes
return ds == null ? DecisionStrategy.UNANIMOUS : ds;
}
@Override
public String getClientId() {
return entity.getClientId();
}
@Override
public RealmModel getRealm() {
return realmModel;
}
@Override
public String toString() {
return String.format("%s@%08x", getId(), System.identityHashCode(this));

View file

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

View file

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

View file

@ -50,6 +50,9 @@ public interface MapPolicyEntity extends UpdatableEntity, AbstractEntity {
}
}
String getRealmId();
void setRealmId(String realmId);
String getName();
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();
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();
void setAllowRemoteResourceManagement(Boolean allowRemoteResourceManagement);

View file

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

View file

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

View file

@ -144,6 +144,8 @@ public class MapFieldPredicates {
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.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.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.SCOPE_ID, MapFieldPredicates::checkResourceScopes);
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.RESOURCE_SERVER_ID, MapScopeEntity::getResourceServerId);
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.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.POLICY_ID, predicateForKeyField(MapPermissionTicketEntity::getPolicyId));
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.NAME, MapPolicyEntity::getName);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, MapPolicyEntity::getOwner);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, MapPolicyEntity::getType);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, MapPolicyEntity::getResourceServerId);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.REALM_ID, MapPolicyEntity::getRealmId);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes);
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig);

View file

@ -137,7 +137,6 @@ public final class AuthorizationProvider implements Provider {
* Returns a {@link PolicyProviderFactory} given a <code>type</code>.
*
* @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>
*/
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> POLICY_ID = new SearchableModelField<>("policyId", 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 {

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> CONFIG = new SearchableModelField<>("config", 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 {
@ -100,7 +101,7 @@ public interface Policy {
/**
* Sets the {DecisionStrategy} for this policy.
*
* @return the decision strategy for this policy
* @param decisionStrategy for this policy
*/
void setDecisionStrategy(DecisionStrategy decisionStrategy);
@ -114,7 +115,7 @@ public interface Policy {
/**
* Sets the {Logic} for this policy.
*
* @return the decision strategy for this policy
* @param logic for this policy
*/
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.
*
* @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);

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> OWNER = new SearchableModelField<>("owner", 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> SCOPE_ID = new SearchableModelField<>("scope", String.class);
@ -133,7 +134,7 @@ public interface 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);
@ -154,7 +155,7 @@ public interface 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);
@ -203,6 +204,7 @@ public interface Resource {
/**
* 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
*/
String getSingleAttribute(String name);
@ -210,6 +212,7 @@ public interface Resource {
/**
* Returns the values of an attribute with the given <code>name</code>
*
* @param name of the attribute
* @return the values of an attribute
*/
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>.
*
* @param name the attribute name
* @param value the attribute values
* @return a map holding the attributes associated with this resource
* @param values the attribute values
*/
void setAttribute(String name, List<String> values);

View file

@ -18,18 +18,11 @@
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.RoleModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
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
@ -40,7 +33,10 @@ import java.util.stream.Stream;
public interface ResourceServer {
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.
*
* {@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();
@ -95,8 +91,14 @@ public interface ResourceServer {
/**
* Returns id of a client that this {@link ResourceServer} is associated with
* @return id of client
*/
default String getClientId() {
return getId();
}
String getClientId();
/**
* 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> NAME = new SearchableModelField<>("name", 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 {

View file

@ -24,6 +24,7 @@ import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
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.
@ -145,21 +146,25 @@ public interface PermissionTicketStore {
/**
* Returns a list of {@link Resource} granted to the given {@code requester}
*
*
* @param realm
* @param requester the requester
* @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 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}
*/
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
*
*
* @param realm
* @param owner the owner
* @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}.
* @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.models.ClientModel;
import org.keycloak.models.RealmModel;
/**
* 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.
*
*
* @param realm
* @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
*/
ResourceServer findById(String id);
ResourceServer findById(RealmModel realm, String id);
/**
* 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,
@QueryParam("first") Integer first,
@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);
}
@ -108,7 +108,7 @@ public class ResourcesService extends AbstractResourceService {
@Produces(MediaType.APPLICATION_JSON)
public Response getSharedWithOthers(@QueryParam("first") Integer first, @QueryParam("max") Integer max) {
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);
}

View file

@ -17,22 +17,31 @@
package org.keycloak.testsuite.model;
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.KeycloakSession;
import org.keycloak.models.RealmModel;
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.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.notNullValue;
@RequireProvider(RealmProvider.class)
public class RealmModelTest extends KeycloakModelTest {
private String realmId;
private String realm1Id;
private String realm2Id;
@Override
public void createEnvironment(KeycloakSession s) {
@ -44,6 +53,8 @@ public class RealmModelTest extends KeycloakModelTest {
@Override
public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId);
if (realm1Id != null) s.realms().removeRealm(realm1Id);
if (realm2Id != null) s.realms().removeRealm(realm2Id);
}
@Test
@ -93,6 +104,40 @@ public class RealmModelTest extends KeycloakModelTest {
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());
}
}