keycloak-uncached
Changes
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java 22(+8 -14)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java 7(+4 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java 23(+7 -16)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java 61(+35 -26)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java 20(+11 -9)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java 17(+14 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java 3(+1 -2)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java 7(+4 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java 37(+12 -25)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java 90(+59 -31)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java 7(+0 -7)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java 7(+4 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java 40(+20 -20)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java 7(+4 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java 9(+2 -7)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java 72(+44 -28)
authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java 13(+8 -5)
authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java 42(+15 -27)
core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java 23(+0 -23)
core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java 31(+15 -16)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PolicyResource.java 33(+31 -2)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceResource.java 10(+10 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopeResource.java 9(+9 -0)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java 234(+126 -108)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java 52(+28 -24)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java 168(+87 -81)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java 72(+42 -30)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java 19(+15 -4)
model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java 25(+14 -11)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java 11(+4 -7)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java 7(+3 -4)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java 6(+4 -2)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java 28(+14 -14)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java 102(+50 -52)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java 11(+11 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java 2(+1 -1)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java 2(+1 -1)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java 33(+23 -10)
services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java 39(+3 -36)
services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java 57(+43 -14)
services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java 10(+5 -5)
services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java 29(+26 -3)
services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java 4(+2 -2)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java 6(+3 -3)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java 16(+11 -5)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java 6(+3 -3)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java 9(+3 -6)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java 93(+54 -39)
themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html 8(+2 -6)
themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html 26(+6 -20)
themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html 50(+41 -9)
themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html 5(+1 -4)
themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html 26(+7 -19)
themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html 45(+44 -1)
themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html 4(+1 -3)
themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html 77(+55 -22)
Details
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java
index b1e668a..b0b9847 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java
@@ -17,6 +17,8 @@
*/
package org.keycloak.authorization.policy.provider.aggregated;
+import java.util.List;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
@@ -26,21 +28,11 @@ import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
-import java.util.List;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class AggregatePolicyProvider implements PolicyProvider {
- private final Policy policy;
- private final AuthorizationProvider authorization;
-
- public AggregatePolicyProvider(Policy policy, AuthorizationProvider authorization) {
- this.policy = policy;
- this.authorization = authorization;
- }
-
@Override
public void evaluate(Evaluation evaluation) {
//TODO: need to detect deep recursions
@@ -59,10 +51,12 @@ public class AggregatePolicyProvider implements PolicyProvider {
}
};
- this.policy.getAssociatedPolicies().forEach(associatedPolicy -> {
- PolicyProviderFactory providerFactory = authorization.getProviderFactory(associatedPolicy.getType());
- PolicyProvider policyProvider = providerFactory.create(associatedPolicy, authorization);
- policyProvider.evaluate(new DefaultEvaluation(evaluation.getPermission(), evaluation.getContext(), policy, associatedPolicy, decision));
+ Policy policy = evaluation.getPolicy();
+ AuthorizationProvider authorization = evaluation.getAuthorizationProvider();
+
+ policy.getAssociatedPolicies().forEach(associatedPolicy -> {
+ PolicyProvider policyProvider = authorization.getProvider(associatedPolicy.getType());
+ policyProvider.evaluate(new DefaultEvaluation(evaluation.getPermission(), evaluation.getContext(), policy, associatedPolicy, decision, authorization));
});
decision.onComplete();
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
index 9c7faad..3e86973 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
@@ -19,7 +19,6 @@ package org.keycloak.authorization.policy.provider.aggregated;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
@@ -32,6 +31,8 @@ import org.keycloak.models.KeycloakSessionFactory;
*/
public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
+ private AggregatePolicyProvider provider = new AggregatePolicyProvider();
+
@Override
public String getName() {
return "Aggregated";
@@ -43,8 +44,8 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new AggregatePolicyProvider(policy, authorization);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
index ec84bbc..5c778d8 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
@@ -1,5 +1,7 @@
package org.keycloak.authorization.policy.provider.client;
+import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
@@ -8,26 +10,19 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
-import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
-
public class ClientPolicyProvider implements PolicyProvider {
- private final Policy policy;
- private final AuthorizationProvider authorization;
-
- public ClientPolicyProvider(Policy policy, AuthorizationProvider authorization) {
- this.policy = policy;
- this.authorization = authorization;
- }
-
@Override
public void evaluate(Evaluation evaluation) {
+ Policy policy = evaluation.getPolicy();
EvaluationContext context = evaluation.getContext();
- String[] clients = getClients(this.policy);
+ String[] clients = getClients(policy);
+ AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
+ RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
if (clients.length > 0) {
for (String client : clients) {
- ClientModel clientModel = getCurrentRealm().getClientById(client);
+ ClientModel clientModel = realm.getClientById(client);
if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
evaluation.grant();
return;
@@ -40,8 +35,4 @@ public class ClientPolicyProvider implements PolicyProvider {
public void close() {
}
-
- private RealmModel getCurrentRealm() {
- return this.authorization.getKeycloakSession().getContext().getRealm();
- }
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
index e800a5b..8cb0029 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
@@ -1,5 +1,9 @@
package org.keycloak.authorization.policy.provider.client;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@@ -8,18 +12,18 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel.ClientRemovedEvent;
import org.keycloak.util.JsonSerialization;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
public class ClientPolicyProviderFactory implements PolicyProviderFactory {
+ private ClientPolicyProvider provider = new ClientPolicyProvider();
+
@Override
public String getName() {
return "Client";
@@ -31,8 +35,8 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new ClientPolicyProvider(policy, authorization);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
@@ -56,31 +60,36 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
if (event instanceof ClientRemovedEvent) {
KeycloakSession keycloakSession = ((ClientRemovedEvent) event).getKeycloakSession();
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
- PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
+ StoreFactory storeFactory = provider.getStoreFactory();
+ PolicyStore policyStore = storeFactory.getPolicyStore();
ClientModel removedClient = ((ClientRemovedEvent) event).getClient();
+ ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+ ResourceServer resourceServer = resourceServerStore.findByClient(removedClient.getId());
- policyStore.findByType(getId()).forEach(policy -> {
- List<String> clients = new ArrayList<>();
+ if (resourceServer != null) {
+ policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
+ List<String> clients = new ArrayList<>();
- for (String clientId : getClients(policy)) {
- if (!clientId.equals(removedClient.getId())) {
- clients.add(clientId);
+ for (String clientId : getClients(policy)) {
+ if (!clientId.equals(removedClient.getId())) {
+ clients.add(clientId);
+ }
}
- }
-
- try {
- if (clients.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- });
- policyStore.delete(policy.getId());
- } else {
- policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
+
+ try {
+ if (clients.isEmpty()) {
+ policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
+ dependentPolicy.removeAssociatedPolicy(policy);
+ });
+ policyStore.delete(policy.getId());
+ } else {
+ policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error while synchronizing clients with policy [" + policy.getName() + "].", e);
}
- } catch (IOException e) {
- throw new RuntimeException("Error while synchronizing clients with policy [" + policy.getName() + "].", e);
- }
- });
+ });
+ }
}
});
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java
index eeeb3ea..f875731 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java
@@ -17,32 +17,34 @@
*/
package org.keycloak.authorization.policy.provider.js;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.policy.evaluation.Evaluation;
-import org.keycloak.authorization.policy.provider.PolicyProvider;
+import java.util.function.Supplier;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class JSPolicyProvider implements PolicyProvider {
- private final Policy policy;
+ private Supplier<ScriptEngine> engineProvider;
- public JSPolicyProvider(Policy policy) {
- this.policy = policy;
+ public JSPolicyProvider(Supplier<ScriptEngine> engineProvider) {
+ this.engineProvider = engineProvider;
}
@Override
public void evaluate(Evaluation evaluation) {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ ScriptEngine engine = engineProvider.get();
engine.put("$evaluation", evaluation);
+ Policy policy = evaluation.getPolicy();
+
try {
engine.eval(policy.getConfig().get("code"));
} catch (ScriptException e) {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
index 8134d95..b4a5099 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
@@ -1,8 +1,12 @@
package org.keycloak.authorization.policy.provider.js;
+import java.util.function.Supplier;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
@@ -15,6 +19,13 @@ import org.keycloak.models.KeycloakSessionFactory;
*/
public class JSPolicyProviderFactory implements PolicyProviderFactory {
+ private JSPolicyProvider provider = new JSPolicyProvider(new Supplier<ScriptEngine>() {
+ @Override
+ public ScriptEngine get() {
+ return new ScriptEngineManager().getEngineByName("nashorn");
+ }
+ });
+
@Override
public String getName() {
return "JavaScript";
@@ -26,8 +37,8 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new JSPolicyProvider(policy);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java
index c76a989..69e93fe 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java
@@ -17,7 +17,6 @@
*/
package org.keycloak.authorization.policy.provider.resource;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -26,7 +25,7 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
*/
public class ResourcePolicyProvider implements PolicyProvider {
- public ResourcePolicyProvider(Policy policy) {
+ public ResourcePolicyProvider() {
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
index 8ea4800..d7a6b2b 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
@@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.resource;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
@@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
*/
public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
+ private ResourcePolicyProvider provider = new ResourcePolicyProvider();
+
@Override
public String getName() {
return "Resource-Based";
@@ -26,8 +27,8 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new ResourcePolicyProvider(policy);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
index 9fb9787..4aafedd 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
@@ -17,6 +17,10 @@
*/
package org.keycloak.authorization.policy.provider.role;
+import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
+
+import java.util.Map;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.identity.Identity;
import org.keycloak.authorization.model.Policy;
@@ -26,39 +30,26 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import java.util.Map;
-
-import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class RolePolicyProvider implements PolicyProvider {
- private final Policy policy;
- private final AuthorizationProvider authorization;
-
- public RolePolicyProvider(Policy policy, AuthorizationProvider authorization) {
- this.policy = policy;
- this.authorization = authorization;
- }
-
- public RolePolicyProvider() {
- this(null, null);
- }
-
@Override
public void evaluate(Evaluation evaluation) {
- Map<String, Object>[] roleIds = getRoles(this.policy);
+ Policy policy = evaluation.getPolicy();
+ Map<String, Object>[] roleIds = getRoles(policy);
+ AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
+ RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
if (roleIds.length > 0) {
Identity identity = evaluation.getContext().getIdentity();
for (Map<String, Object> current : roleIds) {
- RoleModel role = getCurrentRealm().getRoleById((String) current.get("id"));
+ RoleModel role = realm.getRoleById((String) current.get("id"));
if (role != null) {
- boolean hasRole = hasRole(identity, role);
+ boolean hasRole = hasRole(identity, role, realm);
if (!hasRole && Boolean.valueOf(isRequired(current))) {
evaluation.deny();
@@ -75,19 +66,15 @@ public class RolePolicyProvider implements PolicyProvider {
return (boolean) current.getOrDefault("required", false);
}
- private boolean hasRole(Identity identity, RoleModel role) {
+ private boolean hasRole(Identity identity, RoleModel role, RealmModel realm) {
String roleName = role.getName();
if (role.isClientRole()) {
- ClientModel clientModel = getCurrentRealm().getClientById(role.getContainerId());
+ ClientModel clientModel = realm.getClientById(role.getContainerId());
return identity.hasClientRole(clientModel.getClientId(), roleName);
}
return identity.hasRealmRole(roleName);
}
- private RealmModel getCurrentRealm() {
- return this.authorization.getKeycloakSession().getContext().getRealm();
- }
-
@Override
public void close() {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
index 67de87a..33db2d5 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
@@ -26,8 +26,13 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
import org.keycloak.models.RoleModel;
import org.keycloak.util.JsonSerialization;
@@ -37,12 +42,15 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class RolePolicyProviderFactory implements PolicyProviderFactory {
+ private RolePolicyProvider provider = new RolePolicyProvider();
+
@Override
public String getName() {
return "Role";
@@ -54,8 +62,8 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new RolePolicyProvider(policy, authorization);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
@@ -79,41 +87,61 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
if (event instanceof RoleRemovedEvent) {
KeycloakSession keycloakSession = ((RoleRemovedEvent) event).getKeycloakSession();
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
- PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
+ StoreFactory storeFactory = provider.getStoreFactory();
+ PolicyStore policyStore = storeFactory.getPolicyStore();
RoleModel removedRole = ((RoleRemovedEvent) event).getRole();
+ RoleContainerModel container = removedRole.getContainer();
+ ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+
+ if (container instanceof RealmModel) {
+ RealmModel realm = (RealmModel) container;
+ realm.getClients().forEach(clientModel -> updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore));
+ } else {
+ ClientModel clientModel = (ClientModel) container;
+ updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore);
+ }
+ }
+ });
+ }
- policyStore.findByType(getId()).forEach(policy -> {
- List<Map> roles = new ArrayList<>();
+ private void updateResourceServer(ClientModel clientModel, RoleModel removedRole, ResourceServerStore resourceServerStore, PolicyStore policyStore) {
+ ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
- for (Map<String,Object> role : getRoles(policy)) {
- if (!role.get("id").equals(removedRole.getId())) {
- Map updated = new HashMap();
- updated.put("id", role.get("id"));
- Object required = role.get("required");
- if (required != null) {
- updated.put("required", required);
- }
- roles.add(updated);
- }
- }
+ if (resourceServer != null) {
+ policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
+ List<Map> roles = new ArrayList<>();
- try {
- if (roles.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- });
- policyStore.delete(policy.getId());
- } else {
- Map<String, String> config = policy.getConfig();
- config.put("roles", JsonSerialization.writeValueAsString(roles));
- policy.setConfig(config);
+ for (Map<String,Object> role : getRoles(policy)) {
+ if (!role.get("id").equals(removedRole.getId())) {
+ Map updated = new HashMap();
+ updated.put("id", role.get("id"));
+ Object required = role.get("required");
+ if (required != null) {
+ updated.put("required", required);
}
- } catch (IOException e) {
- throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
+ roles.add(updated);
}
- });
- }
- });
+ }
+
+ try {
+ if (roles.isEmpty()) {
+ policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
+ dependentPolicy.removeAssociatedPolicy(policy);
+ if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
+ policyStore.delete(dependentPolicy.getId());
+ }
+ });
+ policyStore.delete(policy.getId());
+ } else {
+ Map<String, String> config = policy.getConfig();
+ config.put("roles", JsonSerialization.writeValueAsString(roles));
+ policy.setConfig(config);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
+ }
+ });
+ }
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java
index 5c10cc3..0a8cf85 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java
@@ -17,7 +17,6 @@
*/
package org.keycloak.authorization.policy.provider.scope;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -26,12 +25,6 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
*/
public class ScopePolicyProvider implements PolicyProvider {
- private final Policy policy;
-
- public ScopePolicyProvider(Policy policy) {
- this.policy = policy;
- }
-
@Override
public void evaluate(Evaluation evaluation) {
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
index 6ed0cd5..0678eb3 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
@@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.scope;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
@@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
*/
public class ScopePolicyProviderFactory implements PolicyProviderFactory {
+ private ScopePolicyProvider provider = new ScopePolicyProvider();
+
@Override
public String getName() {
return "Scope-Based";
@@ -26,8 +27,8 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new ScopePolicyProvider(policy);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
index 84ba1a2..7ce4c6e 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
@@ -17,14 +17,14 @@
*/
package org.keycloak.authorization.policy.provider.time;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.policy.evaluation.Evaluation;
-import org.keycloak.authorization.policy.provider.PolicyProvider;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -32,51 +32,51 @@ public class TimePolicyProvider implements PolicyProvider {
static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss";
- private final Policy policy;
private final SimpleDateFormat dateFormat;
private final Date currentDate;
- public TimePolicyProvider(Policy policy) {
- this.policy = policy;
+ public TimePolicyProvider() {
this.dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
this.currentDate = new Date();
}
@Override
public void evaluate(Evaluation evaluation) {
+ Policy policy = evaluation.getPolicy();
+
try {
- String notBefore = this.policy.getConfig().get("nbf");
- if (notBefore != null) {
+ String notBefore = policy.getConfig().get("nbf");
+ if (notBefore != null && !"".equals(notBefore)) {
if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) {
evaluation.deny();
return;
}
}
- String notOnOrAfter = this.policy.getConfig().get("noa");
- if (notOnOrAfter != null) {
+ String notOnOrAfter = policy.getConfig().get("noa");
+ if (notOnOrAfter != null && !"".equals(notOnOrAfter)) {
if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
evaluation.deny();
return;
}
}
- if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth")
- || isInvalid(Calendar.MONTH, "month")
- || isInvalid(Calendar.YEAR, "year")
- || isInvalid(Calendar.HOUR_OF_DAY, "hour")
- || isInvalid(Calendar.MINUTE, "minute")) {
+ if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth", policy)
+ || isInvalid(Calendar.MONTH, "month", policy)
+ || isInvalid(Calendar.YEAR, "year", policy)
+ || isInvalid(Calendar.HOUR_OF_DAY, "hour", policy)
+ || isInvalid(Calendar.MINUTE, "minute", policy)) {
evaluation.deny();
return;
}
evaluation.grant();
} catch (Exception e) {
- throw new RuntimeException("Could not evaluate time-based policy [" + this.policy.getName() + "].", e);
+ throw new RuntimeException("Could not evaluate time-based policy [" + policy.getName() + "].", e);
}
}
- private boolean isInvalid(int timeConstant, String configName) {
+ private boolean isInvalid(int timeConstant, String configName, Policy policy) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(this.currentDate);
@@ -87,9 +87,9 @@ public class TimePolicyProvider implements PolicyProvider {
dateField++;
}
- String start = this.policy.getConfig().get(configName);
+ String start = policy.getConfig().get(configName);
if (start != null) {
- String end = this.policy.getConfig().get(configName + "End");
+ String end = policy.getConfig().get(configName + "End");
if (end != null) {
if (dateField < Integer.parseInt(start) || dateField > Integer.parseInt(end)) {
return true;
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
index efe3cd2..94c5aad 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
@@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.time;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
@@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
*/
public class TimePolicyProviderFactory implements PolicyProviderFactory {
+ private TimePolicyProvider provider = new TimePolicyProvider();
+
@Override
public String getName() {
return "Time";
@@ -26,8 +27,8 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new TimePolicyProvider(policy);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
index a6fc0a4..2f77106 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
@@ -29,16 +29,11 @@ import static org.keycloak.authorization.policy.provider.user.UserPolicyProvider
*/
public class UserPolicyProvider implements PolicyProvider {
- private final Policy policy;
-
- public UserPolicyProvider(Policy policy) {
- this.policy = policy;
- }
-
@Override
public void evaluate(Evaluation evaluation) {
+ Policy policy = evaluation.getPolicy();
EvaluationContext context = evaluation.getContext();
- String[] userIds = getUsers(this.policy);
+ String[] userIds = getUsers(policy);
if (userIds.length > 0) {
for (String userId : userIds) {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
index fdeeac0..09345ec 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
@@ -18,6 +18,10 @@
package org.keycloak.authorization.policy.provider.user;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@@ -26,21 +30,22 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.UserRemovedEvent;
import org.keycloak.util.JsonSerialization;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class UserPolicyProviderFactory implements PolicyProviderFactory {
+ private UserPolicyProvider provider = new UserPolicyProvider();
+
@Override
public String getName() {
return "User";
@@ -52,8 +57,8 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new UserPolicyProvider(policy);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
@@ -77,29 +82,40 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
if (event instanceof UserRemovedEvent) {
KeycloakSession keycloakSession = ((UserRemovedEvent) event).getKeycloakSession();
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
- PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
+ StoreFactory storeFactory = provider.getStoreFactory();
+ PolicyStore policyStore = storeFactory.getPolicyStore();
UserModel removedUser = ((UserRemovedEvent) event).getUser();
-
- policyStore.findByType(getId()).forEach(policy -> {
- List<String> users = new ArrayList<>();
-
- for (String userId : getUsers(policy)) {
- if (!userId.equals(removedUser.getId())) {
- users.add(userId);
- }
- }
-
- try {
- if (users.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- });
- policyStore.delete(policy.getId());
- } else {
- policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
- }
- } catch (IOException e) {
- throw new RuntimeException("Error while synchronizing users with policy [" + policy.getName() + "].", e);
+ RealmModel realm = ((UserRemovedEvent) event).getRealm();
+ ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+ realm.getClients().forEach(clientModel -> {
+ ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
+
+ if (resourceServer != null) {
+ policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
+ List<String> users = new ArrayList<>();
+
+ for (String userId : getUsers(policy)) {
+ if (!userId.equals(removedUser.getId())) {
+ users.add(userId);
+ }
+ }
+
+ try {
+ if (users.isEmpty()) {
+ policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
+ dependentPolicy.removeAssociatedPolicy(policy);
+ if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
+ policyStore.delete(dependentPolicy.getId());
+ }
+ });
+ policyStore.delete(policy.getId());
+ } else {
+ policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error while synchronizing users with policy [" + policy.getName() + "].", e);
+ }
+ });
}
});
}
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java
index c53e361..77023f5 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java
@@ -17,6 +17,9 @@
*/
package org.keycloak.authorization.policy.provider.drools;
+import java.util.function.Function;
+
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -25,15 +28,15 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
*/
public class DroolsPolicyProvider implements PolicyProvider {
- private final DroolsPolicy policy;
+ private final Function<Policy, DroolsPolicy> policy;
- public DroolsPolicyProvider(DroolsPolicy policy) {
- this.policy = policy;
+ public DroolsPolicyProvider(Function<Policy, DroolsPolicy> policyProvider) {
+ this.policy = policyProvider;
}
@Override
- public void evaluate(Evaluation evaluationt) {
- this.policy.evaluate(evaluationt);
+ public void evaluate(Evaluation evaluation) {
+ policy.apply(evaluation.getPolicy()).evaluate(evaluation);
}
@Override
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
index b3305ae..74ed89d 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
@@ -1,5 +1,9 @@
package org.keycloak.authorization.policy.provider.drools;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@@ -9,24 +13,25 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.models.utils.PostMigrationEvent;
-import org.keycloak.provider.ProviderEvent;
-import org.keycloak.provider.ProviderEventListener;
-import org.keycloak.provider.ProviderFactory;
import org.kie.api.KieServices;
import org.kie.api.KieServices.Factory;
import org.kie.api.runtime.KieContainer;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
private KieServices ks;
- private final Map<String, DroolsPolicy> containers = new HashMap<>();
+ private final Map<String, DroolsPolicy> containers = Collections.synchronizedMap(new HashMap<>());
+ private DroolsPolicyProvider provider = new DroolsPolicyProvider(policy -> {
+ if (!containers.containsKey(policy.getId())) {
+ synchronized (containers) {
+ update(policy);
+ }
+ }
+ return containers.get(policy.getId());
+ });
@Override
public String getName() {
@@ -39,12 +44,8 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- if (!this.containers.containsKey(policy.getId())) {
- update(policy);
- }
-
- return new DroolsPolicyProvider(this.containers.get(policy.getId()));
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return provider;
}
@Override
@@ -64,19 +65,6 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
@Override
public void postInit(KeycloakSessionFactory factory) {
- factory.register(new ProviderEventListener() {
-
- @Override
- public void onEvent(ProviderEvent event) {
- // Ensure the initialization is done after DB upgrade is finished
- if (event instanceof PostMigrationEvent) {
- ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
- AuthorizationProvider authorization = providerFactory.create(factory.create());
- authorization.getStoreFactory().getPolicyStore().findByType(getId()).forEach(DroolsPolicyProviderFactory.this::update);
- }
- }
-
- });
}
@Override
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
index 9a51031..dc04991 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
@@ -16,11 +16,7 @@
*/
package org.keycloak.representations.idm.authorization;
-import com.fasterxml.jackson.annotation.JsonInclude;
-
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -36,9 +32,6 @@ public class PolicyRepresentation {
private Logic logic = Logic.POSITIVE;
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
private Map<String, String> config = new HashMap();
- private List<PolicyRepresentation> dependentPolicies;
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- private List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
public String getId() {
return this.id;
@@ -96,14 +89,6 @@ public class PolicyRepresentation {
this.description = description;
}
- public List<PolicyRepresentation> getAssociatedPolicies() {
- return associatedPolicies;
- }
-
- public void setAssociatedPolicies(List<PolicyRepresentation> associatedPolicies) {
- this.associatedPolicies = associatedPolicies;
- }
-
@Override
public boolean equals(final Object o) {
if (this == o) return true;
@@ -116,12 +101,4 @@ public class PolicyRepresentation {
public int hashCode() {
return Objects.hash(getId());
}
-
- public void setDependentPolicies(List<PolicyRepresentation> dependentPolicies) {
- this.dependentPolicies = dependentPolicies;
- }
-
- public List<PolicyRepresentation> getDependentPolicies() {
- return this.dependentPolicies;
- }
}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
index 8f3c795..86f6f98 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
@@ -16,14 +16,14 @@
*/
package org.keycloak.representations.idm.authorization;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
import java.net.URI;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
-import java.util.function.Predicate;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
/**
* <p>One or more resources that the resource server manages as a set of protected resources.
@@ -159,18 +159,6 @@ public class ResourceRepresentation {
this.owner = owner;
}
- public List<PolicyRepresentation> getPolicies() {
- return this.policies;
- }
-
- public void setPolicies(List<PolicyRepresentation> policies) {
- this.policies = policies;
- }
-
- <T> T test(Predicate<T> t) {
- return null;
- }
-
public void setTypedScopes(List<ScopeRepresentation> typedScopes) {
this.typedScopes = typedScopes;
}
@@ -178,4 +166,15 @@ public class ResourceRepresentation {
public List<ScopeRepresentation> getTypedScopes() {
return typedScopes;
}
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResourceRepresentation scope = (ResourceRepresentation) o;
+ return Objects.equals(getName(), scope.getName());
+ }
+
+ public int hashCode() {
+ return Objects.hash(getName());
+ }
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PolicyResource.java
index 9a45045..28202f5 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PolicyResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PolicyResource.java
@@ -16,16 +16,21 @@
*/
package org.keycloak.admin.client.resource;
-import org.jboss.resteasy.annotations.cache.NoCache;
-import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -42,4 +47,28 @@ public interface PolicyResource {
@DELETE
void remove();
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> associatedPolicies();
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> dependentPolicies();
+
+ @Path("/scopes")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ScopeRepresentation> scopes();
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ResourceRepresentation> resources();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceResource.java
index 834cb06..28e57a7 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceResource.java
@@ -16,13 +16,17 @@
*/
package org.keycloak.admin.client.resource;
+import java.util.List;
+
import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@@ -42,4 +46,10 @@ public interface ResourceResource {
@DELETE
void remove();
+
+ @Path("permissions")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ List<PolicyRepresentation> permissions();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopeResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopeResource.java
index 6975574..87b285b 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopeResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopeResource.java
@@ -16,13 +16,17 @@
*/
package org.keycloak.admin.client.resource;
+import java.util.List;
+
import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@@ -42,4 +46,9 @@ public interface ResourceScopeResource {
@DELETE
void remove();
+
+ @Path("/permissions")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ List<PolicyRepresentation> permissions();
}
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 b503bce..3914c68 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
@@ -18,6 +18,17 @@
package org.keycloak.models.authorization.infinispan;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
import org.infinispan.Cache;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
@@ -29,17 +40,10 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.stream.Collectors;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -47,41 +51,58 @@ public class CachedPolicyStore implements PolicyStore {
private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
- private final Cache<String, List> cache;
+ private final Cache<String, List<CachedPolicy>> cache;
private final KeycloakSession session;
private final CacheTransaction transaction;
+ private final List<String> cacheKeys;
private StoreFactory storeFactory;
private PolicyStore delegate;
+ private CachedStoreFactoryProvider cachedStoreFactory;
- public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction) {
+ public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+ cacheKeys = new ArrayList<>();
+ cacheKeys.add("findByResource");
+ cacheKeys.add("findByResourceType");
+ cacheKeys.add("findByScopeIds");
+ cacheKeys.add("findByType");
+ this.storeFactory = storeFactory;
}
@Override
public Policy create(String name, String type, ResourceServer resourceServer) {
Policy policy = getDelegate().create(name, type, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
+ String id = policy.getId();
- this.transaction.whenRollback(() -> cache.remove(getCacheKeyForPolicy(policy.getId())));
+ this.transaction.whenCommit(() -> {
+ cache.remove(getCacheKeyForPolicy(id));
+ invalidateCache(resourceServer.getId());
+ });
return createAdapter(new CachedPolicy(policy));
}
@Override
public void delete(String id) {
+ Policy policy = findById(id, null);
+ ResourceServer resourceServer = policy.getResourceServer();
getDelegate().delete(id);
- this.transaction.whenCommit(() -> cache.remove(getCacheKeyForPolicy(id)));
+ this.transaction.whenCommit(() -> {
+ cache.remove(getCacheKeyForPolicy(id));
+ invalidateCache(resourceServer.getId());
+ });
}
@Override
- public Policy findById(String id) {
+ public Policy findById(String id, String resourceServerId) {
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
List<CachedPolicy> cached = this.cache.get(cacheKeyForPolicy);
if (cached == null) {
- Policy policy = getDelegate().findById(id);
+ Policy policy = getDelegate().findById(id, resourceServerId);
if (policy != null) {
return createAdapter(updatePolicyCache(policy));
@@ -100,7 +121,7 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResourceServer(String resourceServerId) {
- return getDelegate().findByResourceServer(resourceServerId).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
+ return getDelegate().findByResourceServer(resourceServerId);
}
@Override
@@ -109,88 +130,49 @@ public class CachedPolicyStore implements PolicyStore {
}
@Override
- public List<Policy> findByResource(String resourceId) {
- List<Policy> cache = new ArrayList<>();
-
- for (Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
-
- if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
- List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
- CachedPolicy policy = value.get(0);
-
- if (policy.getResourcesIds().contains(resourceId)) {
- cache.add(findById(policy.getId()));
- }
- }
- }
-
- if (cache.isEmpty()) {
- getDelegate().findByResource(resourceId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
- }
-
- return cache;
+ public List<Policy> findByResource(String resourceId, String resourceServerId) {
+ return cacheResult(new StringBuilder("findByResource").append(resourceServerId).append(resourceId).toString(), () -> getDelegate().findByResource(resourceId, resourceServerId));
}
@Override
public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
- List<Policy> cache = new ArrayList<>();
-
- for (Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
-
- if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
- List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
- CachedPolicy policy = value.get(0);
-
- if (policy.getResourceServerId().equals(resourceServerId) && policy.getConfig().getOrDefault("defaultResourceType", "").equals(resourceType)) {
- cache.add(findById(policy.getId()));
- }
- }
- }
-
- if (cache.isEmpty()) {
- getDelegate().findByResourceType(resourceType, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
- }
-
- return cache;
+ return cacheResult(new StringBuilder("findByResourceType").append(resourceServerId).append(resourceType).toString(), () -> getDelegate().findByResourceType(resourceType, resourceServerId));
}
@Override
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
- List<Policy> cache = new ArrayList<>();
-
- for (Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
+ List<Policy> policies = new ArrayList<>();
- if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
- List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
- CachedPolicy policy = value.get(0);
-
- for (String scopeId : policy.getScopesIds()) {
- if (scopeIds.contains(scopeId)) {
- cache.add(findById(policy.getId()));
- break;
- }
- }
- }
+ for (String scopeId : scopeIds) {
+ policies.addAll(cacheResult(new StringBuilder("findByScopeIds").append(resourceServerId).append(scopeId).toString(), () -> getDelegate().findByScopeIds(Arrays.asList(scopeId), resourceServerId)));
}
- if (cache.isEmpty()) {
- getDelegate().findByScopeIds(scopeIds, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
- }
+ return policies;
+ }
- return cache;
+ @Override
+ public List<Policy> findByType(String type, String resourceServerId) {
+ return cacheResult(new StringBuilder("findByType").append(resourceServerId).append(type).toString(), () -> getDelegate().findByType(type, resourceServerId));
}
@Override
- public List<Policy> findByType(String type) {
- return getDelegate().findByType(type).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
+ public List<Policy> findDependentPolicies(String id, String resourceServerId) {
+ return getDelegate().findDependentPolicies(id, resourceServerId);
}
@Override
- public List<Policy> findDependentPolicies(String id) {
- return getDelegate().findDependentPolicies(id).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
+ public void notifyChange(Object cached) {
+ String resourceServerId;
+
+ if (Resource.class.isInstance(cached)) {
+ resourceServerId = ((Resource) cached).getResourceServer().getId();
+ } else if (Scope.class.isInstance(cached)){
+ resourceServerId = ((Scope) cached).getResourceServer().getId();
+ } else {
+ throw new RuntimeException("Unexpected notification [" + cached + "]");
+ }
+
+ invalidateCache(resourceServerId);
}
private String getCacheKeyForPolicy(String policyId) {
@@ -198,10 +180,6 @@ public class CachedPolicyStore implements PolicyStore {
}
private StoreFactory getStoreFactory() {
- if (this.storeFactory == null) {
- this.storeFactory = this.session.getProvider(StoreFactory.class);
- }
-
return this.storeFactory;
}
@@ -216,6 +194,9 @@ public class CachedPolicyStore implements PolicyStore {
private Policy createAdapter(CachedPolicy cached) {
return new Policy() {
+ private Set<Scope> scopes;
+ private Set<Resource> resources;
+ private Set<Policy> associatedPolicies;
private Policy updated;
@Override
@@ -285,54 +266,56 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public ResourceServer getResourceServer() {
- return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+ return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
}
@Override
public void addScope(Scope scope) {
- getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId()));
+ getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
cached.addScope(scope);
}
@Override
public void removeScope(Scope scope) {
- getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId()));
+ getDelegateForUpdate().removeScope(scope);
cached.removeScope(scope);
}
@Override
public void addAssociatedPolicy(Policy associatedPolicy) {
- getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
+ getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
cached.addAssociatedPolicy(associatedPolicy);
}
@Override
public void removeAssociatedPolicy(Policy associatedPolicy) {
- getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
+ getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
cached.removeAssociatedPolicy(associatedPolicy);
}
@Override
public void addResource(Resource resource) {
- getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId()));
+ getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
cached.addResource(resource);
}
@Override
public void removeResource(Resource resource) {
- getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId()));
+ getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
cached.removeResource(resource);
}
@Override
public Set<Policy> getAssociatedPolicies() {
- Set<Policy> associatedPolicies = new HashSet<>();
+ if (associatedPolicies == null) {
+ associatedPolicies = new HashSet<>();
- for (String id : cached.getAssociatedPoliciesIds()) {
- Policy cached = findById(id);
+ for (String id : cached.getAssociatedPoliciesIds()) {
+ Policy policy = findById(id, cached.getResourceServerId());
- if (cached != null) {
- associatedPolicies.add(cached);
+ if (policy != null) {
+ associatedPolicies.add(policy);
+ }
}
}
@@ -341,13 +324,15 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public Set<Resource> getResources() {
- Set<Resource> resources = new HashSet<>();
+ if (resources == null) {
+ resources = new HashSet<>();
- for (String id : cached.getResourcesIds()) {
- Resource cached = getStoreFactory().getResourceStore().findById(id);
+ for (String id : cached.getResourcesIds()) {
+ Resource resource = getCachedStoreFactory().getResourceStore().findById(id, cached.getResourceServerId());
- if (cached != null) {
- resources.add(cached);
+ if (resource != null) {
+ resources.add(resource);
+ }
}
}
@@ -356,13 +341,15 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public Set<Scope> getScopes() {
- Set<Scope> scopes = new HashSet<>();
+ if (scopes == null) {
+ scopes = new HashSet<>();
- for (String id : cached.getScopesIds()) {
- Scope cached = getStoreFactory().getScopeStore().findById(id);
+ for (String id : cached.getScopesIds()) {
+ Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId());
- if (cached != null) {
- scopes.add(cached);
+ if (scope != null) {
+ scopes.add(scope);
+ }
}
}
@@ -392,9 +379,12 @@ public class CachedPolicyStore implements PolicyStore {
private Policy getDelegateForUpdate() {
if (this.updated == null) {
- this.updated = getDelegate().findById(getId());
+ this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
if (this.updated == null) throw new IllegalStateException("Not found in database");
- transaction.whenCommit(() -> cache.remove(getCacheKeyForPolicy(getId())));
+ transaction.whenCommit(() -> {
+ cache.remove(getCacheKeyForPolicy(getId()));
+ invalidateCache(cached.getResourceServerId());
+ });
}
return this.updated;
@@ -402,9 +392,16 @@ public class CachedPolicyStore implements PolicyStore {
};
}
+ private CachedStoreFactoryProvider getCachedStoreFactory() {
+ if (cachedStoreFactory == null) {
+ cachedStoreFactory = session.getProvider(CachedStoreFactoryProvider.class);
+ }
+ return cachedStoreFactory;
+ }
+
private CachedPolicy updatePolicyCache(Policy policy) {
CachedPolicy cached = new CachedPolicy(policy);
- List<Policy> cache = new ArrayList<>();
+ List<CachedPolicy> cache = new ArrayList<>();
cache.add(cached);
@@ -413,4 +410,25 @@ public class CachedPolicyStore implements PolicyStore {
return cached;
}
+ private void invalidateCache(String resourceServerId) {
+ cacheKeys.forEach(cacheKey -> cache.keySet().stream().filter(key -> key.startsWith(cacheKey + resourceServerId)).forEach(cache::remove));
+ }
+
+ private List<Policy> cacheResult(String key, Supplier<List<Policy>> provider) {
+ List<CachedPolicy> cached = cache.computeIfAbsent(key, (Function<String, List<CachedPolicy>>) o -> {
+ List<Policy> result = provider.get();
+
+ if (result.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ return result.stream().map(policy -> new CachedPolicy(policy)).collect(Collectors.toList());
+ });
+
+ if (cached == null) {
+ return Collections.emptyList();
+ }
+
+ 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/CachedResourceServerStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
index 2685135..ed98500 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
@@ -18,6 +18,10 @@
package org.keycloak.models.authorization.infinispan;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import org.infinispan.Cache;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.ResourceServerStore;
@@ -28,16 +32,13 @@ import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvid
import org.keycloak.models.authorization.infinispan.entities.CachedResourceServer;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class CachedResourceServerStore implements ResourceServerStore {
private static final String RS_ID_CACHE_PREFIX = "rs-id-";
+ private static final String RS_CLIENT_ID_CACHE_PREFIX = "rs-client-id-";
private final KeycloakSession session;
private final CacheTransaction transaction;
@@ -45,11 +46,12 @@ public class CachedResourceServerStore implements ResourceServerStore {
private ResourceServerStore delegate;
private final Cache<String, List> cache;
- public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction) {
+ public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+ this.storeFactory = storeFactory;
}
@Override
@@ -64,7 +66,14 @@ public class CachedResourceServerStore implements ResourceServerStore {
@Override
public void delete(String id) {
getDelegate().delete(id);
- this.transaction.whenCommit(() -> this.cache.remove(getCacheKeyForResourceServer(id)));
+ this.transaction.whenCommit(() -> {
+ List<CachedResourceServer> servers = cache.remove(getCacheKeyForResourceServer(id));
+
+ if (servers != null) {
+ CachedResourceServer entry = servers.get(0);
+ cache.remove(getCacheKeyForResourceServerClientId(entry.getClientId()));
+ }
+ });
}
@Override
@@ -87,32 +96,31 @@ public class CachedResourceServerStore implements ResourceServerStore {
@Override
public ResourceServer findByClient(String id) {
- for (Map.Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
+ String cacheKeyForResourceServer = getCacheKeyForResourceServerClientId(id);
+ List<String> cached = this.cache.get(cacheKeyForResourceServer);
- if (cacheKey.startsWith(RS_ID_CACHE_PREFIX)) {
- List<ResourceServer> cache = (List<ResourceServer>) entry.getValue();
- ResourceServer resourceServer = cache.get(0);
+ if (cached == null) {
+ ResourceServer resourceServer = getDelegate().findByClient(id);
- if (resourceServer.getClientId().equals(id)) {
- return findById(resourceServer.getId());
- }
+ if (resourceServer != null) {
+ cache.put(cacheKeyForResourceServer, Arrays.asList(resourceServer.getId()));
+ return findById(resourceServer.getId());
}
- }
- ResourceServer resourceServer = getDelegate().findByClient(id);
-
- if (resourceServer != null) {
- return findById(updateResourceServerCache(resourceServer).getId());
+ return null;
}
- return null;
+ return findById(cached.get(0));
}
private String getCacheKeyForResourceServer(String id) {
return RS_ID_CACHE_PREFIX + id;
}
+ private String getCacheKeyForResourceServerClientId(String id) {
+ return RS_CLIENT_ID_CACHE_PREFIX + id;
+ }
+
private ResourceServerStore getDelegate() {
if (this.delegate == null) {
this.delegate = getStoreFactory().getResourceServerStore();
@@ -122,10 +130,6 @@ public class CachedResourceServerStore implements ResourceServerStore {
}
private StoreFactory getStoreFactory() {
- if (this.storeFactory == null) {
- this.storeFactory = session.getProvider(StoreFactory.class);
- }
-
return this.storeFactory;
}
private ResourceServer createAdapter(ResourceServer cached) {
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 8696d8d..6ebac83 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
@@ -18,6 +18,16 @@
package org.keycloak.models.authorization.infinispan;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
import org.infinispan.Cache;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
@@ -28,14 +38,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
import org.keycloak.models.authorization.infinispan.entities.CachedResource;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.stream.Collectors;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -43,60 +46,63 @@ import java.util.stream.Collectors;
public class CachedResourceStore implements ResourceStore {
private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
- private static final String RESOURCE_OWNER_CACHE_PREFIX = "rsc-owner-";
+ private static final String RESOURCE_NAME_CACHE_PREFIX = "rsc-name-";
private final KeycloakSession session;
private final CacheTransaction transaction;
+ private final List<String> cacheKeys;
private StoreFactory storeFactory;
private ResourceStore delegate;
- private final Cache<String, List> cache;
+ private final Cache<String, List<CachedResource>> cache;
- public CachedResourceStore(KeycloakSession session, CacheTransaction transaction) {
+ public CachedResourceStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
this.transaction = transaction;
+ cacheKeys = new ArrayList<>();
+ cacheKeys.add("findByOwner");
+ this.storeFactory = storeFactory;
}
@Override
public Resource create(String name, ResourceServer resourceServer, String owner) {
Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
- this.transaction.whenRollback(() -> cache.remove(getCacheKeyForResource(resource.getId())));
+ this.transaction.whenRollback(() -> {
+ cache.remove(getCacheKeyForResource(resource.getId()));
+ invalidateCache(resourceServer.getId());
+ });
return createAdapter(new CachedResource(resource));
}
@Override
public void delete(String id) {
- List<CachedResource> removed = this.cache.remove(getCacheKeyForResource(id));
-
- if (removed != null) {
- CachedResource cachedResource = removed.get(0);
- List<String> byOwner = this.cache.get(getResourceOwnerCacheKey(cachedResource.getOwner()));
-
- if (byOwner != null) {
- byOwner.remove(id);
+ Resource resource = findById(id, null);
+ ResourceServer resourceServer = resource.getResourceServer();
+ getDelegate().delete(id);
+ this.transaction.whenCommit(() -> {
+ List<CachedResource> resources = cache.remove(getCacheKeyForResource(id));
- if (byOwner.isEmpty()) {
- this.cache.remove(getResourceOwnerCacheKey(cachedResource.getOwner()));
- }
+ if (resources != null) {
+ CachedResource entry = resources.get(0);
+ cache.remove(getCacheKeyForResourceName(entry.getName(), entry.getResourceServerId()));
}
- }
- getDelegate().delete(id);
+ invalidateCache(resourceServer.getId());
+ });
}
@Override
- public Resource findById(String id) {
+ public Resource findById(String id, String resourceServerId) {
String cacheKeyForResource = getCacheKeyForResource(id);
List<CachedResource> cached = this.cache.get(cacheKeyForResource);
if (cached == null) {
- Resource resource = getDelegate().findById(id);
+ Resource resource = getDelegate().findById(id, resourceServerId);
if (resource != null) {
- updateCachedIds(getResourceOwnerCacheKey(resource.getOwner()), resource, false);
return createAdapter(updateResourceCache(resource));
}
@@ -107,20 +113,13 @@ public class CachedResourceStore implements ResourceStore {
}
@Override
- public List<Resource> findByOwner(String ownerId) {
-
- for (Resource resource : getDelegate().findByOwner(ownerId)) {
- updateCachedIds(getResourceOwnerCacheKey(ownerId), resource, true);
- }
-
- return ((List<String>) this.cache.getOrDefault(getResourceOwnerCacheKey(ownerId), Collections.emptyList())).stream().map(this::findById)
- .filter(resource -> resource != null)
- .collect(Collectors.toList());
+ public List<Resource> findByOwner(String ownerId, String resourceServerId) {
+ return cacheResult(new StringBuilder("findByOwner").append(resourceServerId).append(ownerId).toString(), () -> getDelegate().findByOwner(ownerId, resourceServerId));
}
@Override
public List<Resource> findByResourceServer(String resourceServerId) {
- return getDelegate().findByResourceServer(resourceServerId).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
+ return getDelegate().findByResourceServer(resourceServerId);
}
@Override
@@ -129,43 +128,42 @@ public class CachedResourceStore implements ResourceStore {
}
@Override
- public List<Resource> findByScope(String... id) {
- return getDelegate().findByScope(id).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
+ public List<Resource> findByScope(List<String> id, String resourceServerId) {
+ return getDelegate().findByScope(id, resourceServerId);
}
@Override
public Resource findByName(String name, String resourceServerId) {
- for (Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
+ String cacheKeyForResource = getCacheKeyForResourceName(name, resourceServerId);
+ List<CachedResource> cached = this.cache.get(cacheKeyForResource);
- if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
- List<CachedResource> value = (List<CachedResource>) entry.getValue();
- CachedResource resource = value.get(0);
+ if (cached == null) {
+ Resource resource = getDelegate().findByName(name, resourceServerId);
- if (resource.getResourceServerId().equals(resourceServerId) && resource.getName().equals(name)) {
- return findById(resource.getId());
- }
+ if (resource != null) {
+ cache.put(cacheKeyForResource, Arrays.asList(new CachedResource(resource)));
+ return findById(resource.getId(), resourceServerId);
}
- }
-
- Resource resource = getDelegate().findByName(name, resourceServerId);
- if (resource != null) {
- return findById(updateResourceCache(resource).getId());
+ return null;
}
- return null;
+ return createAdapter(cached.get(0));
}
@Override
- public List<Resource> findByType(String type) {
- return getDelegate().findByType(type).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
+ public List<Resource> findByType(String type, String resourceServerId) {
+ return getDelegate().findByType(type, resourceServerId);
}
private String getCacheKeyForResource(String id) {
return RESOURCE_ID_CACHE_PREFIX + id;
}
+ private String getCacheKeyForResourceName(String name, String resourceServerId) {
+ return RESOURCE_NAME_CACHE_PREFIX + name + "-" + resourceServerId;
+ }
+
private ResourceStore getDelegate() {
if (this.delegate == null) {
this.delegate = getStoreFactory().getResourceStore();
@@ -175,10 +173,6 @@ public class CachedResourceStore implements ResourceStore {
}
private StoreFactory getStoreFactory() {
- if (this.storeFactory == null) {
- this.storeFactory = session.getProvider(StoreFactory.class);
- }
-
return this.storeFactory;
}
@@ -228,13 +222,15 @@ public class CachedResourceStore implements ResourceStore {
@Override
public List<Scope> getScopes() {
- List<Scope> scopes = new ArrayList<>();
+ if (scopes == null) {
+ scopes = new ArrayList<>();
- for (String id : cached.getScopesIds()) {
- Scope cached = getStoreFactory().getScopeStore().findById(id);
+ for (String id : cached.getScopesIds()) {
+ Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId());
- if (cached != null) {
- scopes.add(cached);
+ if (scope != null) {
+ scopes.add(scope);
+ }
}
}
@@ -254,7 +250,7 @@ public class CachedResourceStore implements ResourceStore {
@Override
public ResourceServer getResourceServer() {
- return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+ return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
}
@Override
@@ -264,15 +260,19 @@ public class CachedResourceStore implements ResourceStore {
@Override
public void updateScopes(Set<Scope> scopes) {
- getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId())).collect(Collectors.toSet()));
+ getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
cached.updateScopes(scopes);
}
private Resource getDelegateForUpdate() {
if (this.updated == null) {
- this.updated = getDelegate().findById(getId());
+ this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
if (this.updated == null) throw new IllegalStateException("Not found in database");
- transaction.whenCommit(() -> cache.remove(getCacheKeyForResource(getId())));
+ transaction.whenRollback(() -> {
+ cache.remove(getCacheKeyForResource(cached.getId()));
+ invalidateCache(cached.getResourceServerId());
+ getCachedStoreFactory().getPolicyStore().notifyChange(cached);
+ });
}
return this.updated;
@@ -280,6 +280,10 @@ public class CachedResourceStore implements ResourceStore {
};
}
+ private CachedStoreFactoryProvider getCachedStoreFactory() {
+ return session.getProvider(CachedStoreFactoryProvider.class);
+ }
+
private CachedResource updateResourceCache(Resource resource) {
CachedResource cached = new CachedResource(resource);
List cache = new ArrayList<>();
@@ -291,23 +295,25 @@ public class CachedResourceStore implements ResourceStore {
return cached;
}
- private void updateCachedIds(String cacheKey, Resource resource, boolean create) {
- List<String> cached = this.cache.get(cacheKey);
+ private List<Resource> cacheResult(String key, Supplier<List<Resource>> provider) {
+ List<CachedResource> cached = cache.computeIfAbsent(key, (Function<String, List<CachedResource>>) o -> {
+ List<Resource> result = provider.get();
- if (cached == null) {
- if (!create) {
- return;
+ if (result.isEmpty()) {
+ return null;
}
- cached = new ArrayList<>();
- this.cache.put(cacheKey, cached);
- }
- if (cached != null && !cached.contains(resource.getId())) {
- cached.add(resource.getId());
+ return result.stream().map(resource -> new CachedResource(resource)).collect(Collectors.toList());
+ });
+
+ if (cached == null) {
+ return Collections.emptyList();
}
+
+ return cached.stream().map(this::createAdapter).collect(Collectors.toList());
}
- private String getResourceOwnerCacheKey(String ownerId) {
- return RESOURCE_OWNER_CACHE_PREFIX + ownerId;
+ private void invalidateCache(String resourceServerId) {
+ cacheKeys.forEach(cacheKey -> cache.keySet().stream().filter(key -> key.startsWith(cacheKey + resourceServerId)).forEach(cache::remove));
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
index f86a7d1..77a6970 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
@@ -18,6 +18,11 @@
package org.keycloak.models.authorization.infinispan;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
import org.infinispan.Cache;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
@@ -27,11 +32,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
import org.keycloak.models.authorization.infinispan.entities.CachedScope;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -39,6 +40,7 @@ import java.util.Map.Entry;
public class CachedScopeStore implements ScopeStore {
private static final String SCOPE_ID_CACHE_PREFIX = "scp-id-";
+ private static final String SCOPE_NAME_CACHE_PREFIX = "scp-name-";
private final Cache<String, List> cache;
private final KeycloakSession session;
@@ -46,11 +48,12 @@ public class CachedScopeStore implements ScopeStore {
private ScopeStore delegate;
private StoreFactory storeFactory;
- public CachedScopeStore(KeycloakSession session, CacheTransaction transaction) {
+ public CachedScopeStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+ this.storeFactory = storeFactory;
}
@Override
@@ -65,16 +68,23 @@ public class CachedScopeStore implements ScopeStore {
@Override
public void delete(String id) {
getDelegate().delete(id);
- this.transaction.whenCommit(() -> cache.remove(getCacheKeyForScope(id)));
+ this.transaction.whenCommit(() -> {
+ List<CachedScope> scopes = cache.remove(getCacheKeyForScope(id));
+
+ if (scopes != null) {
+ CachedScope entry = scopes.get(0);
+ cache.remove(getCacheKeyForScopeName(entry.getName(), entry.getResourceServerId()));
+ }
+ });
}
@Override
- public Scope findById(String id) {
+ public Scope findById(String id, String resourceServerId) {
String cacheKeyForScope = getCacheKeyForScope(id);
List<CachedScope> cached = this.cache.get(cacheKeyForScope);
if (cached == null) {
- Scope scope = getDelegate().findById(id);
+ Scope scope = getDelegate().findById(id, resourceServerId);
if (scope != null) {
return createAdapter(updateScopeCache(scope));
@@ -88,26 +98,21 @@ public class CachedScopeStore implements ScopeStore {
@Override
public Scope findByName(String name, String resourceServerId) {
- for (Entry entry : this.cache.entrySet()) {
- String cacheKey = (String) entry.getKey();
+ String cacheKeyForScope = getCacheKeyForScopeName(name, resourceServerId);
+ List<String> cached = this.cache.get(cacheKeyForScope);
- if (cacheKey.startsWith(SCOPE_ID_CACHE_PREFIX)) {
- List<CachedScope> cache = (List<CachedScope>) entry.getValue();
- CachedScope scope = cache.get(0);
+ if (cached == null) {
+ Scope scope = getDelegate().findByName(name, resourceServerId);
- if (scope.getResourceServerId().equals(resourceServerId) && scope.getName().equals(name)) {
- return findById(scope.getId());
- }
+ if (scope != null) {
+ cache.put(cacheKeyForScope, Arrays.asList(scope.getId()));
+ return findById(scope.getId(), resourceServerId);
}
- }
- Scope scope = getDelegate().findByName(name, resourceServerId);
-
- if (scope != null) {
- return findById(updateScopeCache(scope).getId());
+ return null;
}
- return null;
+ return findById(cached.get(0), resourceServerId);
}
@Override
@@ -124,6 +129,10 @@ public class CachedScopeStore implements ScopeStore {
return SCOPE_ID_CACHE_PREFIX + id;
}
+ private String getCacheKeyForScopeName(String name, String resourceServerId) {
+ return SCOPE_NAME_CACHE_PREFIX + name + "-" + resourceServerId;
+ }
+
private ScopeStore getDelegate() {
if (this.delegate == null) {
this.delegate = getStoreFactory().getScopeStore();
@@ -133,10 +142,6 @@ public class CachedScopeStore implements ScopeStore {
}
private StoreFactory getStoreFactory() {
- if (this.storeFactory == null) {
- this.storeFactory = session.getProvider(StoreFactory.class);
- }
-
return this.storeFactory;
}
@@ -174,14 +179,17 @@ public class CachedScopeStore implements ScopeStore {
@Override
public ResourceServer getResourceServer() {
- return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+ return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
}
private Scope getDelegateForUpdate() {
if (this.updated == null) {
- this.updated = getDelegate().findById(getId());
+ this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
if (this.updated == null) throw new IllegalStateException("Not found in database");
- transaction.whenCommit(() -> cache.remove(getCacheKeyForScope(getId())));
+ transaction.whenCommit(() -> {
+ cache.remove(getCacheKeyForScope(getId()));
+ getCachedStoreFactory().getPolicyStore().notifyChange(updated);
+ });
}
return this.updated;
@@ -189,6 +197,10 @@ public class CachedScopeStore implements ScopeStore {
};
}
+ private CachedStoreFactoryProvider getCachedStoreFactory() {
+ return session.getProvider(CachedStoreFactoryProvider.class);
+ }
+
private CachedScope updateScopeCache(Scope scope) {
CachedScope cached = new CachedScope(scope);
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
index 56a385f..ff66da6 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
@@ -22,6 +22,7 @@ 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.KeycloakTransaction;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
@@ -36,31 +37,41 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
private final KeycloakSession session;
private final CacheTransaction transaction;
+ private final StoreFactory storeFactory;
+ private final CachedResourceStore resourceStore;
+ private final CachedScopeStore scopeStore;
+ private final CachedPolicyStore policyStore;
+ private ResourceServerStore resourceServerStore;
InfinispanStoreFactoryProvider(KeycloakSession delegate) {
this.session = delegate;
this.transaction = new CacheTransaction();
this.session.getTransactionManager().enlistAfterCompletion(transaction);
+ storeFactory = this.session.getProvider(StoreFactory.class);
+ resourceStore = new CachedResourceStore(this.session, this.transaction, storeFactory);
+ resourceServerStore = new CachedResourceServerStore(this.session, this.transaction, storeFactory);
+ scopeStore = new CachedScopeStore(this.session, this.transaction, storeFactory);
+ policyStore = new CachedPolicyStore(this.session, this.transaction, storeFactory);
}
@Override
public ResourceStore getResourceStore() {
- return new CachedResourceStore(this.session, this.transaction);
+ return resourceStore;
}
@Override
public ResourceServerStore getResourceServerStore() {
- return new CachedResourceServerStore(this.session, this.transaction);
+ return resourceServerStore;
}
@Override
public ScopeStore getScopeStore() {
- return new CachedScopeStore(this.session, this.transaction);
+ return scopeStore;
}
@Override
public PolicyStore getPolicyStore() {
- return new CachedPolicyStore(this.session, this.transaction);
+ return policyStore;
}
@Override
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 e1ba326..648b1b3 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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -79,19 +80,19 @@ public class PolicyEntity implements Policy {
@CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") })
private Map<String, String> 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<PolicyEntity> 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<ResourceEntity> 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<ScopeEntity> 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 7cb1a6f..29b5740 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 99f8b41..9f2c3b8 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/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
index b57cd1e..544018d 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -19,8 +19,10 @@ package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -68,17 +70,30 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public void delete(String id) {
- Policy policy = findById(id);
+ Policy policy = entityManager.find(PolicyEntity.class, id);
if (policy != null) {
- getEntityManager().remove(policy);
+ this.entityManager.remove(policy);
}
}
@Override
- public Policy findById(String id) {
- return getEntityManager().find(PolicyEntity.class, id);
+ public Policy findById(String id, String resourceServerId) {
+ if (id == null) {
+ return null;
+ }
+
+ if (resourceServerId == null) {
+ return entityManager.find(PolicyEntity.class, id);
+ }
+
+ Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId and id = :id");
+
+ query.setParameter("serverId", resourceServerId);
+ query.setParameter("id", id);
+
+ return entityManager.find(PolicyEntity.class, id);
}
@Override
@@ -142,32 +157,23 @@ public class JPAPolicyStore implements PolicyStore {
}
@Override
- public List<Policy> findByResource(final String resourceId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where r.id = :resourceId");
+ public List<Policy> findByResource(final String resourceId, String resourceServerId) {
+ Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
query.setParameter("resourceId", resourceId);
+ query.setParameter("serverId", resourceServerId);
return query.getResultList();
}
@Override
public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
- List<Policy> policies = new ArrayList<>();
- Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
+ Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c = :type");
query.setParameter("serverId", resourceServerId);
+ query.setParameter("type", resourceType);
- List<Policy> models = query.getResultList();
-
- for (Policy policy : models) {
- String defaultType = policy.getConfig().get("defaultResourceType");
-
- if (defaultType != null && defaultType.equals(resourceType) && policy.getResources().isEmpty()) {
- policies.add(policy);
- }
- }
-
- return policies;
+ return query.getResultList();
}
@Override
@@ -177,7 +183,7 @@ public class JPAPolicyStore implements PolicyStore {
}
// Use separate subquery to handle DB2 and MSSSQL
- Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.id IN (select p.id from PolicyEntity p inner join p.scopes s where p.resourceServer.id = :serverId and s.id in (:scopeIds) and p.resources is empty group by p.id) order by pe.name");
+ Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
query.setParameter("serverId", resourceServerId);
query.setParameter("scopeIds", scopeIds);
@@ -186,19 +192,21 @@ public class JPAPolicyStore implements PolicyStore {
}
@Override
- public List<Policy> findByType(String type) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.type = :type");
+ public List<Policy> findByType(String type, String resourceServerId) {
+ Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
+ query.setParameter("serverId", resourceServerId);
query.setParameter("type", type);
return query.getResultList();
}
@Override
- public List<Policy> findDependentPolicies(String policyId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where ap.id in (:policyId)");
+ public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
+ Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
- query.setParameter("policyId", Arrays.asList(policyId));
+ query.setParameter("serverId", resourceServerId);
+ query.setParameter("policyId", policyId);
return query.getResultList();
}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index 6d00bb6..9f5346d 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -66,7 +66,7 @@ public class JPAResourceStore implements ResourceStore {
@Override
public void delete(String id) {
- Resource resource = findById(id);
+ Resource resource = entityManager.find(ResourceEntity.class, id);
resource.getScopes().clear();
@@ -76,19 +76,29 @@ public class JPAResourceStore implements ResourceStore {
}
@Override
- public Resource findById(String id) {
+ public Resource findById(String id, String resourceServerId) {
if (id == null) {
return null;
}
+ if (resourceServerId == null) {
+ return entityManager.find(ResourceEntity.class, id);
+ }
+
+ Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and id = :id");
+
+ query.setParameter("serverId", resourceServerId);
+ query.setParameter("id", id);
+
return entityManager.find(ResourceEntity.class, id);
}
@Override
- public List<Resource> findByOwner(String ownerId) {
- Query query = entityManager.createQuery("from ResourceEntity where owner = :ownerId");
+ public List<Resource> findByOwner(String ownerId, String resourceServerId) {
+ Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and owner = :ownerId");
query.setParameter("ownerId", ownerId);
+ query.setParameter("serverId", resourceServerId);
return query.getResultList();
}
@@ -112,7 +122,9 @@ public class JPAResourceStore implements ResourceStore {
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
attributes.forEach((name, value) -> {
- if ("scope".equals(name)) {
+ if ("id".equals(name)) {
+ predicates.add(root.get(name).in(value));
+ } else if ("scope".equals(name)) {
predicates.add(root.join("scopes").get("id").in(value));
} else {
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
@@ -134,10 +146,11 @@ public class JPAResourceStore implements ResourceStore {
}
@Override
- public List<Resource> findByScope(String... id) {
- Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
+ public List<Resource> findByScope(List<String> id, String resourceServerId) {
+ Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))");
- query.setParameter("scopeIds", Arrays.asList(id));
+ query.setParameter("scopeIds", id);
+ query.setParameter("serverId", resourceServerId);
return query.getResultList();
}
@@ -159,10 +172,11 @@ public class JPAResourceStore implements ResourceStore {
}
@Override
- public List<Resource> findByType(String type) {
- Query query = entityManager.createQuery("from ResourceEntity where type = :type");
+ public List<Resource> findByType(String type, String resourceServerId) {
+ Query query = entityManager.createQuery("from ResourceEntity r where r.resourceServer.id = :serverId and type = :type");
query.setParameter("type", type);
+ query.setParameter("serverId", resourceServerId);
return query.getResultList();
}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
index d468314..30869d3 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
@@ -17,12 +17,9 @@
*/
package org.keycloak.authorization.jpa.store;
-import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
-import org.keycloak.authorization.jpa.entities.ScopeEntity;
-import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
-import org.keycloak.authorization.store.ScopeStore;
-import org.keycloak.models.utils.KeycloakModelUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
@@ -31,9 +28,13 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.jpa.entities.ScopeEntity;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -61,12 +62,30 @@ public class JPAScopeStore implements ScopeStore {
@Override
public void delete(String id) {
- this.entityManager.remove(findById(id));
+ Scope scope = entityManager.find(ScopeEntity.class, id);
+
+ if (scope != null) {
+ this.entityManager.remove(scope);
+ }
}
@Override
- public Scope findById(String id) {
+ public Scope findById(String id, String resourceServerId) {
+ if (id == null) {
+ return null;
+ }
+
+ if (true) {
+ return entityManager.find(ScopeEntity.class, id);
+ }
+
+ Query query = entityManager.createQuery("from ScopeEntity where resourceServer.id = :serverId and id = :id");
+
+ query.setParameter("serverId", resourceServerId);
+ query.setParameter("id", id);
+
return entityManager.find(ScopeEntity.class, id);
+
}
@Override
@@ -74,8 +93,8 @@ public class JPAScopeStore implements ScopeStore {
try {
Query query = entityManager.createQuery("select s from ScopeEntity s inner join s.resourceServer rs where rs.id = :resourceServerId and name = :name");
- query.setParameter("name", name);
query.setParameter("resourceServerId", resourceServerId);
+ query.setParameter("name", name);
return (Scope) query.getSingleResult();
} catch (NoResultException nre) {
@@ -102,7 +121,11 @@ public class JPAScopeStore implements ScopeStore {
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
attributes.forEach((name, value) -> {
- predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
+ if ("id".equals(name)) {
+ predicates.add(root.get(name).in(value));
+ } else {
+ predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
+ }
});
querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("name")));
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java
index 2b28f16..928bba9 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java
@@ -124,21 +124,21 @@ public class PolicyAdapter extends AbstractMongoAdapter<PolicyEntity> implements
@Override
public Set<Policy> getAssociatedPolicies() {
return getMongoEntity().getAssociatedPolicies().stream()
- .map((Function<String, Policy>) id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id))
+ .map((Function<String, Policy>) id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, getMongoEntity().getResourceServerId()))
.collect(Collectors.toSet());
}
@Override
public Set<Resource> getResources() {
return getMongoEntity().getResources().stream()
- .map((Function<String, Resource>) id -> authorizationProvider.getStoreFactory().getResourceStore().findById(id))
+ .map((Function<String, Resource>) id -> authorizationProvider.getStoreFactory().getResourceStore().findById(id, getMongoEntity().getResourceServerId()))
.collect(Collectors.toSet());
}
@Override
public Set<Scope> getScopes() {
return getMongoEntity().getScopes().stream()
- .map((Function<String, Scope>) id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
+ .map((Function<String, Scope>) id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id, getMongoEntity().getResourceServerId()))
.collect(Collectors.toSet());
}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java
index 7c67f6e..8138a24 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java
@@ -68,7 +68,7 @@ public class ResourceAdapter extends AbstractMongoAdapter<ResourceEntity> implem
@Override
public List<Scope> getScopes() {
return getMongoEntity().getScopes().stream()
- .map(id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
+ .map(id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id, getResourceServer().getId()))
.collect(toList());
}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
index 04a3d9a..c7227f6 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
@@ -70,7 +70,7 @@ public class MongoPolicyStore implements PolicyStore {
}
@Override
- public Policy findById(String id) {
+ public Policy findById(String id, String resourceServerId) {
PolicyEntity entity = getMongoStore().loadEntity(PolicyEntity.class, id, getInvocationContext());
if (entity == null) {
@@ -89,7 +89,7 @@ public class MongoPolicyStore implements PolicyStore {
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId)).findFirst().orElse(null);
}
@Override
@@ -99,7 +99,7 @@ public class MongoPolicyStore implements PolicyStore {
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@@ -125,17 +125,18 @@ public class MongoPolicyStore implements PolicyStore {
DBObject sort = new BasicDBObject("name", 1);
return getMongoStore().loadEntities(PolicyEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
- .map(policy -> findById(policy.getId())).collect(toList());
+ .map(policy -> findById(policy.getId(), resourceServerId)).collect(toList());
}
@Override
- public List<Policy> findByResource(String resourceId) {
+ public List<Policy> findByResource(String resourceId, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("resources").is(resourceId)
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@@ -150,7 +151,7 @@ public class MongoPolicyStore implements PolicyStore {
String defaultResourceType = policyEntity.getConfig().get("defaultResourceType");
return defaultResourceType != null && defaultResourceType.equals(resourceType);
})
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@@ -162,29 +163,31 @@ public class MongoPolicyStore implements PolicyStore {
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@Override
- public List<Policy> findByType(String type) {
+ public List<Policy> findByType(String type, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("type").is(type)
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@Override
- public List<Policy> findDependentPolicies(String policyId) {
+ public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("associatedPolicies").is(policyId)
.get();
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
index a85de72..79f6a9e 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
@@ -70,7 +70,7 @@ public class MongoResourceStore implements ResourceStore {
}
@Override
- public Resource findById(String id) {
+ public Resource findById(String id, String resourceServerId) {
ResourceEntity entity = getMongoStore().loadEntity(ResourceEntity.class, id, getInvocationContext());
if (entity == null) {
@@ -81,13 +81,14 @@ public class MongoResourceStore implements ResourceStore {
}
@Override
- public List<Resource> findByOwner(String ownerId) {
+ public List<Resource> findByOwner(String ownerId, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("owner").is(ownerId)
.get();
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
- .map(scope -> findById(scope.getId())).collect(toList());
+ .map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
}
@Override
@@ -97,7 +98,7 @@ public class MongoResourceStore implements ResourceStore {
.get();
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
- .map(scope -> findById(scope.getId())).collect(toList());
+ .map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
}
@Override
@@ -116,39 +117,41 @@ public class MongoResourceStore implements ResourceStore {
DBObject sort = new BasicDBObject("name", 1);
return getMongoStore().loadEntities(ResourceEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
- .map(scope -> findById(scope.getId())).collect(toList());
+ .map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
}
@Override
- public List<Resource> findByScope(String... id) {
+ public List<Resource> findByScope(List<String> id, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("scopes").in(id)
.get();
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
@Override
public Resource findByName(String name, String resourceServerId) {
DBObject query = new QueryBuilder()
- .and("name").is(name)
.and("resourceServerId").is(resourceServerId)
+ .and("name").is(name)
.get();
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId)).findFirst().orElse(null);
}
@Override
- public List<Resource> findByType(String type) {
+ public List<Resource> findByType(String type, String resourceServerId) {
DBObject query = new QueryBuilder()
+ .and("resourceServerId").is(resourceServerId)
.and("type").is(type)
.get();
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
.collect(toList());
}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
index 4b7edd6..04decb2 100644
--- a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
@@ -69,7 +69,7 @@ public class MongoScopeStore implements ScopeStore {
}
@Override
- public Scope findById(String id) {
+ public Scope findById(String id, String resourceServerId) {
ScopeEntity entity = getMongoStore().loadEntity(ScopeEntity.class, id, getInvocationContext());
if (entity == null) {
@@ -87,7 +87,7 @@ public class MongoScopeStore implements ScopeStore {
.get();
return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
- .map(scope -> findById(scope.getId())).findFirst().orElse(null);
+ .map(scope -> findById(scope.getId(), scope.getResourceServerId())).findFirst().orElse(null);
}
@Override
@@ -97,7 +97,7 @@ public class MongoScopeStore implements ScopeStore {
.get();
return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
- .map(policyEntity -> findById(policyEntity.getId()))
+ .map(scope -> findById(scope.getId(), scope.getResourceServerId()))
.collect(toList());
}
@@ -113,7 +113,7 @@ public class MongoScopeStore implements ScopeStore {
DBObject sort = new BasicDBObject("name", 1);
return getMongoStore().loadEntities(ScopeEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
- .map(scope -> findById(scope.getId())).collect(toList());
+ .map(scope -> findById(scope.getId(), scope.getResourceServerId())).collect(toList());
}
private MongoStoreInvocationContext getInvocationContext() {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index fb7c91b..3aafae3 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -18,6 +18,12 @@
package org.keycloak.authorization;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
import org.keycloak.authorization.permission.evaluator.Evaluators;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -26,11 +32,6 @@ import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.Provider;
-import org.keycloak.provider.ProviderFactory;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
/**
* <p>The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances. Usually
@@ -62,22 +63,22 @@ public final class AuthorizationProvider implements Provider {
private final DefaultPolicyEvaluator policyEvaluator;
private final Executor scheduller;
- private final StoreFactory storeFactory;
- private final List<PolicyProviderFactory> policyProviderFactories;
+ private final Supplier<StoreFactory> storeFactory;
+ private final Map<String, PolicyProviderFactory> policyProviderFactories;
private final KeycloakSession keycloakSession;
private final RealmModel realm;
- public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Executor scheduller) {
+ public AuthorizationProvider(KeycloakSession session, RealmModel realm, Supplier<StoreFactory> storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories, Executor scheduller) {
this.keycloakSession = session;
this.realm = realm;
this.storeFactory = storeFactory;
this.scheduller = scheduller;
- this.policyProviderFactories = configurePolicyProviderFactories(session);
- this.policyEvaluator = new DefaultPolicyEvaluator(this, this.policyProviderFactories);
+ this.policyProviderFactories = policyProviderFactories;
+ this.policyEvaluator = new DefaultPolicyEvaluator(this);
}
- public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory) {
- this(session, realm, storeFactory, Runnable::run);
+ public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
+ this(session, realm, () -> storeFactory, policyProviderFactories, Runnable::run);
}
/**
@@ -87,7 +88,7 @@ public final class AuthorizationProvider implements Provider {
* @return a {@link Evaluators} instance
*/
public Evaluators evaluators() {
- return new Evaluators(this.policyProviderFactories, this.policyEvaluator, this.scheduller);
+ return new Evaluators(this.policyEvaluator, this.scheduller);
}
/**
@@ -96,7 +97,7 @@ public final class AuthorizationProvider implements Provider {
* @return the {@link StoreFactory}
*/
public StoreFactory getStoreFactory() {
- return this.storeFactory;
+ return this.storeFactory.get();
}
/**
@@ -104,8 +105,8 @@ public final class AuthorizationProvider implements Provider {
*
* @return a {@link List} containing all registered {@link PolicyProviderFactory}
*/
- public List<PolicyProviderFactory> getProviderFactories() {
- return this.policyProviderFactories;
+ public Collection<PolicyProviderFactory> getProviderFactories() {
+ return this.policyProviderFactories.values();
}
/**
@@ -116,7 +117,24 @@ public final class AuthorizationProvider implements Provider {
* @return a {@link PolicyProviderFactory} with the given <code>type</code>
*/
public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
- return (F) getProviderFactories().stream().filter(policyProviderFactory -> policyProviderFactory.getId().equals(type)).findFirst().orElse(null);
+ return (F) policyProviderFactories.get(type);
+ }
+
+ /**
+ * 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 PolicyProvider} with the given <code>type</code>
+ */
+ public <P extends PolicyProvider> P getProvider(String type) {
+ PolicyProviderFactory policyProviderFactory = policyProviderFactories.get(type);
+
+ if (policyProviderFactory == null) {
+ return null;
+ }
+
+ return (P) policyProviderFactory.create(this);
}
public KeycloakSession getKeycloakSession() {
@@ -127,16 +145,6 @@ public final class AuthorizationProvider implements Provider {
return realm;
}
- private List<PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSession session) {
- List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class);
-
- if (providerFactories.isEmpty()) {
- throw new RuntimeException("Could not find any policy provider.");
- }
-
- return providerFactories.stream().map(providerFactory -> (PolicyProviderFactory) providerFactory).collect(Collectors.toList());
- }
-
@Override
public void close() {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
index e26ad1c..ed1aa89 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
@@ -18,13 +18,12 @@
package org.keycloak.authorization.permission.evaluator;
+import java.util.List;
+import java.util.concurrent.Executor;
+
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
-import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
-
-import java.util.List;
-import java.util.concurrent.Executor;
/**
* A factory for the different {@link PermissionEvaluator} implementations.
@@ -33,12 +32,10 @@ import java.util.concurrent.Executor;
*/
public final class Evaluators {
- private final List<PolicyProviderFactory> policyProviderFactories;
private final DefaultPolicyEvaluator policyEvaluator;
private final Executor scheduler;
- public Evaluators(List<PolicyProviderFactory> policyProviderFactories, DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
- this.policyProviderFactories = policyProviderFactories;
+ public Evaluators(DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
this.policyEvaluator = policyEvaluator;
this.scheduler = scheduler;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java
index 13e08e4..4630507 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java
@@ -17,11 +17,10 @@
*/
package org.keycloak.authorization.permission.evaluator;
-import org.keycloak.authorization.Decision;
-
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import org.keycloak.authorization.Decision;
+
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
* @see PermissionEvaluator
@@ -38,6 +37,6 @@ class ScheduledPermissionEvaluator implements PermissionEvaluator {
@Override
public void evaluate(Decision decision) {
- CompletableFuture.runAsync(() -> publisher.evaluate(decision), scheduler);
+ publisher.evaluate(decision);
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
index abd3f93..2866360 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
@@ -37,8 +37,10 @@ public abstract class DecisionResultCollector implements Decision<DefaultEvaluat
@Override
public void onDecision(DefaultEvaluation evaluation) {
- if (evaluation.getParentPolicy() != null) {
- results.computeIfAbsent(evaluation.getPermission(), Result::new).policy(evaluation.getParentPolicy()).policy(evaluation.getPolicy()).setStatus(evaluation.getEffect());
+ Policy parentPolicy = evaluation.getParentPolicy();
+
+ if (parentPolicy != null) {
+ results.computeIfAbsent(evaluation.getPermission(), Result::new).policy(parentPolicy).policy(evaluation.getPolicy()).setStatus(evaluation.getEffect());
} else {
results.computeIfAbsent(evaluation.getPermission(), Result::new).setStatus(evaluation.getEffect());
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java
index 0bd5b6c..74a2e07 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java
@@ -18,6 +18,7 @@
package org.keycloak.authorization.policy.evaluation;
+import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.Decision;
import org.keycloak.authorization.Decision.Effect;
import org.keycloak.authorization.model.Policy;
@@ -34,37 +35,29 @@ public class DefaultEvaluation implements Evaluation {
private final Decision decision;
private final Policy policy;
private final Policy parentPolicy;
+ private final AuthorizationProvider authorizationProvider;
private Effect effect;
- public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision) {
+ public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision, AuthorizationProvider authorizationProvider) {
this.permission = permission;
this.executionContext = executionContext;
this.parentPolicy = parentPolicy;
this.policy = policy;
this.decision = decision;
+ this.authorizationProvider = authorizationProvider;
}
- /**
- * Returns the {@link ResourcePermission} to be evaluated.
- *
- * @return the permission to be evaluated
- */
+ @Override
public ResourcePermission getPermission() {
return this.permission;
}
- /**
- * Returns the {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator}. Which provides access to the whole evaluation runtime context.
- *
- * @return the evaluation context
- */
+ @Override
public EvaluationContext getContext() {
return this.executionContext;
}
- /**
- * Grants all the requested permissions to the caller.
- */
+ @Override
public void grant() {
if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
this.effect = Effect.DENY;
@@ -75,6 +68,7 @@ public class DefaultEvaluation implements Evaluation {
this.decision.onDecision(this);
}
+ @Override
public void deny() {
if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
this.effect = Effect.PERMIT;
@@ -85,10 +79,16 @@ public class DefaultEvaluation implements Evaluation {
this.decision.onDecision(this);
}
+ @Override
public Policy getPolicy() {
return this.policy;
}
+ @Override
+ public AuthorizationProvider getAuthorizationProvider() {
+ return authorizationProvider;
+ }
+
public Policy getParentPolicy() {
return this.parentPolicy;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index 207c89e..b74f845 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -18,6 +18,14 @@
package org.keycloak.authorization.policy.evaluation;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.Decision;
import org.keycloak.authorization.model.Policy;
@@ -26,105 +34,95 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.provider.PolicyProvider;
-import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class DefaultPolicyEvaluator implements PolicyEvaluator {
private final AuthorizationProvider authorization;
- private Map<String, PolicyProviderFactory> policyProviders = new HashMap<>();
+ private final StoreFactory storeFactory;
+ private final PolicyStore policyStore;
- public DefaultPolicyEvaluator(AuthorizationProvider authorization, List<PolicyProviderFactory> policyProviderFactories) {
+ public DefaultPolicyEvaluator(AuthorizationProvider authorization) {
this.authorization = authorization;
-
- for (PolicyProviderFactory providerFactory : policyProviderFactories) {
- this.policyProviders.put(providerFactory.getId(), providerFactory);
- }
+ storeFactory = this.authorization.getStoreFactory();
+ policyStore = storeFactory.getPolicyStore();
}
@Override
public void evaluate(ResourcePermission permission, EvaluationContext executionContext, Decision decision) {
ResourceServer resourceServer = permission.getResourceServer();
+ PolicyEnforcementMode enforcementMode = resourceServer.getPolicyEnforcementMode();
- if (PolicyEnforcementMode.DISABLED.equals(resourceServer.getPolicyEnforcementMode())) {
+ if (PolicyEnforcementMode.DISABLED.equals(enforcementMode)) {
createEvaluation(permission, executionContext, decision, null, null).grant();
return;
}
- StoreFactory storeFactory = this.authorization.getStoreFactory();
- PolicyStore policyStore = storeFactory.getPolicyStore();
- AtomicInteger policiesCount = new AtomicInteger(0);
- Consumer<Policy> consumer = createDecisionConsumer(permission, executionContext, decision, policiesCount);
+ AtomicBoolean verified = new AtomicBoolean(false);
+ Consumer<Policy> consumer = createDecisionConsumer(permission, executionContext, decision, verified);
Resource resource = permission.getResource();
+ List<Scope> scopes = permission.getScopes();
if (resource != null) {
- List<? extends Policy> resourcePolicies = policyStore.findByResource(resource.getId());
-
- if (!resourcePolicies.isEmpty()) {
- resourcePolicies.forEach(consumer);
- }
+ evaluatePolicies(() -> policyStore.findByResource(resource.getId(), resourceServer.getId()), consumer);
if (resource.getType() != null) {
- policyStore.findByResourceType(resource.getType(), resourceServer.getId()).forEach(consumer);
+ evaluatePolicies(() -> policyStore.findByResourceType(resource.getType(), resourceServer.getId()), consumer);
}
- if (permission.getScopes().isEmpty() && !resource.getScopes().isEmpty()) {
- policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
+ if (scopes.isEmpty() && !resource.getScopes().isEmpty()) {
+ scopes.removeAll(resource.getScopes());
+ evaluatePolicies(() -> policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
}
}
- if (!permission.getScopes().isEmpty()) {
- policyStore.findByScopeIds(permission.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
+ if (!scopes.isEmpty()) {
+ evaluatePolicies(() -> policyStore.findByScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
}
- if (PolicyEnforcementMode.PERMISSIVE.equals(resourceServer.getPolicyEnforcementMode()) && policiesCount.get() == 0) {
+ if (PolicyEnforcementMode.PERMISSIVE.equals(enforcementMode) && verified.get()) {
createEvaluation(permission, executionContext, decision, null, null).grant();
}
}
- private Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicInteger policiesCount) {
- return (parentPolicy) -> {
- if (hasRequestedScopes(permission, parentPolicy)) {
- for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
- PolicyProviderFactory providerFactory = policyProviders.get(associatedPolicy.getType());
+ private void evaluatePolicies(Supplier<List<Policy>> supplier, Consumer<Policy> consumer) {
+ List<Policy> policies = supplier.get();
- if (providerFactory == null) {
- throw new RuntimeException("Could not find a policy provider for policy type [" + associatedPolicy.getType() + "].");
- }
+ if (!policies.isEmpty()) {
+ policies.forEach(consumer);
+ }
+ }
- PolicyProvider policyProvider = providerFactory.create(associatedPolicy, this.authorization);
+ private Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicBoolean verified) {
+ return (parentPolicy) -> {
+ if (!hasRequestedScopes(permission, parentPolicy)) {
+ return;
+ }
- if (policyProvider == null) {
- throw new RuntimeException("Unknown parentPolicy provider for type [" + associatedPolicy.getType() + "].");
- }
+ for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
+ PolicyProvider policyProvider = authorization.getProvider(associatedPolicy.getType());
- DefaultEvaluation evaluation = createEvaluation(permission, executionContext, decision, parentPolicy, associatedPolicy);
+ if (policyProvider == null) {
+ throw new RuntimeException("Unknown parentPolicy provider for type [" + associatedPolicy.getType() + "].");
+ }
- policyProvider.evaluate(evaluation);
- evaluation.denyIfNoEffect();
+ DefaultEvaluation evaluation = createEvaluation(permission, executionContext, decision, parentPolicy, associatedPolicy);
- policiesCount.incrementAndGet();
- }
+ policyProvider.evaluate(evaluation);
+ evaluation.denyIfNoEffect();
}
+
+ verified.compareAndSet(false, true);
};
}
private DefaultEvaluation createEvaluation(ResourcePermission permission, EvaluationContext executionContext, Decision decision, Policy parentPolicy, Policy associatedPolicy) {
- return new DefaultEvaluation(permission, executionContext, parentPolicy, associatedPolicy, decision);
+ return new DefaultEvaluation(permission, executionContext, parentPolicy, associatedPolicy, decision, authorization);
}
private boolean hasRequestedScopes(final ResourcePermission permission, final Policy policy) {
@@ -136,7 +134,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
Set<Resource> policyResources = policy.getResources();
if (resourcePermission != null && !policyResources.isEmpty()) {
- if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
+ if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
return false;
}
}
@@ -161,7 +159,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
String type = resource.getType();
if (type != null) {
- List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type);
+ List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type, resource.getResourceServer().getId());
for (Resource resourceType : resourcesByType) {
if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java
index f5b0868..4ac0264 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java
@@ -18,6 +18,8 @@
package org.keycloak.authorization.policy.evaluation;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.permission.ResourcePermission;
/**
@@ -43,6 +45,15 @@ public interface Evaluation {
EvaluationContext getContext();
/**
+ * Returns the {@link Policy}. being evaluated.
+ *
+ * @return the evaluation context
+ */
+ Policy getPolicy();
+
+ AuthorizationProvider getAuthorizationProvider();
+
+ /**
* Grants the requested permission to the caller.
*/
void grant();
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
index f82bdb7..f7041b5 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
@@ -32,7 +32,7 @@ public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
String getGroup();
- PolicyProvider create(Policy policy, AuthorizationProvider authorization);
+ PolicyProvider create(AuthorizationProvider authorization);
PolicyProviderAdminService getAdminResource(ResourceServer resourceServer);
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
index 51c7dd7..626e317 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
@@ -53,9 +53,10 @@ public interface PolicyStore {
* Returns a {@link Policy} with the given <code>id</code>
*
* @param id the identifier of the policy
+ * @param resourceServerId the resource server id
* @return a policy with the given identifier.
*/
- Policy findById(String id);
+ Policy findById(String id, String resourceServerId);
/**
* Returns a {@link Policy} with the given <code>name</code>
@@ -87,9 +88,10 @@ public interface PolicyStore {
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
*
* @param resourceId the identifier of a resource
+ * @param resourceServerId the resource server id
* @return a list of policies associated with the given resource
*/
- List<Policy> findByResource(String resourceId);
+ List<Policy> findByResource(String resourceId, String resourceServerId);
/**
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>type</code>.
@@ -113,15 +115,26 @@ public interface PolicyStore {
* Returns a list of {@link Policy} with the given <code>type</code>.
*
* @param type the type of the policy
+ * @param resourceServerId the resource server id
* @return a list of policies with the given type
*/
- List<Policy> findByType(String type);
+ List<Policy> findByType(String type, String resourceServerId);
/**
* Returns a list of {@link Policy} that depends on another policy with the given <code>id</code>.
*
* @param id the id of the policy to query its dependents
+ * @param resourceServerId the resource server id
* @return a list of policies that depends on the a policy with the given identifier
*/
- List<Policy> findDependentPolicies(String id);
+ List<Policy> findDependentPolicies(String id, String resourceServerId);
+
+ /**
+ * Notify this store about changes to data associated with policies. E.g.: resources and scopes..
+ *
+ * TODO: need a better strategy to handle cross-references between stores, specially in cases where the store is caching data. Use some event-based solution here.
+ *
+ * @param cached
+ */
+ default void notifyChange(Object cached) {}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
index f06be78..e897664 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
@@ -53,7 +53,7 @@ public interface ResourceStore {
* @param id the identifier of an existing resource instance
* @return the resource instance with the given identifier or null if no instance was found
*/
- Resource findById(String id);
+ Resource findById(String id, String resourceServerId);
/**
* Finds all {@link Resource} instances with the given {@code ownerId}.
@@ -61,7 +61,7 @@ public interface ResourceStore {
* @param ownerId the identifier of the owner
* @return a list with all resource instances owned by the given owner
*/
- List<Resource> findByOwner(String ownerId);
+ List<Resource> findByOwner(String ownerId, String resourceServerId);
/**
* Finds all {@link Resource} instances associated with a given resource server.
@@ -86,7 +86,7 @@ public interface ResourceStore {
* @param id one or more scope identifiers
* @return a list of resources associated with the given scope(s)
*/
- List<Resource> findByScope(String... id);
+ List<Resource> findByScope(List<String> id, String resourceServerId);
/**
* Find a {@link Resource} by its name.
@@ -103,5 +103,5 @@ public interface ResourceStore {
* @param type the type of the resource
* @return a list of resources with the given type
*/
- List<Resource> findByType(String type);
+ List<Resource> findByType(String type, String resourceServerId);
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
index 81a7064..fa9e70d 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
@@ -53,17 +53,17 @@ public interface ScopeStore {
* Returns a {@link Scope} with the given <code>id</code>
*
* @param id the identifier of the scope
- *
+ * @param resourceServerId the resource server id
* @return a scope with the given identifier.
*/
- Scope findById(String id);
+ Scope findById(String id, String resourceServerId);
/**
* Returns a {@link Scope} with the given <code>name</code>
*
* @param name the name of the scope
*
- * @param resourceServerId
+ * @param resourceServerId the resource server id
* @return a scope with the given name.
*/
Scope findByName(String name, String resourceServerId);
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
index 4f0ef32..ed24c53 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
@@ -36,7 +36,7 @@ public class RealmSynchronizer implements Synchronizer<RealmRemovedEvent> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
event.getRealm().getClients().forEach(clientModel -> {
- ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getClientId());
+ ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getId());
if (resourceServer != null) {
String id = resourceServer.getId();
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
index 01830ff..03a2cda 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
@@ -17,11 +17,16 @@
package org.keycloak.authorization.store.syncronization;
+import java.util.function.Consumer;
+
import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.UserRemovedEvent;
import org.keycloak.provider.ProviderFactory;
@@ -39,17 +44,25 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
UserModel userModel = event.getUser();
ResourceStore resourceStore = storeFactory.getResourceStore();
PolicyStore policyStore = storeFactory.getPolicyStore();
+ ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+ RealmModel realm = event.getRealm();
+
+ realm.getClients().forEach(clientModel -> {
+ ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
- resourceStore.findByOwner(userModel.getId()).forEach(resource -> {
- String resourceId = resource.getId();
- policyStore.findByResource(resourceId).forEach(policy -> {
- if (policy.getResources().size() == 1) {
- policyStore.delete(policy.getId());
- } else {
- policy.removeResource(resource);
- }
- });
- resourceStore.delete(resourceId);
+ if (resourceServer != null) {
+ resourceStore.findByOwner(userModel.getId(), resourceServer.getId()).forEach(resource -> {
+ String resourceId = resource.getId();
+ policyStore.findByResource(resourceId, resourceServer.getId()).forEach(policy -> {
+ if (policy.getResources().size() == 1) {
+ policyStore.delete(policy.getId());
+ } else {
+ policy.removeResource(resource);
+ }
+ });
+ resourceStore.delete(resourceId);
+ });
+ }
});
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
index 995dafb..f8844f1 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
@@ -70,7 +70,7 @@ public class MigrateTo2_1_0 implements Migration {
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getId());
if (resourceServer != null) {
- policyStore.findByType("role").forEach(policy -> {
+ policyStore.findByType("role", resourceServer.getId()).forEach(policy -> {
Map<String, String> config = policy.getConfig();
String roles = config.get("roles");
List roleConfig;
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 0a323e0..0ca0400 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -17,14 +17,23 @@
package org.keycloak.models.utils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
import org.keycloak.authorization.AuthorizationProvider;
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.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
-import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
@@ -84,20 +93,6 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.storage.StorageId;
-import org.keycloak.util.JsonSerialization;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -772,34 +767,16 @@ public class ModelToRepresentation {
}
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
+ return toRepresentation(model, authorizationProvider, true);
+ }
+
+ public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider, boolean deep) {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setId(model.getId());
scope.setName(model.getName());
scope.setIconUri(model.getIconUri());
- StoreFactory storeFactory = authorizationProvider.getStoreFactory();
-
- scope.setResources(new ArrayList<>());
-
- storeFactory.getResourceStore().findByScope(model.getId()).forEach(resource -> scope.getResources().add(toRepresentation(resource, resource.getResourceServer(), authorizationProvider)));
-
- PolicyStore policyStore = storeFactory.getPolicyStore();
-
- scope.setPolicies(new ArrayList<>());
-
- policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> {
- PolicyRepresentation policy = new PolicyRepresentation();
-
- policy.setId(policyModel.getId());
- policy.setName(policyModel.getName());
- policy.setType(policyModel.getType());
-
- if (!scope.getPolicies().contains(policy)) {
- scope.getPolicies().add(policy);
- }
- });
-
return scope;
}
@@ -815,7 +792,7 @@ public class ModelToRepresentation {
return server;
}
- public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
+ public static PolicyRepresentation toRepresentation(Policy model) {
PolicyRepresentation representation = new PolicyRepresentation();
representation.setId(model.getId());
@@ -824,45 +801,16 @@ public class ModelToRepresentation {
representation.setType(model.getType());
representation.setDecisionStrategy(model.getDecisionStrategy());
representation.setLogic(model.getLogic());
- representation.setConfig(new HashMap<>(model.getConfig()));
-
- List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId());
-
- representation.setDependentPolicies(policies.stream().map(policy -> {
- PolicyRepresentation representation1 = new PolicyRepresentation();
-
- representation1.setId(policy.getId());
- representation1.setName(policy.getName());
-
- return representation1;
- }).collect(Collectors.toList()));
-
- List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
-
- List<String> obj = model.getAssociatedPolicies().stream().map(policy -> {
- PolicyRepresentation representation1 = new PolicyRepresentation();
-
- representation1.setId(policy.getId());
- representation1.setName(policy.getName());
- representation1.setType(policy.getType());
-
- associatedPolicies.add(representation1);
-
- return policy.getId();
- }).collect(Collectors.toList());
-
- representation.setAssociatedPolicies(associatedPolicies);
-
- try {
- representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj));
- } catch (IOException e) {
- e.printStackTrace();
- }
+ representation.setConfig(model.getConfig());
return representation;
}
public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) {
+ return toRepresentation(model, resourceServer, authorization, true);
+ }
+
+ public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization, Boolean deep) {
ResourceRepresentation resource = new ResourceRepresentation();
resource.setId(model.getId());
@@ -893,55 +841,36 @@ public class ModelToRepresentation {
resource.setOwner(owner);
- resource.setScopes(model.getScopes().stream().map(model1 -> {
- ScopeRepresentation scope = new ScopeRepresentation();
- scope.setId(model1.getId());
- scope.setName(model1.getName());
- String iconUri = model1.getIconUri();
- if (iconUri != null) {
- scope.setIconUri(iconUri);
- }
- return scope;
- }).collect(Collectors.toSet()));
-
- resource.setTypedScopes(new ArrayList<>());
-
- if (resource.getType() != null) {
- ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
- for (Resource typed : resourceStore.findByType(resource.getType())) {
- if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
- resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
- ScopeRepresentation scope = new ScopeRepresentation();
- scope.setId(model1.getId());
- scope.setName(model1.getName());
- String iconUri = model1.getIconUri();
- if (iconUri != null) {
- scope.setIconUri(iconUri);
- }
- return scope;
- }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
+ if (deep) {
+ resource.setScopes(model.getScopes().stream().map(model1 -> {
+ ScopeRepresentation scope = new ScopeRepresentation();
+ scope.setId(model1.getId());
+ scope.setName(model1.getName());
+ String iconUri = model1.getIconUri();
+ if (iconUri != null) {
+ scope.setIconUri(iconUri);
+ }
+ return scope;
+ }).collect(Collectors.toSet()));
+
+ resource.setTypedScopes(new ArrayList<>());
+
+ if (resource.getType() != null) {
+ ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+ for (Resource typed : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
+ if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
+ resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
+ ScopeRepresentation scope = new ScopeRepresentation();
+ scope.setId(model1.getId());
+ scope.setName(model1.getName());
+ String iconUri = model1.getIconUri();
+ if (iconUri != null) {
+ scope.setIconUri(iconUri);
+ }
+ return scope;
+ }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
+ }
}
- }
- }
-
- resource.setPolicies(new ArrayList<>());
-
- Set<Policy> policies = new HashSet<>();
- PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
-
- policies.addAll(policyStore.findByResource(resource.getId()));
- policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId()));
- policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
-
- for (Policy policyModel : policies) {
- PolicyRepresentation policy = new PolicyRepresentation();
-
- policy.setId(policyModel.getId());
- policy.setName(policyModel.getName());
- policy.setType(policyModel.getType());
-
- if (!resource.getPolicies().contains(policy)) {
- resource.getPolicies().add(policy);
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 96ffe8c..0fa958c 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1898,7 +1898,7 @@ public class RepresentationToModel {
if (roles != null && !roles.isEmpty()) {
try {
- List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
+ List<Map> rolesMap = (List<Map>)JsonSerialization.readValue(roles, List.class);
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
String roleName = roleConfig.get("id").toString();
String clientId = null;
@@ -1950,7 +1950,7 @@ public class RepresentationToModel {
if (users != null && !users.isEmpty()) {
try {
- List<String> usersMap = JsonSerialization.readValue(users, List.class);
+ List<String> usersMap = (List<String>) JsonSerialization.readValue(users, List.class);
config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userId -> {
UserModel user = session.users().getUserByUsername(userId, realm);
@@ -1974,12 +1974,12 @@ public class RepresentationToModel {
if (scopes != null && !scopes.isEmpty()) {
try {
ScopeStore scopeStore = storeFactory.getScopeStore();
- List<String> scopesMap = JsonSerialization.readValue(scopes, List.class);
+ List<String> scopesMap = (List<String>) JsonSerialization.readValue(scopes, List.class);
config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
if (newScope == null) {
- newScope = scopeStore.findById(scopeName);
+ newScope = scopeStore.findById(scopeName, resourceServer.getId());
}
if (newScope == null) {
@@ -1999,21 +1999,18 @@ public class RepresentationToModel {
ResourceStore resourceStore = storeFactory.getResourceStore();
try {
List<String> resources = JsonSerialization.readValue(policyResources, List.class);
- config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(new Function<String, String>() {
- @Override
- public String apply(String resourceName) {
- Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
+ config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
+ Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
- if (resource == null) {
- resource = resourceStore.findById(resourceName);
- }
-
- if (resource == null) {
- throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
- }
+ if (resource == null) {
+ resource = resourceStore.findById(resourceName, resourceServer.getId());
+ }
- return resource.getId();
+ if (resource == null) {
+ throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
}
+
+ return resource.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
@@ -2025,12 +2022,12 @@ public class RepresentationToModel {
if (applyPolicies != null && !applyPolicies.isEmpty()) {
PolicyStore policyStore = storeFactory.getPolicyStore();
try {
- List<String> policies = JsonSerialization.readValue(applyPolicies, List.class);
+ List<String> policies = (List<String>) JsonSerialization.readValue(applyPolicies, List.class);
config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> {
Policy policy = policyStore.findByName(policyName, resourceServer.getId());
if (policy == null) {
- policy = policyStore.findById(policyName);
+ policy = policyStore.findById(policyName, resourceServer.getId());
}
if (policy == null) {
@@ -2062,7 +2059,7 @@ public class RepresentationToModel {
Policy existing;
if (policy.getId() != null) {
- existing = policyStore.findById(policy.getId());
+ existing = policyStore.findById(policy.getId(), resourceServer.getId());
} else {
existing = policyStore.findByName(policy.getName(), resourceServer.getId());
}
@@ -2119,7 +2116,14 @@ public class RepresentationToModel {
}
}
if (!hasScope) {
- policy.addScope(storeFactory.getScopeStore().findById(scopeId));
+ ResourceServer resourceServer = policy.getResourceServer();
+ Scope scope = storeFactory.getScopeStore().findById(scopeId, resourceServer.getId());
+
+ if (scope == null) {
+ storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
+ }
+
+ policy.addScope(scope);
}
}
@@ -2135,6 +2139,8 @@ public class RepresentationToModel {
policy.removeScope(scopeModel);
}
}
+
+ policy.getConfig().remove("scopes");
}
}
@@ -2164,7 +2170,7 @@ public class RepresentationToModel {
if (!hasPolicy) {
- Policy associatedPolicy = policyStore.findById(policyId);
+ Policy associatedPolicy = policyStore.findById(policyId, resourceServer.getId());
if (associatedPolicy == null) {
associatedPolicy = policyStore.findByName(policyId, resourceServer.getId());
@@ -2186,6 +2192,8 @@ public class RepresentationToModel {
policy.removeAssociatedPolicy(policyModel);;
}
}
+
+ policy.getConfig().remove("applyPolicies");
}
}
@@ -2210,7 +2218,7 @@ public class RepresentationToModel {
}
}
if (!hasResource && !"".equals(resourceId)) {
- policy.addResource(storeFactory.getResourceStore().findById(resourceId));
+ policy.addResource(storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId()));
}
}
@@ -2227,6 +2235,8 @@ public class RepresentationToModel {
policy.removeResource(resourceModel);
}
}
+
+ policy.getConfig().remove("resources");
}
}
@@ -2235,7 +2245,7 @@ public class RepresentationToModel {
Resource existing;
if (resource.getId() != null) {
- existing = resourceStore.findById(resource.getId());
+ existing = resourceStore.findById(resource.getId(), resourceServer.getId());
} else {
existing = resourceStore.findByName(resource.getName(), resourceServer.getId());
}
@@ -2286,7 +2296,7 @@ public class RepresentationToModel {
Scope existing;
if (scope.getId() != null) {
- existing = scopeStore.findById(scope.getId());
+ existing = scopeStore.findById(scope.getId(), resourceServer.getId());
} else {
existing = scopeStore.findByName(scope.getName(), resourceServer.getId());
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index 076f9af..b096d24 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -17,6 +17,27 @@
*/
package org.keycloak.authorization.admin;
+import static java.util.Arrays.asList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.admin.representation.PolicyEvaluationRequest;
@@ -46,29 +67,10 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.Urls;
import org.keycloak.services.resources.admin.RealmAuth;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.POST;
-import javax.ws.rs.Produces;
-import javax.ws.rs.container.AsyncResponse;
-import javax.ws.rs.container.Suspended;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static java.util.Arrays.asList;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -144,40 +146,35 @@ public class PolicyEvaluationService {
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
- Set<String> givenScopes = resource.getScopes();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ if (resource == null) {
+ resource = new PolicyEvaluationRequest.Resource();
+ }
+
+ Set<ScopeRepresentation> givenScopes = resource.getScopes();
if (givenScopes == null) {
givenScopes = new HashSet();
}
- StoreFactory storeFactory = authorization.getStoreFactory();
+ Set<String> scopeNames = givenScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet());
if (resource.getId() != null) {
- Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId());
- return Permissions.createResourcePermissions(resourceModel, givenScopes, authorization).stream();
+ Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId(), resourceServer.getId());
+ return Permissions.createResourcePermissions(resourceModel, scopeNames, authorization).stream();
} else if (resource.getType() != null) {
- Set<String> finalGivenScopes = givenScopes;
- return storeFactory.getResourceStore().findByType(resource.getType()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, finalGivenScopes, authorization).stream());
+ return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization).stream());
} else {
ScopeStore scopeStore = storeFactory.getScopeStore();
- List<Scope> scopes = givenScopes.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
- List<ResourcePermission> collect = scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList());
-
- if (scopes.isEmpty()) {
- scopes = scopeStore.findByResourceServer(resourceServer.getId());
- }
+ List<Scope> scopes = scopeNames.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
+ List<ResourcePermission> collect = new ArrayList<ResourcePermission>();
- for (Scope scope : scopes) {
- collect.addAll(storeFactory.getResourceStore().findByScope(scope.getId()).stream().map(resource12 -> new ResourcePermission(resource12, asList(scope), resourceServer)).collect(Collectors.toList()));
+ if (!scopes.isEmpty()) {
+ collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList()));
+ } else {
+ collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization));
}
- collect.addAll(storeFactory.getResourceStore().findByResourceServer(resourceServer.getId()).stream().map(new Function<Resource, ResourcePermission>() {
- @Override
- public ResourcePermission apply(Resource resource) {
- return new ResourcePermission(resource, resource.getScopes(), resourceServer);
- }
- }).collect(Collectors.toList()));
-
return collect.stream();
}
}).collect(Collectors.toList());
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index e274bea..3caf2ea 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -17,6 +17,28 @@
*/
package org.keycloak.authorization.admin;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import static org.keycloak.models.utils.RepresentationToModel.toModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
@@ -25,35 +47,16 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
-import static org.keycloak.models.utils.RepresentationToModel.toModel;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -100,7 +103,7 @@ public class PolicyService {
this.auth.requireManage();
representation.setId(id);
StoreFactory storeFactory = authorization.getStoreFactory();
- Policy policy = storeFactory.getPolicyStore().findById(representation.getId());
+ Policy policy = storeFactory.getPolicyStore().findById(representation.getId(), resourceServer.getId());
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -127,7 +130,7 @@ public class PolicyService {
this.auth.requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.findById(id);
+ Policy policy = policyStore.findById(id, resourceServer.getId());
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -143,7 +146,7 @@ public class PolicyService {
}
}
- policyStore.findDependentPolicies(id).forEach(dependentPolicy -> {
+ policyStore.findDependentPolicies(id, resourceServer.getId()).forEach(dependentPolicy -> {
if (dependentPolicy.getAssociatedPolicies().size() == 1) {
policyStore.delete(dependentPolicy.getId());
} else {
@@ -163,13 +166,109 @@ public class PolicyService {
public Response findById(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id);
+ Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
- return Response.ok(toRepresentation(model, authorization)).build();
+ return Response.ok(toRepresentation(model)).build();
+ }
+
+ @Path("{id}/dependentPolicies")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getDependentPolicies(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId(), resourceServer.getId());
+
+ return Response.ok(policies.stream().map(policy -> {
+ PolicyRepresentation representation1 = new PolicyRepresentation();
+
+ representation1.setId(policy.getId());
+ representation1.setName(policy.getName());
+ representation1.setType(policy.getType());
+
+ return representation1;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("{id}/scopes")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getScopes(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(model.getScopes().stream().map(scope -> {
+ ScopeRepresentation representation = new ScopeRepresentation();
+
+ representation.setId(scope.getId());
+ representation.setName(scope.getName());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("{id}/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getResources(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(model.getResources().stream().map(resource -> {
+ ResourceRepresentation representation = new ResourceRepresentation();
+
+ representation.setId(resource.getId());
+ representation.setName(resource.getName());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("{id}/associatedPolicies")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getAssociatedPolicies(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(model.getAssociatedPolicies().stream().map(policy -> {
+ PolicyRepresentation representation1 = new PolicyRepresentation();
+
+ representation1.setId(policy.getId());
+ representation1.setName(policy.getName());
+ representation1.setType(policy.getType());
+
+ return representation1;
+ }).collect(Collectors.toList())).build();
}
@Path("/search")
@@ -190,13 +289,14 @@ public class PolicyService {
return Response.status(Status.OK).build();
}
- return Response.ok(toRepresentation(model, authorization)).build();
+ return Response.ok(toRepresentation(model)).build();
}
@GET
@Produces("application/json")
@NoCache
- public Response findAll(@QueryParam("name") String name,
+ public Response findAll(@QueryParam("policyId") String id,
+ @QueryParam("name") String name,
@QueryParam("type") String type,
@QueryParam("resource") String resource,
@QueryParam("permission") Boolean permission,
@@ -206,6 +306,10 @@ public class PolicyService {
Map<String, String[]> search = new HashMap<>();
+ if (id != null && !"".equals(id.trim())) {
+ search.put("id", new String[] {id});
+ }
+
if (name != null && !"".equals(name.trim())) {
search.put("name", new String[] {name});
}
@@ -216,16 +320,17 @@ public class PolicyService {
StoreFactory storeFactory = authorization.getStoreFactory();
+ PolicyStore policyStore = storeFactory.getPolicyStore();
if (resource != null && !"".equals(resource.trim())) {
List<Policy> policies = new ArrayList<>();
HashMap<String, String[]> resourceSearch = new HashMap<>();
resourceSearch.put("name", new String[] {resource});
- storeFactory.getResourceStore().findByResourceServer(resourceSearch, resourceServer.getId(), -1, -1).forEach(resource1 -> {
- ResourceRepresentation resourceRepresentation = ModelToRepresentation.toRepresentation(resource1, resourceServer, authorization);
- resourceRepresentation.getPolicies().forEach(policyRepresentation -> {
- Policy associated = storeFactory.getPolicyStore().findById(policyRepresentation.getId());
+ ResourceStore resourceStore = storeFactory.getResourceStore();
+ resourceStore.findByResourceServer(resourceSearch, resourceServer.getId(), -1, -1).forEach(resource1 -> {
+ policyStore.findByResource(resource1.getId(), resourceServer.getId()).forEach(policyRepresentation -> {
+ Policy associated = policyStore.findById(policyRepresentation.getId(), resourceServer.getId());
policies.add(associated);
findAssociatedPolicies(associated, policies);
});
@@ -243,8 +348,8 @@ public class PolicyService {
}
return Response.ok(
- storeFactory.getPolicyStore().findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
- .map(policy -> toRepresentation(policy, authorization))
+ policyStore.findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
+ .map(policy -> toRepresentation(policy))
.collect(Collectors.toList()))
.build();
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java
index 17edef9..da7b420 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java
@@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -82,42 +84,7 @@ public class PolicyEvaluationRequest {
this.entitlements = entitlements;
}
- public static class Resource {
- private String id;
- private String name;
- private String type;
- private Set<String> scopes;
-
- public String getId() {
- return this.id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(final String type) {
- this.type = type;
- }
-
- public Set<String> getScopes() {
- return scopes;
- }
+ public static class Resource extends ResourceRepresentation {
- public void setScopes(final Set<String> scopes) {
- this.scopes = scopes;
- }
}
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
index 4f0c64a..83c00b8 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
@@ -35,9 +35,11 @@ import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -62,7 +64,7 @@ public class PolicyEvaluationResponse {
AccessToken accessToken = identity.getAccessToken();
AccessToken.Authorization authorizationData = new AccessToken.Authorization();
- authorizationData.setPermissions(Permissions.allPermits(results, authorization));
+ authorizationData.setPermissions(Permissions.permits(results, authorization, resourceServer.getId()));
accessToken.setAuthorization(authorizationData);
response.rpt = accessToken;
@@ -80,7 +82,12 @@ public class PolicyEvaluationResponse {
resultsRep.add(rep);
if (result.getPermission().getResource() != null) {
- rep.setResource(ModelToRepresentation.toRepresentation(result.getPermission().getResource(), resourceServer, authorization));
+ ResourceRepresentation resource = new ResourceRepresentation();
+
+ resource.setId(result.getPermission().getResource().getId());
+ resource.setName(result.getPermission().getResource().getName());
+
+ rep.setResource(resource);
} else {
ResourceRepresentation resource = new ResourceRepresentation();
@@ -89,7 +96,14 @@ public class PolicyEvaluationResponse {
rep.setResource(resource);
}
- rep.setScopes(result.getPermission().getScopes().stream().map(scope -> ModelToRepresentation.toRepresentation(scope, authorization)).collect(Collectors.toList()));
+ rep.setScopes(result.getPermission().getScopes().stream().map(scope -> {
+ ScopeRepresentation representation = new ScopeRepresentation();
+
+ representation.setId(scope.getId());
+ representation.setName(scope.getName());
+
+ return representation;
+ }).collect(Collectors.toList()));
List<PolicyResultRepresentation> policies = new ArrayList<>();
@@ -100,7 +114,7 @@ public class PolicyEvaluationResponse {
rep.setPolicies(policies);
}
- resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName()));
+ resultsRep.sort(Comparator.comparing(o -> o.getResource().getName()));
Map<String, EvaluationResultRepresentation> groupedResults = new HashMap<>();
@@ -127,17 +141,29 @@ public class PolicyEvaluationResponse {
List<ScopeRepresentation> currentScopes = evaluationResultRepresentation.getScopes();
if (currentScopes != null) {
+ List<ScopeRepresentation> allowedScopes = result.getAllowedScopes();
for (ScopeRepresentation scope : currentScopes) {
if (!scopes.contains(scope)) {
scopes.add(scope);
}
if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) {
- List<ScopeRepresentation> allowedScopes = result.getAllowedScopes();
if (!allowedScopes.contains(scope)) {
allowedScopes.add(scope);
}
+ } else {
+ evaluationResultRepresentation.getPolicies().forEach(new Consumer<PolicyResultRepresentation>() {
+ @Override
+ public void accept(PolicyResultRepresentation policyResultRepresentation) {
+ if (policyResultRepresentation.getStatus().equals(Effect.PERMIT)) {
+ if (!allowedScopes.contains(scope)) {
+ allowedScopes.add(scope);
+ }
+ }
+ }
+ });
}
}
+ result.setAllowedScopes(allowedScopes);
}
if (resource.getId() != null) {
@@ -160,18 +186,14 @@ public class PolicyEvaluationResponse {
}
if (policy.getStatus().equals(Effect.DENY)) {
- Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId());
+ Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId(), resourceServer.getId());
for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) {
- if (!policy.getScopes().contains(scope)) {
+ if (!policy.getScopes().contains(scope) && policyModel.getScopes().stream().filter(policyScope -> policyScope.getId().equals(scope.getId())).findFirst().isPresent()) {
+ result.getAllowedScopes().remove(scope);
policy.getScopes().add(scope);
}
}
- for (ScopeRepresentation scope : currentScopes) {
- if (!policy.getScopes().contains(scope)) {
- policy.getScopes().add(scope);
- }
- }
- }
+ } else {}
}
});
@@ -183,7 +205,14 @@ public class PolicyEvaluationResponse {
private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) {
PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation();
- policyResultRep.setPolicy(ModelToRepresentation.toRepresentation(policy.getPolicy(), authorization));
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setId(policy.getPolicy().getId());
+ representation.setName(policy.getPolicy().getName());
+ representation.setType(policy.getPolicy().getType());
+ representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy());
+
+ policyResultRep.setPolicy(representation);
policyResultRep.setStatus(policy.getStatus());
policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index c1cb821..bb241d8 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -32,8 +32,10 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.RealmAuth;
@@ -48,10 +50,15 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
import java.util.stream.Collectors;
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
@@ -127,7 +134,7 @@ public class ResourceSetService {
resource.setId(id);
StoreFactory storeFactory = this.authorization.getStoreFactory();
ResourceStore resourceStore = storeFactory.getResourceStore();
- Resource model = resourceStore.findById(resource.getId());
+ Resource model = resourceStore.findById(resource.getId(), resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -143,14 +150,14 @@ public class ResourceSetService {
public Response delete(@PathParam("id") String id) {
requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
- Resource resource = storeFactory.getResourceStore().findById(id);
+ Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
if (resource == null) {
return Response.status(Status.NOT_FOUND).build();
}
PolicyStore policyStore = storeFactory.getPolicyStore();
- List<Policy> policies = policyStore.findByResource(id);
+ List<Policy> policies = policyStore.findByResource(id, resourceServer.getId());
for (Policy policyModel : policies) {
if (policyModel.getResources().size() == 1) {
@@ -172,13 +179,93 @@ public class ResourceSetService {
public Response findById(@PathParam("id") String id) {
requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
- Resource model = storeFactory.getResourceStore().findById(id);
+ Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
- return Response.ok(toRepresentation(model, this.resourceServer, authorization)).build();
+ return Response.ok(toRepresentation(model, this.resourceServer, authorization, true)).build();
+ }
+
+ @Path("{id}/scopes")
+ @GET
+ @NoCache
+ @Produces("application/json")
+ public Response getScopes(@PathParam("id") String id) {
+ requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ List<ScopeRepresentation> scopes = model.getScopes().stream().map(scope -> {
+ ScopeRepresentation representation = new ScopeRepresentation();
+
+ representation.setId(scope.getId());
+ representation.setName(scope.getName());
+
+ return representation;
+ }).collect(Collectors.toList());
+
+ if (model.getType() != null) {
+ ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+ for (Resource typed : resourceStore.findByType(model.getType(), resourceServer.getId())) {
+ if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(model.getId())) {
+ scopes.addAll(typed.getScopes().stream().map(model1 -> {
+ ScopeRepresentation scope = new ScopeRepresentation();
+ scope.setId(model1.getId());
+ scope.setName(model1.getName());
+ String iconUri = model1.getIconUri();
+ if (iconUri != null) {
+ scope.setIconUri(iconUri);
+ }
+ return scope;
+ }).filter(scopeRepresentation -> !scopes.contains(scopeRepresentation)).collect(Collectors.toList()));
+ }
+ }
+ }
+
+ return Response.ok(scopes).build();
+ }
+
+ @Path("{id}/permissions")
+ @GET
+ @NoCache
+ @Produces("application/json")
+ public Response getPermissions(@PathParam("id") String id) {
+ requireView();
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+ Set<Policy> policies = new HashSet<>();
+
+ policies.addAll(policyStore.findByResource(model.getId(), resourceServer.getId()));
+ policies.addAll(policyStore.findByResourceType(model.getType(), resourceServer.getId()));
+ policies.addAll(policyStore.findByScopeIds(model.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
+
+ List<PolicyRepresentation> representation = new ArrayList<>();
+
+ for (Policy policyModel : policies) {
+ PolicyRepresentation policy = new PolicyRepresentation();
+
+ policy.setId(policyModel.getId());
+ policy.setName(policyModel.getName());
+ policy.setType(policyModel.getType());
+
+ if (!representation.contains(policy)) {
+ representation.add(policy);
+ }
+ }
+
+ return Response.ok(representation).build();
}
@Path("/search")
@@ -205,18 +292,29 @@ public class ResourceSetService {
@GET
@NoCache
@Produces("application/json")
- public Response find(@QueryParam("name") String name,
+ public Response find(@QueryParam("_id") String id,
+ @QueryParam("name") String name,
@QueryParam("uri") String uri,
@QueryParam("owner") String owner,
@QueryParam("type") String type,
@QueryParam("scope") String scope,
+ @QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) {
requireView();
+
StoreFactory storeFactory = authorization.getStoreFactory();
+ if (deep == null) {
+ deep = true;
+ }
+
Map<String, String[]> search = new HashMap<>();
+ if (id != null && !"".equals(id.trim())) {
+ search.put("id", new String[] {id});
+ }
+
if (name != null && !"".equals(name.trim())) {
search.put("name", new String[] {name});
}
@@ -260,9 +358,10 @@ public class ResourceSetService {
search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
}
+ Boolean finalDeep = deep;
return Response.ok(
storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
- .map(resource -> toRepresentation(resource, this.resourceServer, authorization))
+ .map(resource -> toRepresentation(resource, resourceServer, authorization, finalDeep))
.collect(Collectors.toList()))
.build();
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index 83cffeb..7724830 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -26,6 +26,8 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.Constants;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.RealmAuth;
@@ -41,6 +43,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -85,7 +88,7 @@ public class ScopeService {
this.auth.requireManage();
scope.setId(id);
StoreFactory storeFactory = authorization.getStoreFactory();
- Scope model = storeFactory.getScopeStore().findById(scope.getId());
+ Scope model = storeFactory.getScopeStore().findById(scope.getId(), resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -101,13 +104,13 @@ public class ScopeService {
public Response delete(@PathParam("id") String id) {
this.auth.requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
- List<Resource> resources = storeFactory.getResourceStore().findByScope(id);
+ List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
if (!resources.isEmpty()) {
return ErrorResponse.exists("Scopes can not be removed while associated with resources.");
}
- Scope scope = storeFactory.getScopeStore().findById(id);
+ Scope scope = storeFactory.getScopeStore().findById(id, resourceServer.getId());
if (scope == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -134,7 +137,7 @@ public class ScopeService {
@Produces("application/json")
public Response findById(@PathParam("id") String id) {
this.auth.requireView();
- Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id);
+ Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -143,6 +146,53 @@ public class ScopeService {
return Response.ok(toRepresentation(model, this.authorization)).build();
}
+ @Path("{id}/resources")
+ @GET
+ @Produces("application/json")
+ public Response getResources(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = this.authorization.getStoreFactory();
+ Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(storeFactory.getResourceStore().findByScope(Arrays.asList(model.getId()), resourceServer.getId()).stream().map(resource -> {
+ ResourceRepresentation representation = new ResourceRepresentation();
+
+ representation.setId(resource.getId());
+ representation.setName(resource.getName());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("{id}/permissions")
+ @GET
+ @Produces("application/json")
+ public Response getPermissions(@PathParam("id") String id) {
+ this.auth.requireView();
+ StoreFactory storeFactory = this.authorization.getStoreFactory();
+ Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
+
+ if (model == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ PolicyStore policyStore = storeFactory.getPolicyStore();
+
+ return Response.ok(policyStore.findByScopeIds(Arrays.asList(model.getId()), resourceServer.getId()).stream().map(policy -> {
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setId(policy.getId());
+ representation.setName(policy.getName());
+ representation.setType(policy.getType());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
@Path("/search")
@GET
@Produces("application/json")
@@ -166,20 +216,31 @@ public class ScopeService {
@GET
@Produces("application/json")
- public Response findAll(@QueryParam("name") String name,
+ public Response findAll(@QueryParam("scopeId") String id,
+ @QueryParam("name") String name,
+ @QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) {
this.auth.requireView();
+ if (deep == null) {
+ deep = true;
+ }
+
Map<String, String[]> search = new HashMap<>();
+ if (id != null && !"".equals(id.trim())) {
+ search.put("id", new String[] {id});
+ }
+
if (name != null && !"".equals(name.trim())) {
search.put("name", new String[] {name});
}
+ Boolean finalDeep = deep;
return Response.ok(
this.authorization.getStoreFactory().getScopeStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
- .map(scope -> toRepresentation(scope, this.authorization))
+ .map(scope -> toRepresentation(scope, this.authorization, finalDeep))
.collect(Collectors.toList()))
.build();
}
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
index 77a35e9..e60a0d6 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -110,7 +110,7 @@ public class AuthorizationTokenService {
authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate(new DecisionResultCollector() {
@Override
public void onComplete(List<Result> results) {
- List<Permission> entitlements = Permissions.allPermits(results, authorization);
+ List<Permission> entitlements = Permissions.permits(results, authorization, ticket.getResourceServerId());
if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>();
@@ -144,7 +144,7 @@ public class AuthorizationTokenService {
Resource resource;
if (requestedResource.getId() != null) {
- resource = storeFactory.getResourceStore().findById(requestedResource.getId());
+ resource = storeFactory.getResourceStore().findById(requestedResource.getId(), ticket.getResourceServerId());
} else {
resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId());
}
@@ -171,7 +171,7 @@ public class AuthorizationTokenService {
}
return scope.getId();
- }).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
+ }).filter(s -> s != null).collect(Collectors.toList()), ticket.getResourceServerId()));
for (Resource resource1 : resources) {
permissionsToEvaluate.put(resource1.getId(), collect);
@@ -204,7 +204,7 @@ public class AuthorizationTokenService {
if (permissions != null) {
permissions.forEach(permission -> {
- Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
+ Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId(), ticket.getResourceServerId());
if (resourcePermission != null) {
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
@@ -240,7 +240,7 @@ public class AuthorizationTokenService {
}).collect(Collectors.toList());
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
} else {
- Resource entryResource = storeFactory.getResourceStore().findById(key);
+ Resource entryResource = storeFactory.getResourceStore().findById(key, resourceServer.getId());
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
}
}).collect(Collectors.toList());
diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
index 6146594..159e5aa 100644
--- a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
+++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
@@ -18,14 +18,20 @@
package org.keycloak.authorization;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
import org.keycloak.Config;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
-
-import java.util.concurrent.Executor;
+import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -33,6 +39,7 @@ import java.util.concurrent.Executor;
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
private Executor scheduler;
+ private Map<String, PolicyProviderFactory> policyProviderFactories;
@Override
public AuthorizationProvider create(KeycloakSession session) {
@@ -54,6 +61,7 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
@Override
public void postInit(KeycloakSessionFactory factory) {
+ policyProviderFactories = configurePolicyProviderFactories(factory);
}
@Override
@@ -74,6 +82,21 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
storeFactory = session.getProvider(StoreFactory.class);
}
- return new AuthorizationProvider(session, realm, storeFactory);
+ return new AuthorizationProvider(session, realm, storeFactory, policyProviderFactories);
+ }
+
+ private Map<String, PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSessionFactory keycloakSessionFactory) {
+ List<ProviderFactory> providerFactories = keycloakSessionFactory.getProviderFactories(PolicyProvider.class);
+
+ if (providerFactories.isEmpty()) {
+ throw new RuntimeException("Could not find any policy provider.");
+ }
+
+ HashMap<String, PolicyProviderFactory> providers = new HashMap<>();
+
+ providerFactories.forEach(providerFactory -> providers.put(providerFactory.getId(), (PolicyProviderFactory) providerFactory));
+
+ return providers;
}
+
}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
index 9f89e4a..579417d 100644
--- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
@@ -46,6 +46,7 @@ import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.Cors;
@@ -125,7 +126,7 @@ public class EntitlementService {
@Override
protected void onComplete(List<Result> results) {
- List<Permission> entitlements = Permissions.allPermits(results, authorization);
+ List<Permission> entitlements = Permissions.allPermits(results, authorization, resourceServer);
if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>();
@@ -169,31 +170,40 @@ public class EntitlementService {
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(client.getId());
- authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
-
- @Override
- public void onError(Throwable cause) {
- asyncResponse.resume(cause);
- }
+ try {
+ authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
+ @Override
+ public void onError(Throwable cause) {
+ asyncResponse.resume(cause);
+ }
- @Override
- protected void onComplete(List<Result> results) {
- List<Permission> entitlements = Permissions.allPermits(results, authorization);
+ @Override
+ protected void onComplete(List<Result> results) {
+ List<Permission> entitlements = Permissions.allPermits(results, authorization, resourceServer);
- if (entitlements.isEmpty()) {
- HashMap<Object, Object> error = new HashMap<>();
+ if (entitlements.isEmpty()) {
+ HashMap<Object, Object> error = new HashMap<>();
- error.put(OAuth2Constants.ERROR, "not_authorized");
+ error.put(OAuth2Constants.ERROR, "not_authorized");
- asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
- .entity(error))
- .allowedOrigins(identity.getAccessToken())
- .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
- } else {
- asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+ asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
+ .entity(error))
+ .allowedOrigins(identity.getAccessToken())
+ .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+ } else {
+ asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+ }
}
+ });
+ } catch (Exception e) {
+ String message = e.getMessage();
+
+ if (message == null) {
+ message = "Could not process authorization request";
}
- });
+
+ asyncResponse.resume(ErrorResponse.error(message, Status.BAD_REQUEST));
+ }
}
private String createRequestingPartyToken(List<Permission> permissions) {
@@ -215,7 +225,7 @@ public class EntitlementService {
Resource resource;
if (requestedResource.getResourceSetId() != null) {
- resource = storeFactory.getResourceStore().findById(requestedResource.getResourceSetId());
+ resource = storeFactory.getResourceStore().findById(requestedResource.getResourceSetId(), resourceServer.getId());
} else {
resource = storeFactory.getResourceStore().findByName(requestedResource.getResourceSetName(), resourceServer.getId());
}
@@ -242,7 +252,7 @@ public class EntitlementService {
}
return scope.getId();
- }).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
+ }).filter(s -> s != null).collect(Collectors.toList()), resourceServer.getId()));
for (Resource resource1 : resources) {
permissionsToEvaluate.put(resource1.getId(), collect);
@@ -276,7 +286,7 @@ public class EntitlementService {
if (permissions != null) {
permissions.forEach(permission -> {
- Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
+ Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId(), resourceServer.getId());
if (resourcePermission != null) {
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
@@ -310,7 +320,7 @@ public class EntitlementService {
}).collect(Collectors.toList());
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
} else {
- Resource entryResource = storeFactory.getResourceStore().findById(key);
+ Resource entryResource = storeFactory.getResourceStore().findById(key, resourceServer.getId());
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
}
}).collect(Collectors.toList());
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
index a412c0c..80fb84a 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
@@ -77,7 +77,7 @@ public class AbstractPermissionService {
if (!resourceNotProvider) {
if (resourceSetId != null) {
- resource = storeFactory.getResourceStore().findById(resourceSetId);
+ resource = storeFactory.getResourceStore().findById(resourceSetId, resourceServer.getId());
} else {
resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
}
@@ -113,7 +113,7 @@ public class AbstractPermissionService {
}
}
- for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType())) {
+ for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType(), resourceServer.getId())) {
if (baseResource.getOwner().equals(resource.getResourceServer().getClientId())) {
for (Scope baseScope : baseResource.getScopes()) {
if (baseScope.getName().equals(scopeName)) {
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
index fdaa12f..219bcca 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -109,7 +109,7 @@ public class ResourceService {
}
private Set<String> findAll() {
- Response response = this.resourceManager.find(null, null, null, null, null, -1, -1);
+ Response response = this.resourceManager.find(null, null, null, null, null, null, true, -1, -1);
List<ResourceRepresentation> resources = (List<ResourceRepresentation>) response.getEntity();
return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet());
}
@@ -150,7 +150,7 @@ public class ResourceService {
}
}
} else {
- resources = storeFactory.getResourceStore().findByOwner(identity.getId()).stream()
+ resources = storeFactory.getResourceStore().findByOwner(identity.getId(), resourceServer.getId()).stream()
.map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
.collect(Collectors.toSet());
}
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 116ddd6..90de912 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -18,9 +18,18 @@
package org.keycloak.authorization.util;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.Decision.Effect;
import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
@@ -31,16 +40,6 @@ import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.idm.authorization.Permission;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -62,8 +61,8 @@ public final class Permissions {
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceStore resourceStore = storeFactory.getResourceStore();
- resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization)));
- resourceStore.findByOwner(identity.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;
}
@@ -81,7 +80,7 @@ public final class Permissions {
if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) {
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceStore resourceStore = storeFactory.getResourceStore();
- resourceStore.findByType(type).forEach(resource1 -> {
+ resourceStore.findByType(type, resourceServer.getId()).forEach(resource1 -> {
if (resource1.getOwner().equals(resourceServer.getClientId())) {
for (Scope typeScope : resource1.getScopes()) {
if (!scopes.contains(typeScope)) {
@@ -95,78 +94,141 @@ public final class Permissions {
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
scopes = requestedScopes.stream().map(scopeName -> {
Scope byName = scopeStore.findByName(scopeName, resource.getResourceServer().getId());
+
+ if (byName == null) {
+ throw new RuntimeException("Invalid scope [" + scopeName + "].");
+ }
+
return byName;
}).collect(Collectors.toList());
}
- if (scopes.isEmpty()) {
- permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
- } else {
- for (Scope scope : scopes) {
- permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
- }
+ permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
+
+ return permissions;
+ }
+
+ public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization) {
+ List<ResourcePermission> 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<Permission> allPermits(List<Result> evaluation, AuthorizationProvider authorizationProvider) {
+ public static List<Permission> allPermits(List<Result> evaluation, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) {
+ Map<String, Permission> permissions = new HashMap<>();
+
+ for (Result evaluationResult : evaluation) {
+ ResourcePermission permission = evaluationResult.getPermission();
+
+ // if overall decision was a DENY, check for scope-based policies for a PERMIT
+ if (evaluationResult.getEffect().equals(Effect.DENY)) {
+ for (Result.PolicyResult result : evaluationResult.getResults()) {
+ Policy policy = result.getPolicy();
+
+ if ("scope".equals(policy.getType())) {
+ Set<Resource> resources = policy.getResources();
+
+ if (Effect.PERMIT.equals(result.getStatus())) {
+ List<Scope> scopes = policy.getScopes().stream().collect(Collectors.toList());
+
+ if (!resources.isEmpty()) {
+ resources.forEach(resource -> grantPermission(authorizationProvider, permissions, new ResourcePermission(resource, scopes, policy.getResourceServer()), resourceServer.getId()));
+ } else {
+ grantPermission(authorizationProvider, permissions, new ResourcePermission(permission.getResource(), scopes, policy.getResourceServer()), resourceServer.getId());
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ grantPermission(authorizationProvider, permissions, permission, resourceServer.getId());
+ }
+
+ return permissions.values().stream().collect(Collectors.toList());
+ }
+
+ public static List<Permission> permits(List<Result> evaluation, AuthorizationProvider authorizationProvider, String resourceServer) {
Map<String, Permission> permissions = new HashMap<>();
for (Result evaluationResult : evaluation) {
ResourcePermission permission = evaluationResult.getPermission();
- Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
if (evaluationResult.getEffect().equals(Effect.DENY)) {
continue;
}
- List<Resource> resources = new ArrayList<>();
- Resource resource = permission.getResource();
+ grantPermission(authorizationProvider, permissions, permission, resourceServer);
+ }
- if (resource != null) {
- resources.add(resource);
- } else {
- List<Scope> permissionScopes = permission.getScopes();
+ return permissions.values().stream().collect(Collectors.toList());
+ }
- if (!permissionScopes.isEmpty()) {
- ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
- resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()).toArray(new String[permissionScopes.size()])));
- }
+ private static void grantPermission(AuthorizationProvider authorizationProvider, Map<String, Permission> permissions, ResourcePermission permission, String resourceServer) {
+ List<Resource> resources = new ArrayList<>();
+ Resource resource = permission.getResource();
+ Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
+
+ if (resource != null) {
+ resources.add(resource);
+ } else {
+ List<Scope> permissionScopes = permission.getScopes();
+
+ if (!permissionScopes.isEmpty()) {
+ ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
+ resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer));
}
+ }
- if (!resources.isEmpty()) {
- for (Resource allowedResource : resources) {
- String resourceId = allowedResource.getId();
- String resourceName = allowedResource.getName();
- Permission evalPermission = permissions.get(allowedResource.getId());
+ if (!resources.isEmpty()) {
+ for (Resource allowedResource : resources) {
+ String resourceId = allowedResource.getId();
+ String resourceName = allowedResource.getName();
+ Permission evalPermission = permissions.get(allowedResource.getId());
- if (evalPermission == null) {
- evalPermission = new Permission(resourceId, resourceName, scopes);
- permissions.put(resourceId, evalPermission);
- }
+ if (evalPermission == null) {
+ evalPermission = new Permission(resourceId, resourceName, scopes);
+ permissions.put(resourceId, evalPermission);
+ }
- if (scopes != null && !scopes.isEmpty()) {
- Set<String> finalScopes = evalPermission.getScopes();
+ if (scopes != null && !scopes.isEmpty()) {
+ Set<String> finalScopes = evalPermission.getScopes();
- if (finalScopes == null) {
- finalScopes = new HashSet();
- evalPermission.setScopes(finalScopes);
- }
+ if (finalScopes == null) {
+ finalScopes = new HashSet();
+ evalPermission.setScopes(finalScopes);
+ }
- for (String scopeName : scopes) {
- if (!finalScopes.contains(scopeName)) {
- finalScopes.add(scopeName);
- }
+ for (String scopeName : scopes) {
+ if (!finalScopes.contains(scopeName)) {
+ finalScopes.add(scopeName);
}
}
}
- } else {
- Permission scopePermission = new Permission(null, null, scopes);
- permissions.put(scopePermission.toString(), scopePermission);
}
+ } else {
+ Permission scopePermission = new Permission(null, null, scopes);
+ permissions.put(scopePermission.toString(), scopePermission);
}
-
- return permissions.values().stream().collect(Collectors.toList());
}
}
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index df7f8d3..29b8942 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -17,18 +17,27 @@
package org.keycloak.exportimport.util;
-import com.fasterxml.jackson.core.JsonEncoding;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.AuthorizationProviderFactory;
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.PolicyStore;
-import org.keycloak.authorization.store.ResourceStore;
-import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.Version;
import org.keycloak.common.util.Base64;
@@ -63,20 +72,11 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.util.JsonSerialization;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -295,7 +295,6 @@ public class ExportUtils {
rep.getOwner().setId(null);
}
rep.setId(null);
- rep.setPolicies(null);
rep.getScopes().forEach(scopeRepresentation -> {
scopeRepresentation.setId(null);
scopeRepresentation.setIconUri(null);
@@ -338,10 +337,9 @@ public class ExportUtils {
RealmModel realm = authorizationProvider.getRealm();
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
try {
- PolicyRepresentation rep = toRepresentation(policy, authorizationProvider);
+ PolicyRepresentation rep = toRepresentation(policy);
rep.setId(null);
- rep.setDependentPolicies(null);
Map<String, String> config = rep.getConfig();
@@ -363,20 +361,18 @@ public class ExportUtils {
config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, realm).getUsername()).collect(Collectors.toList())));
}
- String scopes = config.get("scopes");
+ Set<Scope> scopes = policy.getScopes();
- if (scopes != null && !scopes.isEmpty()) {
- ScopeStore scopeStore = storeFactory.getScopeStore();
- List<String> scopeIds = JsonSerialization.readValue(scopes, List.class);
- config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList())));
+ if (!scopes.isEmpty()) {
+ List<String> scopeNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
+ config.put("scopes", JsonSerialization.writeValueAsString(scopeNames));
}
- String policyResources = config.get("resources");
+ Set<Resource> policyResources = policy.getResources();
- if (policyResources != null && !policyResources.isEmpty()) {
- ResourceStore resourceStore = storeFactory.getResourceStore();
- List<String> resourceIds = JsonSerialization.readValue(policyResources, List.class);
- config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList())));
+ if (!policyResources.isEmpty()) {
+ List<String> resourceNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
+ config.put("resources", JsonSerialization.writeValueAsString(resourceNames));
}
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
@@ -385,8 +381,6 @@ public class ExportUtils {
config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
}
- rep.setAssociatedPolicies(null);
-
return rep;
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
index c9b69d8..c46b338 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
@@ -101,7 +101,7 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
// during tests we create resource instances, but we need to reload them to get their collections updated
List<ResourcePermission> updatedPermissions = permissions.stream().map(permission -> {
- Resource resource = storeFactory.getResourceStore().findById(permission.getResource().getId());
+ Resource resource = storeFactory.getResourceStore().findById(permission.getResource().getId(), resourceServer.getId());
return new ResourcePermission(resource, permission.getScopes(), permission.getResourceServer());
}).collect(Collectors.toList());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java
index 086b5ac..4a4fc9a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java
@@ -53,7 +53,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+ Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
assertNotNull(resourceModel);
assertEquals(resource.getId(), resourceModel.getId());
@@ -89,7 +89,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+ Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
assertNotNull(resourceModel);
assertEquals(resource.getId(), resourceModel.getId());
@@ -147,7 +147,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
onAuthorizationSession(authorizationProvider -> {
- Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+ Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
assertNull(resourceModel);
});
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
index 9ecbc3d..4708a2a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
@@ -46,8 +46,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -77,7 +81,7 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
+ Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
assertNotNull(policyModel);
assertEquals(permission.getId(), policyModel.getId());
@@ -357,7 +361,7 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
+ Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
assertNotNull(policyModel);
assertEquals(permission.getId(), policyModel.getId());
@@ -430,7 +434,8 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
config.put("defaultResourceType", albumResource.getType());
- String applyPolicies = JsonSerialization.writeValueAsString(new String[]{this.anyUserPolicy.getId(), this.administrationPolicy.getId()});
+ String[] associatedPolicies = {this.anyUserPolicy.getId(), this.administrationPolicy.getId()};
+ String applyPolicies = JsonSerialization.writeValueAsString(associatedPolicies);
config.put("applyPolicies", applyPolicies);
@@ -443,14 +448,15 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
+ Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
assertNotNull(policyModel);
assertEquals(permission.getId(), policyModel.getId());
assertEquals(permission.getName(), policyModel.getName());
assertEquals(permission.getType(), policyModel.getType());
assertTrue(permission.getConfig().containsValue(albumResource.getType()));
- assertTrue(permission.getConfig().containsValue(applyPolicies));
+ assertTrue(policyModel.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toList()).containsAll(Arrays.asList(associatedPolicies)));
+
assertEquals(resourceServer.getId(), policyModel.getResourceServer().getId());
});
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java
index b2e1a42..644883f 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java
@@ -57,7 +57,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+ Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
assertNotNull(scopeModel);
assertEquals(scope.getId(), scopeModel.getId());
@@ -86,7 +86,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
onAuthorizationSession(authorizationProvider -> {
- Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+ Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
assertNotNull(scopeModel);
assertEquals(scope.getId(), scopeModel.getId());
@@ -138,7 +138,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
onAuthorizationSession(authorizationProvider -> {
- Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+ Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
assertNull(scopeModel);
});
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 3f4ddd1..27e9f5e 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -64,7 +64,7 @@
"connectionsJpa": {
"default": {
- "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test}",
+ "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;DB_CLOSE_DELAY=-1}",
"driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
"driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
"user": "${keycloak.connectionsJpa.user:sa}",
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
index 1fa5fc7..b3a0475 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
@@ -18,7 +18,6 @@ package org.keycloak.testsuite.authorization;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -43,8 +42,8 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
- return new TestPolicyProvider(policy, authorization);
+ public PolicyProvider create(AuthorizationProvider authorization) {
+ return new TestPolicyProvider(authorization);
}
@Override
@@ -79,11 +78,9 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
private class TestPolicyProvider implements PolicyProvider {
- private final Policy policy;
private final AuthorizationProvider authorization;
- public TestPolicyProvider(Policy policy, AuthorizationProvider authorization) {
- this.policy = policy;
+ public TestPolicyProvider(AuthorizationProvider authorization) {
this.authorization = authorization;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
index e6f83d8..a1a2bfc 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
@@ -38,6 +38,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@@ -71,7 +72,7 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation();
assertEquals("Test Generic Policy", newPolicy.getName());
- assertEquals("test", newPolicy.getType());
+ assertEquals("scope", newPolicy.getType());
assertEquals(Logic.POSITIVE, newPolicy.getLogic());
assertEquals(DecisionStrategy.UNANIMOUS, newPolicy.getDecisionStrategy());
assertEquals("configuration for A", newPolicy.getConfig().get("configA"));
@@ -98,28 +99,28 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
@Test
public void testUpdate() {
PolicyResource policyResource = createTestingPolicy();
- PolicyRepresentation resource = policyResource.toRepresentation();
+ PolicyRepresentation policy = policyResource.toRepresentation();
- resource.setName("changed");
- resource.setLogic(Logic.NEGATIVE);
- resource.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
- resource.getConfig().put("configA", "changed configuration for A");
- resource.getConfig().remove("configB");
- resource.getConfig().put("configC", "changed configuration for C");
+ policy.setName("changed");
+ policy.setLogic(Logic.NEGATIVE);
+ policy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ policy.getConfig().put("configA", "changed configuration for A");
+ policy.getConfig().remove("configB");
+ policy.getConfig().put("configC", "changed configuration for C");
- policyResource.update(resource);
+ policyResource.update(policy);
- resource = policyResource.toRepresentation();
+ policy = policyResource.toRepresentation();
- assertEquals("changed", resource.getName());
- assertEquals(Logic.NEGATIVE, resource.getLogic());
+ assertEquals("changed", policy.getName());
+ assertEquals(Logic.NEGATIVE, policy.getLogic());
- assertEquals(DecisionStrategy.AFFIRMATIVE, resource.getDecisionStrategy());
- assertEquals("changed configuration for A", resource.getConfig().get("configA"));
- assertNull(resource.getConfig().get("configB"));
- assertEquals("changed configuration for C", resource.getConfig().get("configC"));
+ assertEquals(DecisionStrategy.AFFIRMATIVE, policy.getDecisionStrategy());
+ assertEquals("changed configuration for A", policy.getConfig().get("configA"));
+ assertNull(policy.getConfig().get("configB"));
+ assertEquals("changed configuration for C", policy.getConfig().get("configC"));
- Map<String, String> config = resource.getConfig();
+ Map<String, String> config = policy.getConfig();
config.put("applyPolicies", buildConfigOption(findPolicyByName("Test Associated C").getId()));
@@ -127,22 +128,25 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
config.put("scopes", buildConfigOption(findScopeByName("Test Scope A").getId()));
- policyResource.update(resource);
+ policyResource.update(policy);
- resource = policyResource.toRepresentation();
- config = resource.getConfig();
+ policy = policyResource.toRepresentation();
+ config = policy.getConfig();
- assertAssociatedPolicy("Test Associated C", resource);
- assertFalse(config.get("applyPolicies").contains(findPolicyByName("Test Associated A").getId()));
- assertFalse(config.get("applyPolicies").contains(findPolicyByName("Test Associated B").getId()));
+ assertAssociatedPolicy("Test Associated C", policy);
+ List<PolicyRepresentation> associatedPolicies = getClientResource().authorization().policies().policy(policy.getId()).associatedPolicies();
+ assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated A").getId())).findFirst().isPresent());
+ assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated B").getId())).findFirst().isPresent());
- assertAssociatedResource("Test Resource B", resource);
- assertFalse(config.get("resources").contains(findResourceByName("Test Resource A").getId()));
- assertFalse(config.get("resources").contains(findResourceByName("Test Resource C").getId()));
+ assertAssociatedResource("Test Resource B", policy);
+ List<ResourceRepresentation> resources = policyResource.resources();
+ assertFalse(resources.contains(findResourceByName("Test Resource A")));
+ assertFalse(resources.contains(findResourceByName("Test Resource C")));
- assertAssociatedScope("Test Scope A", resource);
- assertFalse(config.get("scopes").contains(findScopeByName("Test Scope B").getId()));
- assertFalse(config.get("scopes").contains(findScopeByName("Test Scope C").getId()));
+ assertAssociatedScope("Test Scope A", policy);
+ List<ScopeRepresentation> scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
+ assertFalse(scopes.contains(findScopeByName("Test Scope B").getId()));
+ assertFalse(scopes.contains(findScopeByName("Test Scope C").getId()));
}
@Test
@@ -186,7 +190,7 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
PolicyRepresentation newPolicy = new PolicyRepresentation();
newPolicy.setName(name);
- newPolicy.setType("test");
+ newPolicy.setType("scope");
newPolicy.setConfig(config);
PoliciesResource policies = getClientResource().authorization().policies();
@@ -264,27 +268,38 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
private void assertAssociatedPolicy(String associatedPolicyName, PolicyRepresentation dependentPolicy) {
PolicyRepresentation associatedPolicy = findPolicyByName(associatedPolicyName);
+ PoliciesResource policies = getClientResource().authorization().policies();
+ associatedPolicy = policies.policy(associatedPolicy.getId()).toRepresentation();
assertNotNull(associatedPolicy);
- assertTrue(dependentPolicy.getConfig().get("applyPolicies").contains(associatedPolicy.getId()));
- assertEquals(1, associatedPolicy.getDependentPolicies().size());
- assertEquals(dependentPolicy.getId(), associatedPolicy.getDependentPolicies().get(0).getId());
+ PolicyRepresentation finalAssociatedPolicy = associatedPolicy;
+ PolicyResource policyResource = policies.policy(dependentPolicy.getId());
+ List<PolicyRepresentation> associatedPolicies = policyResource.associatedPolicies();
+ assertTrue(associatedPolicies.stream().filter(associated -> associated.getId().equals(finalAssociatedPolicy.getId())).findFirst().isPresent());
+ List<PolicyRepresentation> dependentPolicies = policies.policy(associatedPolicy.getId()).dependentPolicies();
+ assertEquals(1, dependentPolicies.size());
+ assertEquals(dependentPolicy.getId(), dependentPolicies.get(0).getId());
}
private void assertAssociatedResource(String resourceName, PolicyRepresentation policy) {
ResourceRepresentation resource = findResourceByName(resourceName);
assertNotNull(resource);
- assertTrue(policy.getConfig().get("resources").contains(resource.getId()));
- assertEquals(1, resource.getPolicies().size());
- assertTrue(resource.getPolicies().stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
+ List<ResourceRepresentation> resources = getClientResource().authorization().policies().policy(policy.getId()).resources();
+ assertTrue(resources.contains(resource));
+ List<PolicyRepresentation> policies = getClientResource().authorization().resources().resource(resource.getId()).permissions();
+ assertEquals(1, policies.size());
+ assertTrue(policies.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
.contains(policy.getId()));
}
private void assertAssociatedScope(String scopeName, PolicyRepresentation policy) {
ScopeRepresentation scope = findScopeByName(scopeName);
+ scope = getClientResource().authorization().scopes().scope(scope.getId()).toRepresentation();
assertNotNull(scope);
- assertTrue(policy.getConfig().get("scopes").contains(scope.getId()));
- assertEquals(1, scope.getPolicies().size());
- assertTrue(scope.getPolicies().stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
+ List<ScopeRepresentation> scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
+ assertTrue(scopes.stream().map((Function<ScopeRepresentation, String>) rep -> rep.getId()).collect(Collectors.toList()).contains(scope.getId()));
+ List<PolicyRepresentation> permissions = getClientResource().authorization().scopes().scope(scope.getId()).permissions();
+ assertEquals(1, permissions.size());
+ assertTrue(permissions.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
.contains(policy.getId()));
}
}
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 88dbfcd..5380f17 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -1036,6 +1036,10 @@ authz-result=Result
authz-authorization-services-enabled=Authorization Enabled
authz-authorization-services-enabled.tooltip=Enable/Disable fine-grained authorization support for a client
authz-required=Required
+authz-show-details=Show Details
+authz-hide-details=Hide Details
+authz-associated-permissions=Associated Permissions
+authz-no-permission-associated=No permissions associated
# Authz Settings
authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server.
@@ -1056,6 +1060,7 @@ authz-export-settings.tooltip=Export and download all authorization settings for
authz-no-resources-available=No resources available.
authz-no-scopes-assigned=No scopes assigned.
authz-no-type-defined=No type defined.
+authz-no-uri-defined=No URI defined.
authz-no-permission-assigned=No permission assigned.
authz-no-policy-assigned=No policy assigned.
authz-create-permission=Create permission
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index ea039c1..2a86b93 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -86,10 +86,13 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route,
$scope.query = {
realm: realm.realm,
client : client.id,
+ deep: false,
max : 20,
first : 0
};
+ $scope.listSizes = [5, 10, 20];
+
ResourceServer.get({
realm : $route.current.params.realm,
client : client.id
@@ -124,10 +127,49 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route,
$scope.searchQuery = function() {
$scope.searchLoaded = false;
- $scope.resources = ResourceServerResource.query($scope.query, function() {
+ ResourceServerResource.query($scope.query, function(response) {
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
+ $scope.resources = response;
+ if ($scope.detailsFilter) {
+ $scope.showDetails();
+ }
+ });
+ };
+
+ $scope.loadDetails = function (resource) {
+ if (resource.details) {
+ resource.details.loaded = !resource.details.loaded;
+ return;
+ }
+
+ resource.details = {loaded: false};
+
+ ResourceServerResource.scopes({
+ realm : $route.current.params.realm,
+ client : client.id,
+ rsrid : resource._id
+ }, function(response) {
+ resource.scopes = response;
+ ResourceServerResource.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ rsrid : resource._id
+ }, function(response) {
+ resource.policies = response;
+ resource.details.loaded = true;
+ });
});
+ }
+
+ $scope.showDetails = function(item) {
+ if (item) {
+ $scope.loadDetails(item);
+ } else {
+ for (i = 0; i < $scope.resources.length; i++) {
+ $scope.loadDetails($scope.resources[i]);
+ }
+ }
};
});
@@ -135,9 +177,36 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
$scope.realm = realm;
$scope.client = client;
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.scopes = data;
- });
+ $scope.scopesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerScope.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ return object.name;
+ },
+ formatSelection: function(object, container, query) {
+ return object.name;
+ }
+ };
var $instance = this;
@@ -165,6 +234,9 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
}, true);
$scope.save = function() {
+ for (i = 0; i < $scope.resource.scopes.length; i++) {
+ delete $scope.resource.scopes[i].text;
+ }
$instance.checkNameAvailability(function () {
ResourceServerResource.save({realm : realm.realm, client : $scope.client.id}, $scope.resource, function(data) {
$location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource/" + data._id);
@@ -186,17 +258,9 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
data.scopes = [];
}
- if (!data.policies) {
- data.policies = [];
- }
-
$scope.resource = angular.copy(data);
$scope.changed = false;
- for (i = 0; i < $scope.resource.scopes.length; i++) {
- $scope.resource.scopes[i] = $scope.resource.scopes[i].name;
- }
-
$scope.originalResource = angular.copy($scope.resource);
$scope.$watch('resource', function() {
@@ -206,6 +270,9 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
}, true);
$scope.save = function() {
+ for (i = 0; i < $scope.resource.scopes.length; i++) {
+ delete $scope.resource.scopes[i].text;
+ }
$instance.checkNameAvailability(function () {
ResourceServerResource.update({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, $scope.resource, function() {
$route.reload();
@@ -215,22 +282,28 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
}
$scope.remove = function() {
- var msg = "";
-
- if ($scope.resource.policies.length > 0) {
- msg = "<p>This resource is referenced in some policies:</p>";
- msg += "<ul>";
- for (i = 0; i < $scope.resource.policies.length; i++) {
- msg+= "<li><strong>" + $scope.resource.policies[i].name + "</strong></li>";
+ ResourceServerResource.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ rsrid : $scope.resource._id
+ }, function (permissions) {
+ var msg = "";
+
+ if (permissions.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This resource is referenced in some policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < permissions.length; i++) {
+ msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
}
- msg += "</ul>";
- msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
- }
- AuthzDialog.confirmDeleteWithMsg($scope.resource.name, "Resource", msg, function() {
- ResourceServerResource.delete({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, null, function() {
- $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource");
- Notifications.success("The resource has been deleted.");
+ AuthzDialog.confirmDeleteWithMsg($scope.resource.name, "Resource", msg, function() {
+ ResourceServerResource.delete({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, null, function() {
+ $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource");
+ Notifications.success("The resource has been deleted.");
+ });
});
});
}
@@ -269,10 +342,13 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo
$scope.query = {
realm: realm.realm,
client : client.id,
+ deep: false,
max : 20,
first : 0
};
+ $scope.listSizes = [5, 10, 20];
+
ResourceServer.get({
realm : $route.current.params.realm,
client : client.id
@@ -304,14 +380,53 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo
$scope.searchQuery();
}
- $scope.searchQuery = function() {
+ $scope.searchQuery = function(detailsFilter) {
$scope.searchLoaded = false;
- $scope.scopes = ResourceServerScope.query($scope.query, function() {
+ ResourceServerScope.query($scope.query, function(response) {
+ $scope.scopes = response;
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
+ if ($scope.detailsFilter) {
+ $scope.showDetails();
+ }
});
};
+
+ $scope.loadDetails = function (scope) {
+ if (scope.details) {
+ scope.details.loaded = !scope.details.loaded;
+ return;
+ }
+
+ scope.details = {loaded: false};
+
+ ResourceServerScope.resources({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : scope.id
+ }, function(response) {
+ scope.resources = response;
+ ResourceServerScope.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : scope.id
+ }, function(response) {
+ scope.policies = response;
+ scope.details.loaded = true;
+ });
+ });
+ }
+
+ $scope.showDetails = function(item) {
+ if (item) {
+ $scope.loadDetails(item);
+ } else {
+ for (i = 0; i < $scope.scopes.length; i++) {
+ $scope.loadDetails($scope.scopes[i]);
+ }
+ }
+ };
});
module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) {
@@ -377,22 +492,28 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
}
$scope.remove = function() {
- var msg = "";
-
- if ($scope.scope.policies.length > 0) {
- msg = "<p>This resource is referenced in some policies:</p>";
- msg += "<ul>";
- for (i = 0; i < $scope.scope.policies.length; i++) {
- msg+= "<li><strong>" + $scope.scope.policies[i].name + "</strong></li>";
+ ResourceServerScope.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : $scope.scope.id
+ }, function (permissions) {
+ var msg = "";
+
+ if (permissions.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This scope is referenced in some policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < permissions.length; i++) {
+ msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this scope, the policies above will be affected and will not be associated with this scope anymore.</p>";
}
- msg += "</ul>";
- msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
- }
- AuthzDialog.confirmDeleteWithMsg($scope.scope.name, "Scope", msg, function() {
- ResourceServerScope.delete({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, null, function() {
- $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope");
- Notifications.success("The scope has been deleted.");
+ AuthzDialog.confirmDeleteWithMsg($scope.scope.name, "Scope", msg, function() {
+ ResourceServerScope.delete({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, null, function() {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope");
+ Notifications.success("The scope has been deleted.");
+ });
});
});
}
@@ -432,10 +553,12 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
realm: realm.realm,
client : client.id,
permission: false,
- max : 20,
+ max: 20,
first : 0
};
+ $scope.listSizes = [5, 10, 20];
+
PolicyProvider.query({
realm : $route.current.params.realm,
client : client.id
@@ -481,17 +604,41 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
$scope.searchLoaded = false;
ResourceServerPolicy.query($scope.query, function(data) {
- $scope.policies = [];
-
- for (i = 0; i < data.length; i++) {
- if (data[i].type != 'resource' && data[i].type != 'scope') {
- $scope.policies.push(data[i]);
- }
- }
-
+ $scope.policies = data;
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
+ if ($scope.detailsFilter) {
+ $scope.showDetails();
+ }
+ });
+ };
+
+ $scope.loadDetails = function (policy) {
+ if (policy.details) {
+ policy.details.loaded = !policy.details.loaded;
+ return;
+ }
+
+ policy.details = {loaded: false};
+
+ ResourceServerPolicy.dependentPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(response) {
+ policy.dependentPolicies = response;
+ policy.details.loaded = true;
});
+ }
+
+ $scope.showDetails = function(item) {
+ if (item) {
+ $scope.loadDetails(item);
+ } else {
+ for (i = 0; i < $scope.policies.length; i++) {
+ $scope.loadDetails($scope.policies[i]);
+ }
+ }
};
});
@@ -508,6 +655,8 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
first : 0
};
+ $scope.listSizes = [5, 10, 20];
+
PolicyProvider.query({
realm : $route.current.params.realm,
client : client.id
@@ -553,18 +702,42 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
$scope.searchLoaded = false;
ResourceServerPolicy.query($scope.query, function(data) {
- $scope.policies = [];
-
- for (i = 0; i < data.length; i++) {
- if (data[i].type == 'resource' || data[i].type == 'scope') {
- $scope.policies.push(data[i]);
- }
- }
-
+ $scope.policies = data;
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
+ if ($scope.detailsFilter) {
+ $scope.showDetails();
+ }
});
};
+
+ $scope.loadDetails = function (policy) {
+ if (policy.details) {
+ policy.details.loaded = !policy.details.loaded;
+ return;
+ }
+
+ policy.details = {loaded: false};
+
+ ResourceServerPolicy.associatedPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(response) {
+ policy.associatedPolicies = response;
+ policy.details.loaded = true;
+ });
+ }
+
+ $scope.showDetails = function(item) {
+ if (item) {
+ $scope.loadDetails(item);
+ } else {
+ for (i = 0; i < $scope.policies.length; i++) {
+ $scope.loadDetails($scope.policies[i]);
+ }
+ }
+ };
});
module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http, $route, realm, client, PolicyController) {
@@ -623,19 +796,64 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
},
onInit : function() {
- ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.resources = data;
- });
-
- ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.policies = [];
+ $scope.resourcesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ id: function(resource){ return resource._id; },
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerResource.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
+ }
+ };
- for (i = 0; i < data.length; i++) {
- if (data[i].type != 'resource' && data[i].type != 'scope') {
- $scope.policies.push(data[i]);
+ $scope.policiesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
}
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ permission: false,
+ name: query.term.trim(),
+ max : 20,
+ first : 0
+ };
+ ResourceServerPolicy.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
}
- });
+ };
$scope.applyToResourceType = function() {
if ($scope.policy.config.default) {
@@ -648,30 +866,69 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
onInitUpdate : function(policy) {
policy.config.default = eval(policy.config.default);
- policy.config.resources = eval(policy.config.resources);
- policy.config.applyPolicies = eval(policy.config.applyPolicies);
+ policy.config.resources = {};
+ ResourceServerPolicy.resources({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(resources) {
+ resources[0].text = resources[0].name;
+ $scope.policy.config.resources = resources[0];
+ });
+
+ policy.config.applyPolicies = [];
+ ResourceServerPolicy.associatedPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(policies) {
+ for (i = 0; i < policies.length; i++) {
+ policies[i].text = policies[i].name;
+ $scope.policy.config.applyPolicies.push(policies[i]);
+ }
+ });
},
onUpdate : function() {
- $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+ $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
+ }
+
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
},
onInitCreate : function(newPolicy) {
newPolicy.decisionStrategy = 'UNANIMOUS';
newPolicy.config = {};
- newPolicy.config.resources = '';
+ newPolicy.config.resources = null;
var resourceId = $location.search()['rsrid'];
if (resourceId) {
- newPolicy.config.resources = [resourceId];
+ ResourceServerResource.get({
+ realm : $route.current.params.realm,
+ client : client.id,
+ rsrid : resourceId
+ }, function(data) {
+ data.text = data.name;
+ $scope.policy.config.resources = data;
+ });
}
},
onCreate : function() {
- $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+ $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
+ }
+
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
}
}, realm, client, $scope);
});
@@ -687,105 +944,241 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
},
onInit : function() {
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.scopes = data;
- });
-
- ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.resources = data;
- });
-
- ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.policies = [];
-
- for (i = 0; i < data.length; i++) {
- if (data[i].type != 'resource' && data[i].type != 'scope') {
- $scope.policies.push(data[i]);
+ $scope.scopesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
}
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerScope.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
}
- });
-
- $scope.resolveScopes = function(policy, keepScopes) {
- if (!keepScopes) {
- policy.config.scopes = [];
- }
+ };
- if (!policy) {
- policy = $scope.policy;
+ $scope.resourcesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ id: function(resource){ return resource._id; },
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerResource.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
}
+ };
- if (policy.config.resources != null) {
- ResourceServerResource.get({
- realm : $route.current.params.realm,
+ $scope.policiesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
client : client.id,
- rsrid : policy.config.resources
- }, function(data) {
- $scope.scopes = data.scopes;
+ permission: false,
+ name: query.term.trim(),
+ max : 20,
+ first : 0
+ };
+ ResourceServerPolicy.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
});
- } else {
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.scopes = data;
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
+ }
+ };
+
+ $scope.selectResource = function() {
+ if ($scope.policy.config.resources) {
+ ResourceServerResource.scopes({
+ realm: $route.current.params.realm,
+ client: client.id,
+ rsrid: $scope.policy.config.resources._id
+ }, function (data) {
+ $scope.policy.config.resources.scopes = data;
});
}
}
},
onInitUpdate : function(policy) {
- if (policy.config.resources) {
- policy.config.resources = eval(policy.config.resources);
+ policy.config.resources = eval(policy.config.resources);
- if (policy.config.resources.length > 0) {
- policy.config.resources = policy.config.resources[0];
- } else {
- policy.config.resources = null;
- }
+ if (policy.config.resources == null) {
+ policy.config.resources = [];
}
- $scope.resolveScopes(policy, true);
+ if (policy.config.resources.length > 0) {
+ ResourceServerResource.query({
+ realm: $route.current.params.realm,
+ client: client.id,
+ _id: policy.config.resources[0],
+ deep: false
+ }, function (data) {
+ data[0].text = data[0].name;
+ $scope.policy.config.resources = data[0];
+ ResourceServerResource.scopes({
+ realm: $route.current.params.realm,
+ client: client.id,
+ rsrid: policy.config.resources[0]
+ }, function (data) {
+ $scope.policy.config.resources.scopes = data;
+ });
+ ResourceServerPolicy.scopes({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(scopes) {
+ $scope.policy.config.scopes = [];
+ for (i = 0; i < scopes.length; i++) {
+ $scope.policy.config.scopes.push(scopes[i].id);
+ }
+ });
+ });
+ } else {
+ policy.config.resources = null;
+ ResourceServerPolicy.scopes({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(scopes) {
+ $scope.policy.config.scopes = [];
+ for (i = 0; i < scopes.length; i++) {
+ scopes[i].text = scopes[i].name;
+ $scope.policy.config.scopes.push(scopes[i]);
+ }
+ });
+ }
- policy.config.applyPolicies = eval(policy.config.applyPolicies);
- policy.config.scopes = eval(policy.config.scopes);
+ policy.config.applyPolicies = [];
+ ResourceServerPolicy.associatedPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(policies) {
+ for (i = 0; i < policies.length; i++) {
+ policies[i].text = policies[i].name;
+ $scope.policy.config.applyPolicies.push(policies[i]);
+ }
+ });
},
onUpdate : function() {
if ($scope.policy.config.resources != null) {
- var resources = undefined;
+ $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ }
- if ($scope.policy.config.resources.length != 0) {
- resources = JSON.stringify([$scope.policy.config.resources])
+ var scopes = [];
+
+ for (i = 0; i < $scope.policy.config.scopes.length; i++) {
+ if ($scope.policy.config.resources == null) {
+ scopes.push($scope.policy.config.scopes[i].id);
+ } else {
+ scopes.push($scope.policy.config.scopes[i]);
}
+ }
- $scope.policy.config.resources = resources;
+ $scope.policy.config.scopes = JSON.stringify(scopes);
+
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
}
- $scope.policy.config.scopes = JSON.stringify($scope.policy.config.scopes);
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
},
onInitCreate : function(newPolicy) {
newPolicy.decisionStrategy = 'UNANIMOUS';
newPolicy.config = {};
- newPolicy.config.resources = '';
+ newPolicy.config.resources = null;
var scopeId = $location.search()['scpid'];
if (scopeId) {
- newPolicy.config.scopes = [scopeId];
+ ResourceServerScope.get({
+ realm: $route.current.params.realm,
+ client: client.id,
+ id: scopeId,
+ }, function (data) {
+ data.text = data.name;
+ if (!$scope.policy.config.scopes) {
+ $scope.policy.config.scopes = [];
+ }
+ $scope.policy.config.scopes.push(data);
+ });
}
},
onCreate : function() {
if ($scope.policy.config.resources != null) {
- var resources = undefined;
+ $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ }
+
+ var scopes = [];
- if ($scope.policy.config.resources.length != 0) {
- resources = JSON.stringify([$scope.policy.config.resources])
+ for (i = 0; i < $scope.policy.config.scopes.length; i++) {
+ if ($scope.policy.config.scopes[i].id) {
+ scopes.push($scope.policy.config.scopes[i].id);
+ } else {
+ scopes.push($scope.policy.config.scopes[i]);
}
+ }
+
+ $scope.policy.config.scopes = JSON.stringify(scopes);
- $scope.policy.config.resources = resources;
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
}
- $scope.policy.config.scopes = JSON.stringify($scope.policy.config.scopes);
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
}
}, realm, client, $scope);
});
@@ -1250,23 +1643,58 @@ module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $r
},
onInit : function() {
- ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.policies = [];
-
- for (i = 0; i < data.length; i++) {
- if (data[i].type != 'resource' && data[i].type != 'scope') {
- $scope.policies.push(data[i]);
+ $scope.policiesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
}
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ permission: false,
+ name: query.term.trim(),
+ max : 20,
+ first : 0
+ };
+ ResourceServerPolicy.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
}
- });
+ };
},
onInitUpdate : function(policy) {
- policy.config.applyPolicies = eval(policy.config.applyPolicies);
+ policy.config.applyPolicies = [];
+ ResourceServerPolicy.associatedPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(policies) {
+ for (i = 0; i < policies.length; i++) {
+ policies[i].text = policies[i].name;
+ $scope.policy.config.applyPolicies.push(policies[i]);
+ }
+ });
},
onUpdate : function() {
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
+ }
+
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
},
onInitCreate : function(newPolicy) {
@@ -1275,7 +1703,13 @@ module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $r
},
onCreate : function() {
- $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+ var policies = [];
+
+ for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
+ policies.push($scope.policy.config.applyPolicies[i].id);
+ }
+
+ $scope.policy.config.applyPolicies = JSON.stringify(policies);
}
}, realm, client, $scope);
});
@@ -1357,9 +1791,9 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
}
} else {
ResourceServerPolicy.get({
- realm : $route.current.params.realm,
+ realm: realm.realm,
client : client.id,
- id : $route.current.params.id,
+ id: $route.current.params.id
}, function(data) {
$scope.originalPolicy = data;
var policy = angular.copy(data);
@@ -1408,25 +1842,31 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
$scope.remove = function() {
var msg = "";
- if ($scope.policy.dependentPolicies.length > 0) {
- msg = "<p>This policy is being used by other policies:</p>";
- msg += "<ul>";
- for (i = 0; i < $scope.policy.dependentPolicies.length; i++) {
- msg+= "<li><strong>" + $scope.policy.dependentPolicies[i].name + "</strong></li>";
+ ResourceServerPolicy.dependentPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : $scope.policy.id
+ }, function (dependentPolicies) {
+ if (dependentPolicies.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This policy is being used by other policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < dependentPolicies.length; i++) {
+ msg+= "<li><strong>" + dependentPolicies[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this policy, the policies above will be affected and will not be associated with this policy anymore.</p>";
}
- msg += "</ul>";
- msg += "<p>If you remove this policy, the policies above will be affected and will not be associated with this policy anymore.</p>";
- }
- AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() {
- ResourceServerPolicy.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
- if (delegate.isPermission()) {
- $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission");
- Notifications.success("The permission has been deleted.");
- } else {
- $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy");
- Notifications.success("The policy has been deleted.");
- }
+ AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() {
+ ResourceServerPolicy.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
+ if (delegate.isPermission()) {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission");
+ Notifications.success("The permission has been deleted.");
+ } else {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy");
+ Notifications.success("The policy has been deleted.");
+ }
+ });
});
});
}
@@ -1465,13 +1905,8 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
$scope.authzRequest.context = {};
$scope.authzRequest.context.attributes = {};
$scope.authzRequest.roleIds = [];
- $scope.newResource = {};
$scope.resultUrl = resourceUrl + '/partials/authz/policy/resource-server-policy-evaluate-result.html';
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.scopes = data;
- });
-
$scope.addContextAttribute = function() {
if (!$scope.newContextAttribute.value || $scope.newContextAttribute.value == '') {
Notifications.error("You must provide a value to a context attribute.");
@@ -1573,33 +2008,39 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
}
$scope.setApplyToResourceType = function() {
- if ($scope.applyResourceType) {
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.scopes = data;
- });
- }
-
delete $scope.newResource;
$scope.authzRequest.resources = [];
}
$scope.addResource = function() {
- var resource = {};
+ var resource = angular.copy($scope.newResource);
+
+ if (!resource) {
+ resource = {};
+ }
+
+ delete resource.text;
+
+ if (!$scope.newScopes || (resource._id != null && $scope.newScopes.length > 0 && $scope.newScopes[0].id)) {
+ $scope.newScopes = [];
+ }
- resource.id = $scope.newResource._id;
+ var scopes = [];
- for (i = 0; i < $scope.resources.length; i++) {
- if ($scope.resources[i]._id == resource.id) {
- resource.name = $scope.resources[i].name;
- break;
+ for (i = 0; i < $scope.newScopes.length; i++) {
+ if ($scope.newScopes[i].name) {
+ scopes.push($scope.newScopes[i].name);
+ } else {
+ scopes.push($scope.newScopes[i]);
}
}
- resource.scopes = $scope.newResource.scopes;
+ resource.scopes = scopes;
$scope.authzRequest.resources.push(resource);
delete $scope.newResource;
+ delete $scope.newScopes;
}
$scope.removeResource = function(index) {
@@ -1610,20 +2051,11 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
if ($scope.newResource._id) {
$scope.newResource.scopes = [];
$scope.scopes = [];
- ResourceServerResource.get({
+ ResourceServerResource.scopes({
realm: $route.current.params.realm,
- client : client.id,
+ client: client.id,
rsrid: $scope.newResource._id
}, function (data) {
- $scope.scopes = data.scopes;
- if (data.typedScopes) {
- for (i=0;i<data.typedScopes.length;i++) {
- $scope.scopes.push(data.typedScopes[i]);
- }
- }
- });
- } else {
- ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
$scope.scopes = data;
});
}
@@ -1647,7 +2079,17 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
if (!$scope.newResource) {
$scope.newResource = {};
}
- $scope.authzRequest.resources[0].scopes = $scope.newResource.scopes;
+ if (!$scope.newScopes || ($scope.newResource._id != null && $scope.newScopes.length > 0 && $scope.newScopes[0].id)) {
+ $scope.newScopes = [];
+ }
+
+ var scopes = angular.copy($scope.newScopes);
+
+ for (i = 0; i < scopes.length; i++) {
+ delete scopes[i].text;
+ }
+
+ $scope.authzRequest.resources[0].scopes = scopes;
}
$http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/evaluate'
@@ -1697,9 +2139,64 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
}
};
- ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
- $scope.resources = data;
- });
+ $scope.resourcesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ id: function(resource){ return resource._id; },
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerResource.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
+ }
+ };
+
+ $scope.scopesUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ $scope.query = {
+ realm: realm.realm,
+ client : client.id,
+ name: query.term.trim(),
+ deep: false,
+ max : 20,
+ first : 0
+ };
+ ResourceServerScope.query($scope.query, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.name;
+ return object.name;
+ }
+ };
ResourceServer.get({
realm : $route.current.params.realm,
@@ -1717,5 +2214,4 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
$scope.authzRequest.userId = user.id;
}
-
});
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
index 795cf1d..1c4f584 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
@@ -16,7 +16,9 @@ module.factory('ResourceServerResource', function($resource) {
rsrid : '@rsrid'
}, {
'update' : {method : 'PUT'},
- 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/search', method : 'GET'}
+ 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/search', method : 'GET'},
+ 'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid/scopes', method : 'GET', isArray: true},
+ 'permissions' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid/permissions', method : 'GET', isArray: true}
});
});
@@ -27,7 +29,9 @@ module.factory('ResourceServerScope', function($resource) {
id : '@id'
}, {
'update' : {method : 'PUT'},
- 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/search', method : 'GET'}
+ 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/search', method : 'GET'},
+ 'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/:id/resources', method : 'GET', isArray: true},
+ 'permissions' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/:id/permissions', method : 'GET', isArray: true},
});
});
@@ -38,7 +42,11 @@ module.factory('ResourceServerPolicy', function($resource) {
id : '@id'
}, {
'update' : {method : 'PUT'},
- 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'}
+ 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'},
+ 'associatedPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/associatedPolicies', method : 'GET', isArray: true},
+ 'dependentPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/dependentPolicies', method : 'GET', isArray: true},
+ 'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/scopes', method : 'GET', isArray: true},
+ 'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/resources', method : 'GET', isArray: true}
});
});
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
index a8d4512..10e3170 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
@@ -39,9 +39,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2="{ minimumInputLength: 1}" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-select-resource' | translate}}..." multiple data-ng-required="!policy.config.default">
- <option ng-repeat="resource in resources" value="{{resource._id}}" ng-selected="true">{{resource.name}}</option>
- </select>
+ <input type="hidden" ui-select2="resourcesUiSelect" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!policy.config.default"/>
</div>
<kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
</div>
@@ -58,9 +56,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
- <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
- </select>
+ <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
</div>
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
index 3d1660b..90a3dc6 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
@@ -32,12 +32,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resource' | translate}}</label>
<div class="col-md-6">
- <select class="form-control" id="reqActions"
- ng-model="policy.config.resources"
- ng-change="resolveScopes(policy)"
- data-ng-options="resource._id as resource.name for resource in resources">
- <option value="">{{:: 'authz-any-resource' | translate}}...</option>
- </select>
+ <input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
</div>
<kc-tooltip>{{:: 'authz-permission-scope-resource.tooltip' | translate}}</kc-tooltip>
</div>
@@ -45,25 +40,19 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2 id="reqActions"
+ <select ui-select2 id="reqActions2"
data-ng-model="policy.config.scopes"
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
- data-ng-required="policy.config.resources != ''"
- data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
+ data-ng-required="policy.config.resources != null"
+ data-ng-options="scope.id as scope.name for scope in policy.config.resources.scopes track by scope.id"/>
</div>
-
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-show="!policy.config.resources">
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2="{ minimumInputLength: 1}" id="reqActions"
- data-ng-model="policy.config.scopes"
- data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
- data-ng-required="policy.config.resources == ''"
- data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
- </select>
+ <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="policy.config.scopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="policy.config.resources == null" />
</div>
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
</div>
@@ -71,9 +60,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
- <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
- </select>
+ <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
</div>
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
@@ -95,7 +82,6 @@
</div>
<input type="hidden" data-ng-model="policy.type"/>
</fieldset>
-
<div class="form-group" data-ng-show="access.manageAuthorization">
<div class="col-md-10 col-md-offset-2">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
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 bfd1b22..419622f 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
@@ -28,6 +28,12 @@
</select>
</div>
</div>
+ <div class="input-group">
+ <select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
+ <option value="" selected>Hide Details</option>
+ <option value="true">Show Details</option>
+ </select>
+ </div>
<div class="pull-right">
<select class="form-control" ng-model="policyType"
ng-options="p.name for p in policyProviders track by p.type"
@@ -42,7 +48,7 @@
<th>{{:: 'name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th>{{:: 'type' | translate}}</th>
- <th>{{:: 'authz-associated-policies' | translate}}</th>
+ <th>{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
@@ -52,22 +58,48 @@
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
+ <select class="first" data-ng-model="query.max"
+ ng-options="size for size in listSizes" data-ng-change="firstPage()">
+ </select>
</div>
</td>
</tr>
</tfoot>
<tbody>
- <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
+ <tr ng-repeat-start="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
<td>{{policy.description}}</td>
<td>{{policy.type}}</td>
- <td>
- <span data-ng-show="!policy.associatedPolicies.length">{{:: 'authz-no-policy-assigned' | translate}}</span>
- <span data-ng-show="policy.associatedPolicies.length > 0">
- <span ng-repeat="policy in policy.associatedPolicies">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
- </span>
- </span>
+ <td ng-if="!policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
+ {{:: 'authz-show-details' | translate}}
+ </td>
+ <td ng-if="policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
+ {{:: 'authz-hide-details' | translate}}
+ </td>
+ </tr>
+ <tr ng-if="policy.details && policy.details.loaded" ng-repeat-end="">
+ <td colspan="4">
+ <div id="details">
+ <table class="table kc-authz-table-expanded table-striped">
+ <thead>
+ <tr>
+ <th>Associated Permissions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <span data-ng-show="policy.associatedPolicies && !policy.associatedPolicies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
+ <ul ng-repeat="dep in policy.associatedPolicies" data-ng-show="policy.associatedPolicies.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{dep.type}}/{{dep.id}}">{{dep.name}}</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</td>
</tr>
<tr data-ng-show="(policies | filter:search).length == 0">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
index 7888f21..2544d3c 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
@@ -34,11 +34,8 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
- <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
- </select>
+ <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
</div>
-
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html
index d884150..dbaedd3 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html
@@ -172,14 +172,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <select ui-select2="{ minimumInputLength: 1, allowClear:true }"
- ng-model="newResource._id"
- data-placeholder="Select a resource..."
- data-ng-required="!applyResourceType && authzRequest.resources.length == 0 && !authzRequest.entitlements"
- data-ng-click="resolveScopes()"
- ng-options="resource._id as resource.name for resource in resources track by resource._id">
- <option value=""></option>
- </select>
+ <input type="hidden" ui-select2="resourcesUiSelect" id="reqActions3" data-ng-change="resolveScopes()" data-ng-model="newResource" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!applyResourceType && authzRequest.resources.length == 0 && !authzRequest.entitlements" />
</div>
<kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
</div>
@@ -199,12 +192,7 @@
<label class="col-md-2 control-label" for="newResource.scopes">{{:: 'authz-scopes' | translate}}</label>
<div class="col-md-6">
- <select ui-select2="{ minimumInputLength: 1}"
- id="newResource.scopes"
- multiple
- data-ng-model="newResource.scopes"
- data-placeholder="{{:: 'authz-select-scope' | translate}}..."
- data-ng-options="scope.name as scope.name for scope in scopes track by scope.name"/>
+ <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="newScopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple />
</div>
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
@@ -215,7 +203,7 @@
<div class="col-md-6">
<select ui-select2
id="newResource.scopes"
- data-ng-model="newResource.scopes"
+ data-ng-model="newScopes"
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple>
<option ng-repeat="scope in scopes" value="{{scope.name}}">{{scope.name}}</option>
</select>
@@ -246,11 +234,11 @@
<td>{{resource.name ? resource.name : 'authz-evaluation-any-resource-with-scopes' | translate}}</td>
<td>
<span data-ng-show="!resource.scopes.length">{{:: 'authz-any-scope' | translate}}.</span>
- <span data-ng-show="resource.scopes.length > 0">
- <span ng-repeat="scope in resource.scopes">
- {{scope}} {{$last ? '' : ', '}}
+ <span data-ng-show="resource.scopes.length > 0">
+ <span ng-repeat="scope in resource.scopes">
+ {{scope.name ? scope.name : scope}} {{$last ? '' : ', '}}
+ </span>
</span>
- </span>
</td>
<td class="kc-action-cell">
<button class="btn btn-default btn-block btn-sm"
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
index 2574f2d..9ad378a 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
@@ -27,8 +27,16 @@
<option value="" selected ng-click="query.type = ''">{{:: 'authz-all-types' | translate}}</option>
</select>
</div>
+ <div class="input-group">
+ <select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
+ <option value="" selected>Hide Details</option>
+ <option value="true">Show Details</option>
+ </select>
+ </div>
</div>
<div class="pull-right">
+ <a id="hideDetails" data-ng-show="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-hide-details' | translate}}</a>
+ <a id="showDetails" data-ng-hide="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-show-details' | translate}}</a>
<select class="form-control" ng-model="policyType"
ng-options="p.name for p in policyProviders track by p.type"
data-ng-change="addPolicy(policyType);">
@@ -42,6 +50,7 @@
<th>{{:: 'name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th>{{:: 'type' | translate}}</th>
+ <th>{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
@@ -51,15 +60,49 @@
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
+ <select class="first" data-ng-model="query.max"
+ ng-options="size for size in listSizes" data-ng-change="firstPage()">
+ </select>
</div>
</td>
</tr>
</tfoot>
<tbody>
- <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
+ <tr ng-repeat-start="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
<td>{{policy.description}}</td>
<td>{{policy.type}}</td>
+ <td ng-if="!policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
+ {{:: 'authz-show-details' | translate}}
+ </td>
+ <td ng-if="policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
+ {{:: 'authz-hide-details' | translate}}
+ </td>
+ </tr>
+ <tr ng-if="policy.details && policy.details.loaded" ng-repeat-end="">
+ <td colspan="4">
+ <div id="details">
+ <table class="table kc-authz-table-expanded table-striped">
+ <thead>
+ <tr>
+ <th>Dependent Permissions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <span data-ng-show="policy.dependentPolicies && !policy.dependentPolicies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
+ <ul ng-repeat="dep in policy.dependentPolicies" data-ng-show="policy.dependentPolicies.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{dep.type}}/{{dep.id}}">{{dep.name}}</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </td>
</tr>
<tr data-ng-show="(policies | filter:search).length == 0">
<td class="text-muted" colspan="3" data-ng-show="search.name">{{:: 'no-results' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
index f61c6e9..ef20ae9 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
@@ -47,9 +47,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}}</label>
<div class="col-md-6">
- <select ui-select2 id="reqActions" ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple>
- <option ng-repeat="scope in scopes" value="{{scope.name}}" ng-selected="true">{{scope.name}}</option>
- </select>
+ <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple/>
</div>
<kc-tooltip>{{:: 'authz-resource-scopes.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
index b1c3978..1f8e1bd 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
@@ -39,8 +39,13 @@
<i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
</div>
</div>
+ <div class="input-group">
+ <select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
+ <option value="" selected>Hide Details</option>
+ <option value="true">Show Details</option>
+ </select>
+ </div>
</div>
-
<div class="pull-right">
<a id="createResource" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/create">{{:: 'create' | translate}}</a>
</div>
@@ -52,9 +57,7 @@
<th>{{:: 'type' | translate}}</th>
<th>{{:: 'authz-uri' | translate}}</th>
<th>{{:: 'authz-owner' | translate}}</th>
- <th>{{:: 'authz-scopes' | translate}}</th>
- <th>{{:: 'authz-permissions' | translate}}</th>
- <th>{{:: 'actions' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="resources && (resources.length >= query.max || query.first > 0)">
@@ -64,37 +67,67 @@
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="resources.length < query.max">{{:: 'next-page' | translate}}</button>
+ <select class="first" data-ng-model="query.max"
+ ng-options="size for size in listSizes" data-ng-change="firstPage()">
+ </select>
</div>
</td>
</tr>
</tfoot>
<tbody>
- <tr ng-repeat="resource in resources | filter:search | orderBy:'name'">
+ <tr ng-repeat-start="resource in resources | filter:search | orderBy:'name'">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a></td>
<td>
<span data-ng-show="resource.type">{{resource.type}}</span>
<span data-ng-show="!resource.type">{{:: 'authz-no-type-defined' | translate}}</span>
</td>
- <td>{{resource.uri}}</td>
- <td>{{resource.owner.name}}</td>
<td>
- <span data-ng-show="!resource.scopes.length">{{:: 'authz-no-scopes-assigned' | translate}}</span>
- <span data-ng-show="resource.scopes.length > 0">
- <span ng-repeat="scope in resource.scopes">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a>{{$last ? '' : ', '}}
- </span>
- </span>
+ <span data-ng-show="resource.uri">{{resource.uri}}</span>
+ <span data-ng-show="!resource.uri">{{:: 'authz-no-uri-defined' | translate}}</span>
</td>
- <td>
- <span data-ng-show="!resource.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
- <span data-ng-show="resource.policies.length > 0">
- <span ng-repeat="policy in resource.policies">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
- </span>
- </span>
+ <td>{{resource.owner.name}}</td>
+ <td ng-if="!resource.details.loaded" class="kc-action-cell" data-ng-click="showDetails(resource);">
+ {{:: 'authz-show-details' | translate}}
+ </td>
+ <td ng-if="resource.details.loaded" class="kc-action-cell" data-ng-click="showDetails(resource);">
+ {{:: 'authz-hide-details' | translate}}
+ </td>
+ <td class="kc-action-cell" ng-click="createPolicy(resource);">
+ {{:: 'authz-create-permission' | translate}}
</td>
- <td class="kc-action-cell" style="vertical-align: middle">
- <button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(resource);">{{:: 'authz-create-permission' | translate}}</button>
+ </tr>
+ <tr ng-if="resource.details && resource.details.loaded" ng-repeat-end="">
+ <td colspan="6">
+ <div id="details">
+ <table class="table kc-authz-table-expanded table-striped">
+ <thead>
+ <tr>
+ <th>Scopes</th>
+ <th>Associated Permissions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <span data-ng-show="resource.scopes && !resource.scopes.length">{{:: 'authz-no-scopes-assigned' | translate}}</span>
+ <ul ng-repeat="scope in resource.scopes" data-ng-show="resource.scopes.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a>
+ </li>
+ </ul>
+ </td>
+ <td>
+ <span data-ng-show="resource.policies && !resource.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
+ <ul ng-repeat="policy in resource.policies" data-ng-show="resource.policies.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</td>
</tr>
<tr data-ng-show="(resources | filter:search).length == 0">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
index 7886129..ff8bb14 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
@@ -14,6 +14,12 @@
<i class="fa fa-search" id="scopeSearch" type="submit" data-ng-click="firstPage()"></i>
</div>
</div>
+ <div class="input-group">
+ <select class="form-control search" data-ng-model="detailsFilter" data-ng-change="showDetails();">
+ <option value="" selected>Hide Details</option>
+ <option value="true">Show Details</option>
+ </select>
+ </div>
</div>
<div class="pull-right">
<a id="createScope" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/create">{{:: 'create' | translate}}</a>
@@ -23,43 +29,68 @@
</tr>
<tr data-ng-hide="scopes.length == 0">
<th>{{:: 'name' | translate}}</th>
- <th>{{:: 'authz-resources' | translate}}</th>
- <th>{{:: 'authz-permissions' | translate}}</th>
- <th>{{:: 'actions' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="scopes && (scopes.length >= query.max || query.first > 0)">
<tr>
- <td colspan="7">
+ <td colspan="8">
<div class="table-nav">
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="scopes.length < query.max">{{:: 'next-page' | translate}}</button>
+ <select class="first" data-ng-model="query.max"
+ ng-options="size for size in listSizes" data-ng-change="firstPage()">
+ </select>
</div>
</td>
</tr>
</tfoot>
<tbody>
- <tr ng-repeat="scope in scopes | filter:search | orderBy:'name'">
+ <tr ng-repeat-start="scope in scopes | filter:search | orderBy:'name'">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a></td>
- <td>
- <span data-ng-show="!scope.resources.length">{{:: 'authz-no-resources-assigned' | translate}}</span>
- <span data-ng-show="scope.resources.length > 0">
- <span ng-repeat="resource in scope.resources">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a>{{$last ? '' : ', '}}
- </span>
- </span>
+ <td ng-if="!scope.details.loaded" class="kc-action-cell" data-ng-click="showDetails(scope);">
+ {{:: 'authz-show-details' | translate}}
+ </td>
+ <td ng-if="scope.details.loaded" class="kc-action-cell" data-ng-click="showDetails(scope);">
+ {{:: 'authz-hide-details' | translate}}
</td>
- <td>
- <span data-ng-show="!scope.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
- <span data-ng-show="scope.policies.length > 0">
- <span ng-repeat="policy in scope.policies">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
- </span>
- </span>
+ <td class="kc-action-cell" ng-click="createPolicy(scope);">
+ {{:: 'authz-create-permission' | translate}}
</td>
- <td class="kc-action-cell" style="vertical-align: middle">
- <button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(scope);">{{:: 'authz-create-permission' | translate}}</button>
+ </tr>
+ <tr ng-if="scope.details && scope.details.loaded" ng-repeat-end="">
+ <td colspan="3">
+ <div id="details">
+ <table class="table kc-authz-table-expanded table-striped">
+ <thead>
+ <tr>
+ <th>Resources</th>
+ <th>Associated Permissions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <span data-ng-show="scope.resources && !scope.resources.length">{{:: 'authz-no-resources-assigned' | translate}}</span>
+ <ul ng-repeat="resource in scope.resources" data-ng-show="scope.resources.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a>
+ </li>
+ </ul>
+ </td>
+ <td>
+ <span data-ng-show="scope.policies && !scope.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
+ <ul ng-repeat="policy in scope.policies" data-ng-show="scope.policies.length > 0">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</td>
</tr>
<tr data-ng-show="(scopes | filter:search).length == 0">
diff --git a/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css b/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
index 2eb155e..6f72a78 100755
--- a/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
+++ b/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
@@ -390,4 +390,13 @@ h1 i {
float: left;
margin-left: 2%;
width: 25%;
+}
+
+table.kc-authz-table-expanded {
+ margin-top: 0px !important;
+}
+
+.no-gutter > [class*='col-'] {
+ padding-right:0!important;
+ padding-left:0!important;
}
\ No newline at end of file