diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java index c93465eaff..3914c68dd9 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java +++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java @@ -419,7 +419,7 @@ public class CachedPolicyStore implements PolicyStore { List result = provider.get(); if (result.isEmpty()) { - return null; + return Collections.emptyList(); } return result.stream().map(policy -> new CachedPolicy(policy)).collect(Collectors.toList()); @@ -429,11 +429,6 @@ public class CachedPolicyStore implements PolicyStore { return Collections.emptyList(); } - return cached.stream().map(new Function() { - @Override - public Policy apply(CachedPolicy cachedPolicy) { - return findById(cachedPolicy.getId(), cachedPolicy.getResourceServerId()); - } - }).collect(Collectors.toList()); + return cached.stream().map(cachedPolicy -> createAdapter(cachedPolicy)).collect(Collectors.toList()); } } \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java index 742f2e4f41..6ebac833c5 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java +++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java @@ -310,12 +310,7 @@ public class CachedResourceStore implements ResourceStore { return Collections.emptyList(); } - return cached.stream().map(new Function() { - @Override - public Resource apply(CachedResource cached) { - return findById(cached.getId(), cached.getResourceServerId()); - } - }).collect(Collectors.toList()); + return cached.stream().map(this::createAdapter).collect(Collectors.toList()); } private void invalidateCache(String resourceServerId) { diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java index e1ba326c7f..648b1b3175 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java @@ -18,11 +18,10 @@ package org.keycloak.authorization.jpa.entities; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; -import org.keycloak.authorization.model.Scope; -import org.keycloak.representations.idm.authorization.DecisionStrategy; -import org.keycloak.representations.idm.authorization.Logic; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import javax.persistence.Access; import javax.persistence.AccessType; @@ -34,15 +33,17 @@ import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; + +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.Scope; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.Logic; /** * @author Pedro Igor @@ -79,19 +80,19 @@ public class PolicyEntity implements Policy { @CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") }) private Map config = new HashMap(); - @ManyToOne(optional = false) + @ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "RESOURCE_SERVER_ID") private ResourceServerEntity resourceServer; - @ManyToMany(fetch = FetchType.LAZY, cascade = {}) + @OneToMany(fetch = FetchType.LAZY, cascade = {}) @JoinTable(name = "ASSOCIATED_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "ASSOCIATED_POLICY_ID")) private Set associatedPolicies = new HashSet<>(); - @ManyToMany(fetch = FetchType.LAZY, cascade = {}) + @OneToMany(fetch = FetchType.LAZY, cascade = {}) @JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "RESOURCE_ID")) private Set resources = new HashSet<>(); - @ManyToMany(fetch = FetchType.EAGER, cascade = {}) + @OneToMany(fetch = FetchType.EAGER, cascade = {}) @JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID")) private Set scopes = new HashSet<>(); diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java index 7cb1a6f565..29b5740d0c 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java @@ -67,7 +67,7 @@ public class ResourceEntity implements Resource { @Column(name = "OWNER") private String owner; - @ManyToOne(optional = false) + @ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "RESOURCE_SERVER_ID") private ResourceServerEntity resourceServer; diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java index 99f8b41edb..9f2c3b8827 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java @@ -57,7 +57,7 @@ public class ScopeEntity implements Scope { @Column(name = "ICON_URI") private String iconUri; - @ManyToOne(optional = false) + @ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "RESOURCE_SERVER_ID") private ResourceServerEntity resourceServer; diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java index 3d9f9232bd..90de912b26 100644 --- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java +++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java @@ -61,8 +61,8 @@ public final class Permissions { StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); - resourceStore.findByOwner(resourceServer.getClientId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization))); - resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization))); + resourceStore.findByOwner(resourceServer.getClientId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization))); + resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization))); return permissions; } @@ -108,6 +108,32 @@ public final class Permissions { return permissions; } + public static List createResourcePermissionsWithScopes(Resource resource, List scopes, AuthorizationProvider authorization) { + List permissions = new ArrayList<>(); + String type = resource.getType(); + ResourceServer resourceServer = resource.getResourceServer(); + + // check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource + // is owned by the resource server itself + if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) { + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceStore resourceStore = storeFactory.getResourceStore(); + resourceStore.findByType(type, resourceServer.getId()).forEach(resource1 -> { + if (resource1.getOwner().equals(resourceServer.getClientId())) { + for (Scope typeScope : resource1.getScopes()) { + if (!scopes.contains(typeScope)) { + scopes.add(typeScope); + } + } + } + }); + } + + permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer())); + + return permissions; + } + public static List allPermits(List evaluation, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) { Map permissions = new HashMap<>(); diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html index 11a4d9d377..419622f5cc 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html @@ -83,7 +83,7 @@ - +
Dependent PermissionsAssociated Permissions