[KEYCLOAK-7605] - Make sure Evaluation API is read-only
This commit is contained in:
parent
c3f1bd5a25
commit
79ca722b49
16 changed files with 230 additions and 91 deletions
|
@ -146,6 +146,16 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
return permissionTicketCache;
|
return permissionTicketCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly(boolean readOnly) {
|
||||||
|
getDelegate().setReadOnly(readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return getDelegate().isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.close();
|
delegate.close();
|
||||||
|
@ -724,7 +734,14 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||||
List<R> model = resultSupplier.get();
|
List<R> model = resultSupplier.get();
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (invalidations.contains(cacheKey)) return model;
|
if (invalidations.contains(cacheKey)) {
|
||||||
|
if (consumer != null) {
|
||||||
|
for (R policy: model) {
|
||||||
|
consumer.accept(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
};
|
||||||
query = querySupplier.apply(loaded, model);
|
query = querySupplier.apply(loaded, model);
|
||||||
cache.addRevisioned(query, startupRevision);
|
cache.addRevisioned(query, startupRevision);
|
||||||
if (consumer != null) {
|
if (consumer != null) {
|
||||||
|
@ -930,7 +947,14 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||||
List<R> model = resultSupplier.get();
|
List<R> model = resultSupplier.get();
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (invalidations.contains(cacheKey)) return model;
|
if (invalidations.contains(cacheKey)) {
|
||||||
|
if (consumer != null) {
|
||||||
|
for (R policy: model) {
|
||||||
|
consumer.accept(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
};
|
||||||
query = querySupplier.apply(loaded, model);
|
query = querySupplier.apply(loaded, model);
|
||||||
cache.addRevisioned(query, startupRevision);
|
cache.addRevisioned(query, startupRevision);
|
||||||
if (consumer != null) {
|
if (consumer != null) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class JPAStoreFactory implements StoreFactory {
|
||||||
private final ResourceStore resourceStore;
|
private final ResourceStore resourceStore;
|
||||||
private final ScopeStore scopeStore;
|
private final ScopeStore scopeStore;
|
||||||
private final JPAPermissionTicketStore permissionTicketStore;
|
private final JPAPermissionTicketStore permissionTicketStore;
|
||||||
|
private boolean readOnly;
|
||||||
|
|
||||||
public JPAStoreFactory(EntityManager entityManager, AuthorizationProvider provider) {
|
public JPAStoreFactory(EntityManager entityManager, AuthorizationProvider provider) {
|
||||||
policyStore = new JPAPolicyStore(entityManager, provider);
|
policyStore = new JPAPolicyStore(entityManager, provider);
|
||||||
|
@ -76,4 +77,14 @@ public class JPAStoreFactory implements StoreFactory {
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly(boolean readOnly) {
|
||||||
|
this.readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return readOnly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.authorization.jpa.store;
|
||||||
import org.keycloak.authorization.jpa.entities.PolicyEntity;
|
import org.keycloak.authorization.jpa.entities.PolicyEntity;
|
||||||
import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
||||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||||
|
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||||
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;
|
||||||
|
@ -39,12 +40,13 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
public class PolicyAdapter extends AbstractAuthorizationModel implements Policy, JpaModel<PolicyEntity> {
|
||||||
private PolicyEntity entity;
|
private PolicyEntity entity;
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
private StoreFactory storeFactory;
|
private StoreFactory storeFactory;
|
||||||
|
|
||||||
public PolicyAdapter(PolicyEntity entity, EntityManager em, StoreFactory storeFactory) {
|
public PolicyAdapter(PolicyEntity entity, EntityManager em, StoreFactory storeFactory) {
|
||||||
|
super(storeFactory);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.em = em;
|
this.em = em;
|
||||||
this.storeFactory = storeFactory;
|
this.storeFactory = storeFactory;
|
||||||
|
@ -72,6 +74,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setDecisionStrategy(decisionStrategy);
|
entity.setDecisionStrategy(decisionStrategy);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,6 +86,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLogic(Logic logic) {
|
public void setLogic(Logic logic) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setLogic(logic);
|
entity.setLogic(logic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +99,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfig(Map<String, String> config) {
|
public void setConfig(Map<String, String> config) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
if (entity.getConfig() == null) {
|
if (entity.getConfig() == null) {
|
||||||
entity.setConfig(new HashMap<>());
|
entity.setConfig(new HashMap<>());
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,6 +110,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeConfig(String name) {
|
public void removeConfig(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
if (entity.getConfig() == null) {
|
if (entity.getConfig() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +119,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putConfig(String name, String value) {
|
public void putConfig(String name, String value) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
if (entity.getConfig() == null) {
|
if (entity.getConfig() == null) {
|
||||||
entity.setConfig(new HashMap<>());
|
entity.setConfig(new HashMap<>());
|
||||||
}
|
}
|
||||||
|
@ -127,6 +134,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setName(name);
|
entity.setName(name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,6 +146,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setDescription(description);
|
entity.setDescription(description);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -176,38 +185,45 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addScope(Scope scope) {
|
public void addScope(Scope scope) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getScopes().add(ScopeAdapter.toEntity(em, scope));
|
entity.getScopes().add(ScopeAdapter.toEntity(em, scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeScope(Scope scope) {
|
public void removeScope(Scope scope) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getScopes().remove(ScopeAdapter.toEntity(em, scope));
|
entity.getScopes().remove(ScopeAdapter.toEntity(em, scope));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAssociatedPolicy(Policy associatedPolicy) {
|
public void addAssociatedPolicy(Policy associatedPolicy) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getAssociatedPolicies().add(toEntity(em, associatedPolicy));
|
entity.getAssociatedPolicies().add(toEntity(em, associatedPolicy));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAssociatedPolicy(Policy associatedPolicy) {
|
public void removeAssociatedPolicy(Policy associatedPolicy) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getAssociatedPolicies().remove(toEntity(em, associatedPolicy));
|
entity.getAssociatedPolicies().remove(toEntity(em, associatedPolicy));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addResource(Resource resource) {
|
public void addResource(Resource resource) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getResources().add(ResourceAdapter.toEntity(em, resource));
|
entity.getResources().add(ResourceAdapter.toEntity(em, resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeResource(Resource resource) {
|
public void removeResource(Resource resource) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.getResources().remove(ResourceAdapter.toEntity(em, resource));
|
entity.getResources().remove(ResourceAdapter.toEntity(em, resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOwner(String owner) {
|
public void setOwner(String owner) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setOwner(owner);
|
entity.setOwner(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.authorization.jpa.store;
|
||||||
import org.keycloak.authorization.jpa.entities.ResourceAttributeEntity;
|
import org.keycloak.authorization.jpa.entities.ResourceAttributeEntity;
|
||||||
import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
||||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||||
|
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||||
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;
|
||||||
|
@ -43,13 +44,14 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
public class ResourceAdapter extends AbstractAuthorizationModel implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
private ResourceEntity entity;
|
private ResourceEntity entity;
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
private StoreFactory storeFactory;
|
private StoreFactory storeFactory;
|
||||||
|
|
||||||
public ResourceAdapter(ResourceEntity entity, EntityManager em, StoreFactory storeFactory) {
|
public ResourceAdapter(ResourceEntity entity, EntityManager em, StoreFactory storeFactory) {
|
||||||
|
super(storeFactory);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.em = em;
|
this.em = em;
|
||||||
this.storeFactory = storeFactory;
|
this.storeFactory = storeFactory;
|
||||||
|
@ -77,6 +79,7 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDisplayName(String name) {
|
public void setDisplayName(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setDisplayName(name);
|
entity.setDisplayName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +90,13 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUris(Set<String> uri) {
|
public void updateUris(Set<String> uri) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setUris(uri);
|
entity.setUris(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setName(name);
|
entity.setName(name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -103,6 +108,7 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setType(String type) {
|
public void setType(String type) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setType(type);
|
entity.setType(type);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -124,6 +130,7 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setIconUri(String iconUri) {
|
public void setIconUri(String iconUri) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setIconUri(iconUri);
|
entity.setIconUri(iconUri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -145,11 +152,13 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setOwnerManagedAccess(ownerManagedAccess);
|
entity.setOwnerManagedAccess(ownerManagedAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateScopes(Set<Scope> toUpdate) {
|
public void updateScopes(Set<Scope> toUpdate) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
Set<String> ids = new HashSet<>();
|
Set<String> ids = new HashSet<>();
|
||||||
for (Scope scope : toUpdate) {
|
for (Scope scope : toUpdate) {
|
||||||
ids.add(scope.getId());
|
ids.add(scope.getId());
|
||||||
|
@ -171,7 +180,7 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
for (ResourceAttributeEntity attr : entity.getAttributes()) {
|
for (ResourceAttributeEntity attr : entity.getAttributes()) {
|
||||||
result.add(attr.getName(), attr.getValue());
|
result.add(attr.getName(), attr.getValue());
|
||||||
}
|
}
|
||||||
return result;
|
return Collections.unmodifiableMap(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -213,6 +222,7 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAttribute(String name) {
|
public void removeAttribute(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
Query query = em.createNamedQuery("deleteResourceAttributesByNameAndResource");
|
Query query = em.createNamedQuery("deleteResourceAttributesByNameAndResource");
|
||||||
|
|
||||||
query.setParameter("name", name);
|
query.setParameter("name", name);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.authorization.jpa.store;
|
package org.keycloak.authorization.jpa.store;
|
||||||
|
|
||||||
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
|
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
|
||||||
|
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.jpa.JpaModel;
|
import org.keycloak.models.jpa.JpaModel;
|
||||||
|
@ -28,12 +29,13 @@ import javax.persistence.EntityManager;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ResourceServerAdapter implements ResourceServer, JpaModel<ResourceServerEntity> {
|
public class ResourceServerAdapter extends AbstractAuthorizationModel implements ResourceServer, JpaModel<ResourceServerEntity> {
|
||||||
private ResourceServerEntity entity;
|
private ResourceServerEntity entity;
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
private StoreFactory storeFactory;
|
private StoreFactory storeFactory;
|
||||||
|
|
||||||
public ResourceServerAdapter(ResourceServerEntity entity, EntityManager em, StoreFactory storeFactory) {
|
public ResourceServerAdapter(ResourceServerEntity entity, EntityManager em, StoreFactory storeFactory) {
|
||||||
|
super(storeFactory);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.em = em;
|
this.em = em;
|
||||||
this.storeFactory = storeFactory;
|
this.storeFactory = storeFactory;
|
||||||
|
@ -56,6 +58,7 @@ public class ResourceServerAdapter implements ResourceServer, JpaModel<ResourceS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setAllowRemoteResourceManagement(allowRemoteResourceManagement);
|
entity.setAllowRemoteResourceManagement(allowRemoteResourceManagement);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -67,6 +70,7 @@ public class ResourceServerAdapter implements ResourceServer, JpaModel<ResourceS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
|
public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setPolicyEnforcementMode(enforcementMode);
|
entity.setPolicyEnforcementMode(enforcementMode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.authorization.jpa.store;
|
package org.keycloak.authorization.jpa.store;
|
||||||
|
|
||||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||||
|
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||||
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.authorization.store.StoreFactory;
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
|
@ -28,12 +29,13 @@ import javax.persistence.EntityManager;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ScopeAdapter implements Scope, JpaModel<ScopeEntity> {
|
public class ScopeAdapter extends AbstractAuthorizationModel implements Scope, JpaModel<ScopeEntity> {
|
||||||
private ScopeEntity entity;
|
private ScopeEntity entity;
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
private StoreFactory storeFactory;
|
private StoreFactory storeFactory;
|
||||||
|
|
||||||
public ScopeAdapter(ScopeEntity entity, EntityManager em, StoreFactory storeFactory) {
|
public ScopeAdapter(ScopeEntity entity, EntityManager em, StoreFactory storeFactory) {
|
||||||
|
super(storeFactory);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.em = em;
|
this.em = em;
|
||||||
this.storeFactory = storeFactory;
|
this.storeFactory = storeFactory;
|
||||||
|
@ -56,6 +58,7 @@ public class ScopeAdapter implements Scope, JpaModel<ScopeEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setName(name);
|
entity.setName(name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -67,6 +70,7 @@ public class ScopeAdapter implements Scope, JpaModel<ScopeEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDisplayName(String name) {
|
public void setDisplayName(String name) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setDisplayName(name);
|
entity.setDisplayName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +81,7 @@ public class ScopeAdapter implements Scope, JpaModel<ScopeEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setIconUri(String iconUri) {
|
public void setIconUri(String iconUri) {
|
||||||
|
throwExceptionIfReadonly();
|
||||||
entity.setIconUri(iconUri);
|
entity.setIconUri(iconUri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,16 @@ public final class AuthorizationProvider implements Provider {
|
||||||
public void close() {
|
public void close() {
|
||||||
storeFactory.close();
|
storeFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly(boolean readOnly) {
|
||||||
|
storeFactory.setReadOnly(readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return storeFactory.isReadOnly();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.authorization.model;
|
||||||
|
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
|
|
||||||
|
public abstract class AbstractAuthorizationModel {
|
||||||
|
|
||||||
|
private StoreFactory storeFactory;
|
||||||
|
|
||||||
|
public AbstractAuthorizationModel(StoreFactory storeFactory) {
|
||||||
|
this.storeFactory = storeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void throwExceptionIfReadonly() {
|
||||||
|
if (storeFactory.isReadOnly()) {
|
||||||
|
throw new IllegalStateException("Instance marked as read-only");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.policy.evaluation.DecisionPermissionCollector;
|
import org.keycloak.authorization.policy.evaluation.DecisionPermissionCollector;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.authorization.policy.evaluation.PolicyEvaluator;
|
import org.keycloak.authorization.policy.evaluation.PolicyEvaluator;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||||
import org.keycloak.representations.idm.authorization.Permission;
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
|
|
||||||
|
@ -52,9 +53,13 @@ class IterablePermissionEvaluator implements PermissionEvaluator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Decision evaluate(Decision decision) {
|
public Decision evaluate(Decision decision) {
|
||||||
|
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<Policy, Map<Object, Decision.Effect>> decisionCache = new HashMap<>();
|
Map<Policy, Map<Object, Decision.Effect>> decisionCache = new HashMap<>();
|
||||||
|
|
||||||
|
storeFactory.setReadOnly(true);
|
||||||
|
|
||||||
while (this.permissions.hasNext()) {
|
while (this.permissions.hasNext()) {
|
||||||
this.policyEvaluator.evaluate(this.permissions.next(), authorizationProvider, executionContext, decision, decisionCache);
|
this.policyEvaluator.evaluate(this.permissions.next(), authorizationProvider, executionContext, decision, decisionCache);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +67,10 @@ class IterablePermissionEvaluator implements PermissionEvaluator {
|
||||||
decision.onComplete();
|
decision.onComplete();
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
decision.onError(cause);
|
decision.onError(cause);
|
||||||
|
} finally {
|
||||||
|
storeFactory.setReadOnly(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return decision;
|
return decision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,8 +263,7 @@ public class DefaultEvaluation implements Evaluation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getUserAttributes(String id) {
|
public Map<String, List<String>> getUserAttributes(String id) {
|
||||||
Map<String, List<String>> attributes = getUser(id, authorizationProvider.getKeycloakSession()).getAttributes();
|
return Collections.unmodifiableMap(getUser(id, authorizationProvider.getKeycloakSession()).getAttributes());
|
||||||
return attributes;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,4 +64,19 @@ public interface StoreFactory extends Provider {
|
||||||
* @return the permission ticket store
|
* @return the permission ticket store
|
||||||
*/
|
*/
|
||||||
PermissionTicketStore getPermissionTicketStore();
|
PermissionTicketStore getPermissionTicketStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not changes to instances returned from this factory are supported. Once marked as read-only, any attempt to
|
||||||
|
* change state will throw an {@link IllegalStateException}.
|
||||||
|
*
|
||||||
|
* @param readOnly if true, changes are not supported
|
||||||
|
*/
|
||||||
|
void setReadOnly(boolean readOnly);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if instances returned from storage are read-only.
|
||||||
|
*
|
||||||
|
* @return if true, instances only support reads.
|
||||||
|
*/
|
||||||
|
boolean isReadOnly();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,13 @@ import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
||||||
import org.keycloak.authorization.attribute.Attributes;
|
import org.keycloak.authorization.attribute.Attributes;
|
||||||
import org.keycloak.authorization.common.KeycloakEvaluationContext;
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||||
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.authorization.permission.ResourcePermission;
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.policy.evaluation.DecisionPermissionCollector;
|
import org.keycloak.authorization.policy.evaluation.DecisionPermissionCollector;
|
||||||
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
|
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result;
|
import org.keycloak.authorization.policy.evaluation.Result;
|
||||||
import org.keycloak.authorization.store.ScopeStore;
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
|
@ -132,7 +131,7 @@ public class PolicyEvaluationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
|
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
|
||||||
return new KeycloakEvaluationContext(identity, this.authorization.getKeycloakSession()) {
|
return new DefaultEvaluationContext(identity, this.authorization.getKeycloakSession()) {
|
||||||
@Override
|
@Override
|
||||||
public Attributes getAttributes() {
|
public Attributes getAttributes() {
|
||||||
Map<String, Collection<String>> attributes = new HashMap<>(super.getAttributes().toMap());
|
Map<String, Collection<String>> attributes = new HashMap<>(super.getAttributes().toMap());
|
||||||
|
|
|
@ -42,12 +42,13 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.common.KeycloakEvaluationContext;
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||||
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.authorization.permission.ResourcePermission;
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.authorization.policy.evaluation.PermissionTicketAwareDecisionResultCollector;
|
import org.keycloak.authorization.policy.evaluation.PermissionTicketAwareDecisionResultCollector;
|
||||||
import org.keycloak.authorization.store.ResourceServerStore;
|
import org.keycloak.authorization.store.ResourceServerStore;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
|
@ -92,7 +93,7 @@ public class AuthorizationTokenService {
|
||||||
private static final String RESPONSE_MODE_DECISION = "decision";
|
private static final String RESPONSE_MODE_DECISION = "decision";
|
||||||
private static final String RESPONSE_MODE_PERMISSIONS = "permissions";
|
private static final String RESPONSE_MODE_PERMISSIONS = "permissions";
|
||||||
private static final String RESPONSE_MODE_DECISION_RESULT = "result";
|
private static final String RESPONSE_MODE_DECISION_RESULT = "result";
|
||||||
private static Map<String, BiFunction<AuthorizationRequest, AuthorizationProvider, KeycloakEvaluationContext>> SUPPORTED_CLAIM_TOKEN_FORMATS;
|
private static Map<String, BiFunction<AuthorizationRequest, AuthorizationProvider, EvaluationContext>> SUPPORTED_CLAIM_TOKEN_FORMATS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SUPPORTED_CLAIM_TOKEN_FORMATS = new HashMap<>();
|
SUPPORTED_CLAIM_TOKEN_FORMATS = new HashMap<>();
|
||||||
|
@ -103,7 +104,7 @@ public class AuthorizationTokenService {
|
||||||
try {
|
try {
|
||||||
Map claims = JsonSerialization.readValue(Base64Url.decode(authorizationRequest.getClaimToken()), Map.class);
|
Map claims = JsonSerialization.readValue(Base64Url.decode(authorizationRequest.getClaimToken()), Map.class);
|
||||||
authorizationRequest.setClaims(claims);
|
authorizationRequest.setClaims(claims);
|
||||||
return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getSubjectToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
|
return new DefaultEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getSubjectToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
|
||||||
} catch (IOException cause) {
|
} catch (IOException cause) {
|
||||||
throw new RuntimeException("Failed to map claims from claim token [" + claimToken + "]", cause);
|
throw new RuntimeException("Failed to map claims from claim token [" + claimToken + "]", cause);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +115,6 @@ public class AuthorizationTokenService {
|
||||||
SUPPORTED_CLAIM_TOKEN_FORMATS.put(CLAIM_TOKEN_FORMAT_ID_TOKEN, (authorizationRequest, authorization) -> {
|
SUPPORTED_CLAIM_TOKEN_FORMATS.put(CLAIM_TOKEN_FORMAT_ID_TOKEN, (authorizationRequest, authorization) -> {
|
||||||
try {
|
try {
|
||||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||||
RealmModel realm = authorization.getRealm();
|
|
||||||
String accessToken = authorizationRequest.getSubjectToken();
|
String accessToken = authorizationRequest.getSubjectToken();
|
||||||
|
|
||||||
if (accessToken == null) {
|
if (accessToken == null) {
|
||||||
|
@ -122,7 +122,7 @@ public class AuthorizationTokenService {
|
||||||
}
|
}
|
||||||
|
|
||||||
IDToken idToken = new TokenManager().verifyIDTokenSignature(keycloakSession, accessToken);
|
IDToken idToken = new TokenManager().verifyIDTokenSignature(keycloakSession, accessToken);
|
||||||
return new KeycloakEvaluationContext(new KeycloakIdentity(keycloakSession, idToken), authorizationRequest.getClaims(), keycloakSession);
|
return new DefaultEvaluationContext(new KeycloakIdentity(keycloakSession, idToken), authorizationRequest.getClaims(), keycloakSession);
|
||||||
} catch (OAuthErrorException cause) {
|
} catch (OAuthErrorException cause) {
|
||||||
throw new RuntimeException("Failed to verify ID token", cause);
|
throw new RuntimeException("Failed to verify ID token", cause);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ public class AuthorizationTokenService {
|
||||||
request.setClaims(ticket.getClaims());
|
request.setClaims(ticket.getClaims());
|
||||||
|
|
||||||
ResourceServer resourceServer = getResourceServer(ticket, request);
|
ResourceServer resourceServer = getResourceServer(ticket, request);
|
||||||
KeycloakEvaluationContext evaluationContext = createEvaluationContext(request);
|
EvaluationContext evaluationContext = createEvaluationContext(request);
|
||||||
KeycloakIdentity identity = KeycloakIdentity.class.cast(evaluationContext.getIdentity());
|
KeycloakIdentity identity = KeycloakIdentity.class.cast(evaluationContext.getIdentity());
|
||||||
Collection<Permission> permissions;
|
Collection<Permission> permissions;
|
||||||
|
|
||||||
|
@ -213,21 +213,21 @@ public class AuthorizationTokenService {
|
||||||
return request.getClaimToken() != null && request.getKeycloakSession().getContext().getClient().isPublicClient() && request.getTicket() == null;
|
return request.getClaimToken() != null && request.getKeycloakSession().getContext().getClient().isPublicClient() && request.getTicket() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Permission> evaluatePermissions(KeycloakAuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
|
private Collection<Permission> evaluatePermissions(KeycloakAuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, EvaluationContext evaluationContext, KeycloakIdentity identity) {
|
||||||
AuthorizationProvider authorization = request.getAuthorization();
|
AuthorizationProvider authorization = request.getAuthorization();
|
||||||
return authorization.evaluators()
|
return authorization.evaluators()
|
||||||
.from(createPermissions(ticket, request, resourceServer, identity, authorization), evaluationContext)
|
.from(createPermissions(ticket, request, resourceServer, identity, authorization), evaluationContext)
|
||||||
.evaluate(resourceServer, request);
|
.evaluate(resourceServer, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Permission> evaluateUserManagedPermissions(KeycloakAuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
|
private Collection<Permission> evaluateUserManagedPermissions(KeycloakAuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, EvaluationContext evaluationContext, KeycloakIdentity identity) {
|
||||||
AuthorizationProvider authorization = request.getAuthorization();
|
AuthorizationProvider authorization = request.getAuthorization();
|
||||||
return authorization.evaluators()
|
return authorization.evaluators()
|
||||||
.from(createPermissions(ticket, request, resourceServer, identity, authorization), evaluationContext)
|
.from(createPermissions(ticket, request, resourceServer, identity, authorization), evaluationContext)
|
||||||
.evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
|
.evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Permission> evaluateAllPermissions(KeycloakAuthorizationRequest request, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
|
private Collection<Permission> evaluateAllPermissions(KeycloakAuthorizationRequest request, ResourceServer resourceServer, EvaluationContext evaluationContext, KeycloakIdentity identity) {
|
||||||
AuthorizationProvider authorization = request.getAuthorization();
|
AuthorizationProvider authorization = request.getAuthorization();
|
||||||
return authorization.evaluators()
|
return authorization.evaluators()
|
||||||
.from(Permissions.all(resourceServer, identity, authorization, request), evaluationContext)
|
.from(Permissions.all(resourceServer, identity, authorization, request), evaluationContext)
|
||||||
|
@ -340,14 +340,14 @@ public class AuthorizationTokenService {
|
||||||
return resourceServer;
|
return resourceServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeycloakEvaluationContext createEvaluationContext(KeycloakAuthorizationRequest request) {
|
private EvaluationContext createEvaluationContext(KeycloakAuthorizationRequest request) {
|
||||||
String claimTokenFormat = request.getClaimTokenFormat();
|
String claimTokenFormat = request.getClaimTokenFormat();
|
||||||
|
|
||||||
if (claimTokenFormat == null) {
|
if (claimTokenFormat == null) {
|
||||||
claimTokenFormat = CLAIM_TOKEN_FORMAT_ID_TOKEN;
|
claimTokenFormat = CLAIM_TOKEN_FORMAT_ID_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
BiFunction<AuthorizationRequest, AuthorizationProvider, KeycloakEvaluationContext> evaluationContextProvider = SUPPORTED_CLAIM_TOKEN_FORMATS.get(claimTokenFormat);
|
BiFunction<AuthorizationRequest, AuthorizationProvider, EvaluationContext> evaluationContextProvider = SUPPORTED_CLAIM_TOKEN_FORMATS.get(claimTokenFormat);
|
||||||
|
|
||||||
if (evaluationContextProvider == null) {
|
if (evaluationContextProvider == null) {
|
||||||
throw new CorsErrorResponseException(request.getCors(), OAuthErrorException.INVALID_REQUEST, "Claim token format [" + claimTokenFormat + "] not supported", Status.BAD_REQUEST);
|
throw new CorsErrorResponseException(request.getCors(), OAuthErrorException.INVALID_REQUEST, "Claim token format [" + claimTokenFormat + "] not supported", Status.BAD_REQUEST);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.authorization.attribute.Attributes;
|
||||||
import org.keycloak.authorization.identity.Identity;
|
import org.keycloak.authorization.identity.Identity;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -40,6 +41,7 @@ public class DefaultEvaluationContext implements EvaluationContext {
|
||||||
protected final KeycloakSession keycloakSession;
|
protected final KeycloakSession keycloakSession;
|
||||||
protected final Identity identity;
|
protected final Identity identity;
|
||||||
private final Map<String, List<String>> claims;
|
private final Map<String, List<String>> claims;
|
||||||
|
private Attributes attributes;
|
||||||
|
|
||||||
public DefaultEvaluationContext(Identity identity, KeycloakSession keycloakSession) {
|
public DefaultEvaluationContext(Identity identity, KeycloakSession keycloakSession) {
|
||||||
this(identity, null, keycloakSession);
|
this(identity, null, keycloakSession);
|
||||||
|
@ -56,7 +58,7 @@ public class DefaultEvaluationContext implements EvaluationContext {
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Collection<String>> getBaseAttributes() {
|
protected Map<String, Collection<String>> getBaseAttributes() {
|
||||||
Map<String, Collection<String>> attributes = new HashMap<>();
|
Map<String, Collection<String>> attributes = new HashMap<>();
|
||||||
|
|
||||||
attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
|
attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
|
||||||
|
@ -77,11 +79,22 @@ public class DefaultEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (KeycloakIdentity.class.isInstance(identity)) {
|
||||||
|
AccessToken accessToken = KeycloakIdentity.class.cast(this.identity).getAccessToken();
|
||||||
|
|
||||||
|
if (accessToken != null) {
|
||||||
|
attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attributes getAttributes() {
|
public Attributes getAttributes() {
|
||||||
return Attributes.from(getBaseAttributes());
|
if (attributes == null) {
|
||||||
|
attributes = Attributes.from(getBaseAttributes());
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* JBoss, Home of Professional Open Source.
|
|
||||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
|
||||||
* as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.authorization.common;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.keycloak.authorization.identity.Identity;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.representations.AccessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
|
||||||
*/
|
|
||||||
public class KeycloakEvaluationContext extends DefaultEvaluationContext {
|
|
||||||
|
|
||||||
private final KeycloakIdentity identity;
|
|
||||||
|
|
||||||
public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
|
|
||||||
this(identity, null, keycloakSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeycloakEvaluationContext(KeycloakIdentity identity, Map<String, List<String>> claims, KeycloakSession keycloakSession) {
|
|
||||||
super(identity, claims, keycloakSession);
|
|
||||||
this.identity = identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Identity getIdentity() {
|
|
||||||
return this.identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Collection<String>> getBaseAttributes() {
|
|
||||||
Map<String, Collection<String>> attributes = super.getBaseAttributes();
|
|
||||||
AccessToken accessToken = this.identity.getAccessToken();
|
|
||||||
|
|
||||||
if (accessToken != null) {
|
|
||||||
attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
|
|
||||||
}
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,8 +19,10 @@ package org.keycloak.testsuite.authz;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -28,11 +30,9 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jboss.arquillian.container.test.api.Deployment;
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.Decision;
|
|
||||||
import org.keycloak.authorization.Decision.Effect;
|
import org.keycloak.authorization.Decision.Effect;
|
||||||
import org.keycloak.authorization.attribute.Attributes;
|
import org.keycloak.authorization.attribute.Attributes;
|
||||||
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
|
@ -40,9 +40,10 @@ import org.keycloak.authorization.identity.Identity;
|
||||||
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;
|
||||||
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.permission.ResourcePermission;
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
|
import org.keycloak.authorization.permission.evaluator.PermissionEvaluator;
|
||||||
import org.keycloak.authorization.policy.evaluation.DefaultEvaluation;
|
import org.keycloak.authorization.policy.evaluation.DefaultEvaluation;
|
||||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
|
||||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -57,6 +58,7 @@ import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
|
||||||
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
|
@ -630,6 +632,52 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
|
||||||
Assert.assertEquals(Effect.PERMIT, evaluation.getEffect());
|
Assert.assertEquals(Effect.PERMIT, evaluation.getEffect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckReadOnlyInstances() {
|
||||||
|
testingClient.server().run(PolicyEvaluationTest::testCheckReadOnlyInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testCheckReadOnlyInstances(KeycloakSession session) {
|
||||||
|
session.getContext().setRealm(session.realms().getRealmByName("authz-test"));
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm());
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId());
|
||||||
|
JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation();
|
||||||
|
|
||||||
|
policyRepresentation.setName("testCheckReadOnlyInstances");
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.append("$evaluation.getPermission().getResource().setName('test')");
|
||||||
|
|
||||||
|
policyRepresentation.setCode(builder.toString());
|
||||||
|
|
||||||
|
Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer);
|
||||||
|
|
||||||
|
Resource resource = storeFactory.getResourceStore().create("Resource A", resourceServer, resourceServer.getId());
|
||||||
|
Scope scope = storeFactory.getScopeStore().create("Scope A", resourceServer);
|
||||||
|
|
||||||
|
resource.updateScopes(new HashSet<>(Arrays.asList(scope)));
|
||||||
|
|
||||||
|
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||||
|
|
||||||
|
permission.setName("testCheckReadOnlyInstances permission");
|
||||||
|
permission.addPolicy(policy.getId());
|
||||||
|
permission.addResource(resource.getId());
|
||||||
|
|
||||||
|
storeFactory.getPolicyStore().create(permission, resourceServer);
|
||||||
|
|
||||||
|
session.getTransactionManager().commit();
|
||||||
|
|
||||||
|
PermissionEvaluator evaluator = authorization.evaluators().from(Arrays.asList(new ResourcePermission(resource, Arrays.asList(scope), resourceServer)), createEvaluationContext(session, Collections.emptyMap()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluator.evaluate(resourceServer, null);
|
||||||
|
Assert.fail("Instances should be marked as read-only");
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization, ResourceServer resourceServer, Policy policy) {
|
private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization, ResourceServer resourceServer, Policy policy) {
|
||||||
return createEvaluation(session, authorization, null, resourceServer, policy);
|
return createEvaluation(session, authorization, null, resourceServer, policy);
|
||||||
}
|
}
|
||||||
|
@ -641,7 +689,11 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
|
||||||
private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization,
|
private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization,
|
||||||
Resource resource, ResourceServer resourceServer, Policy policy,
|
Resource resource, ResourceServer resourceServer, Policy policy,
|
||||||
Map<String, Collection<String>> contextAttributes) {
|
Map<String, Collection<String>> contextAttributes) {
|
||||||
return new DefaultEvaluation(new ResourcePermission(resource, null, resourceServer), new DefaultEvaluationContext(new Identity() {
|
return new DefaultEvaluation(new ResourcePermission(resource, null, resourceServer), createEvaluationContext(session, contextAttributes), policy, evaluation -> {}, authorization, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultEvaluationContext createEvaluationContext(KeycloakSession session, Map<String, Collection<String>> contextAttributes) {
|
||||||
|
return new DefaultEvaluationContext(new Identity() {
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -664,6 +716,6 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
|
||||||
}
|
}
|
||||||
return baseAttributes;
|
return baseAttributes;
|
||||||
}
|
}
|
||||||
}, policy, evaluation -> {}, authorization, null);
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue