keycloak-aplcache

Merge pull request #4601 from pedroigor/KEYCLOAK-5726 [KEYCLOAK-5726]

10/24/2017 12:51:52 PM

Changes

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index f3127be..15aa1e1 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -33,6 +33,7 @@ import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 import org.keycloak.representations.idm.authorization.Permission;
 
@@ -96,9 +97,9 @@ public abstract class AbstractPolicyEnforcer {
                     return createEmptyAuthorizationContext(true);
                 }
 
-                Set<String> requiredScopes = getRequiredScopes(pathConfig, request);
+                MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
 
-                if (isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
+                if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
                     try {
                         return createAuthorizationContext(accessToken, pathConfig);
                     } catch (Exception e) {
@@ -108,7 +109,7 @@ public abstract class AbstractPolicyEnforcer {
 
                 LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
 
-                if (!challenge(pathConfig, requiredScopes, httpFacade)) {
+                if (!challenge(pathConfig, methodConfig, httpFacade)) {
                     LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
                     handleAccessDenied(httpFacade);
                 }
@@ -118,9 +119,9 @@ public abstract class AbstractPolicyEnforcer {
         return createEmptyAuthorizationContext(false);
     }
 
-    protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
+    protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
 
-    protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+    protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
         Request request = httpFacade.getRequest();
         PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
 
@@ -146,7 +147,7 @@ public abstract class AbstractPolicyEnforcer {
                         continue;
                     }
 
-                    if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
+                    if (hasResourceScopePermission(methodConfig, permission)) {
                         LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
                         if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
                             this.paths.remove(actualPathConfig);
@@ -155,7 +156,7 @@ public abstract class AbstractPolicyEnforcer {
                     }
                 }
             } else {
-                if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
+                if (hasResourceScopePermission(methodConfig, permission)) {
                     hasPermission = true;
                     return true;
                 }
@@ -166,7 +167,7 @@ public abstract class AbstractPolicyEnforcer {
             return true;
         }
 
-        LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
+        LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
 
         return false;
     }
@@ -186,9 +187,29 @@ public abstract class AbstractPolicyEnforcer {
         return false;
     }
 
-    private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
+    private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
+        List<String> requiredScopes = methodConfig.getScopes();
         Set<String> allowedScopes = permission.getScopes();
-        return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
+
+        if (allowedScopes.isEmpty()) {
+            return true;
+        }
+
+        PolicyEnforcerConfig.ScopeEnforcementMode enforcementMode = methodConfig.getScopesEnforcementMode();
+
+        if (PolicyEnforcerConfig.ScopeEnforcementMode.ALL.equals(enforcementMode)) {
+            return allowedScopes.containsAll(requiredScopes);
+        }
+
+        if (PolicyEnforcerConfig.ScopeEnforcementMode.ANY.equals(enforcementMode)) {
+            for (String requiredScope : requiredScopes) {
+                if (allowedScopes.contains(requiredScope)) {
+                    return true;
+                }
+            }
+        }
+
+        return requiredScopes.isEmpty();
     }
 
     protected AuthzClient getAuthzClient() {
@@ -236,20 +257,22 @@ public abstract class AbstractPolicyEnforcer {
         return request.getRelativePath();
     }
 
-    private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
-        Set<String> requiredScopes = new HashSet<>();
-
-        requiredScopes.addAll(pathConfig.getScopes());
-
+    private MethodConfig getRequiredScopes(PathConfig pathConfig, Request request) {
         String method = request.getMethod();
 
-        for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
+        for (MethodConfig methodConfig : pathConfig.getMethods()) {
             if (methodConfig.getMethod().equals(method)) {
-                requiredScopes.addAll(methodConfig.getScopes());
+                return methodConfig;
             }
         }
 
-        return requiredScopes;
+        MethodConfig methodConfig = new MethodConfig();
+
+        methodConfig.setMethod(request.getMethod());
+        methodConfig.setScopes(pathConfig.getScopes());
+        methodConfig.setScopesEnforcementMode(PolicyEnforcerConfig.ScopeEnforcementMode.ANY);
+
+        return methodConfig;
     }
 
     private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
index f2555d4..172c745 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
@@ -17,7 +17,7 @@
  */
 package org.keycloak.adapters.authorization;
 
-import java.util.Set;
+import java.util.HashSet;
 
 import org.jboss.logging.Logger;
 import org.keycloak.adapters.OIDCHttpFacade;
@@ -26,6 +26,7 @@ import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.authorization.client.resource.PermissionResource;
 import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 
 /**
@@ -40,9 +41,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
     }
 
     @Override
-    protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+    protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
         if (getEnforcerConfig().getUserManagedAccess() != null) {
-            challengeUmaAuthentication(pathConfig, requiredScopes, facade);
+            challengeUmaAuthentication(pathConfig, methodConfig, facade);
         } else {
             challengeEntitlementAuthentication(facade);
         }
@@ -61,10 +62,10 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
         }
     }
 
-    private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+    private void challengeUmaAuthentication(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
         HttpFacade.Response response = facade.getResponse();
         AuthzClient authzClient = getAuthzClient();
-        String ticket = getPermissionTicket(pathConfig, requiredScopes, authzClient);
+        String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
         String clientId = authzClient.getConfiguration().getResource();
         String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
         response.setStatus(401);
@@ -74,12 +75,12 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
         }
     }
 
-    private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
+    private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) {
         ProtectionResource protection = authzClient.protection();
         PermissionResource permission = protection.permission();
         PermissionRequest permissionRequest = new PermissionRequest();
         permissionRequest.setResourceSetId(pathConfig.getId());
-        permissionRequest.setScopes(requiredScopes);
+        permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
         return permission.forResource(permissionRequest).getTicket();
     }
 }
\ No newline at end of file
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index 0dbddd4..65fdc1e 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -35,6 +35,7 @@ import org.keycloak.authorization.client.representation.EntitlementResponse;
 import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.authorization.client.representation.PermissionResponse;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 import org.keycloak.representations.idm.authorization.Permission;
 
@@ -50,14 +51,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
     }
 
     @Override
-    protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+    protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
         AccessToken original = accessToken;
 
-        if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
+        if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
             return true;
         }
 
-        accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
+        accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
 
         if (accessToken == null) {
             return false;
@@ -78,11 +79,11 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
 
         original.setAuthorization(authorization);
 
-        return super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade);
+        return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
     }
 
     @Override
-    protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+    protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
         handleAccessDenied(facade);
         return true;
     }
@@ -100,7 +101,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
         }
     }
 
-    private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
+    private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
         try {
             String accessToken = httpFacade.getSecurityContext().getTokenString();
             AuthzClient authzClient = getAuthzClient();
@@ -111,7 +112,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
                 PermissionRequest permissionRequest = new PermissionRequest();
 
                 permissionRequest.setResourceSetId(pathConfig.getId());
-                permissionRequest.setScopes(requiredScopes);
+                permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
 
                 PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
                 AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
index 496c311..390ea15 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
@@ -276,11 +276,13 @@ public final class KeycloakAdapterConfigService {
     }
 
     private void setJSONValues(ModelNode json, ModelNode values) {
-        for (Property prop : new ArrayList<>(values.asPropertyList())) {
-            String name = prop.getName();
-            ModelNode value = prop.getValue();
-            if (value.isDefined()) {
-                json.get(name).set(value);
+        synchronized (values) {
+            for (Property prop : new ArrayList<>(values.asPropertyList())) {
+                String name = prop.getName();
+                ModelNode value = prop.getValue();
+                if (value.isDefined()) {
+                    json.get(name).set(value);
+                }
             }
         }
     }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index a495cad..67c3c59 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -220,6 +220,9 @@ public class PolicyEnforcerConfig {
         private String method;
         private List<String> scopes = Collections.emptyList();
 
+        @JsonProperty("scopes-enforcement-mode")
+        private ScopeEnforcementMode scopesEnforcementMode = ScopeEnforcementMode.ALL;
+
         public String getMethod() {
             return method;
         }
@@ -235,6 +238,14 @@ public class PolicyEnforcerConfig {
         public void setScopes(List<String> scopes) {
             this.scopes = scopes;
         }
+
+        public void setScopesEnforcementMode(ScopeEnforcementMode scopesEnforcementMode) {
+            this.scopesEnforcementMode = scopesEnforcementMode;
+        }
+
+        public ScopeEnforcementMode getScopesEnforcementMode() {
+            return scopesEnforcementMode;
+        }
     }
 
     public enum EnforcementMode {
@@ -243,6 +254,11 @@ public class PolicyEnforcerConfig {
         DISABLED
     }
 
+    public enum ScopeEnforcementMode {
+        ALL,
+        ANY
+    }
+
     public static class UmaProtocolConfig {
 
     }
diff --git a/examples/authz/photoz/photoz-realm.json b/examples/authz/photoz/photoz-realm.json
index b0aeb5d..118b982 100644
--- a/examples/authz/photoz/photoz-realm.json
+++ b/examples/authz/photoz/photoz-realm.json
@@ -108,7 +108,7 @@
       "redirectUris": [
         "/photoz-html5-client/*"
       ],
-      "webOrigins": ["*"]
+      "webOrigins": ["http://localhost:8080"]
     },
     {
       "clientId": "photoz-restful-api",
@@ -119,7 +119,7 @@
       "redirectUris": [
         "/photoz-restful-api/*"
       ],
-      "webOrigins" : ["*"]
+      "webOrigins" : ["http://localhost:8080"]
     }
   ]
 }
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 1fe6675..e84e24d 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -90,7 +90,7 @@ public class AlbumService {
     @Path("{id}")
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        List result = this.entityManager.createQuery("from Album where id = :id").setParameter("id", id).getResultList();
+        List result = this.entityManager.createQuery("from Album where id = :id").setParameter("id", Long.valueOf(id)).getResultList();
 
         if (result.isEmpty()) {
             return Response.status(Status.NOT_FOUND).build();
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
index 691e01a..ecdbf0b 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
@@ -34,7 +34,7 @@ module.config(function ($httpProvider, $routeProvider) {
         controller: 'AdminAlbumCtrl',
     }).when('/profile', {
         templateUrl: 'partials/profile.html',
-        controller: 'ProfileCtrl',
+        controller: 'ProfileCtrl'
     });
 });
 
@@ -50,6 +50,16 @@ module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Albu
             $route.reload();
         });
     }
+
+    $scope.requestPathWithAnyProtectedScope = function() {
+        $http.get(apiUrl + '/scope-any').success(function (data) {
+        });
+    }
+
+    $scope.requestPathWithAllProtectedScope = function() {
+        $http.get(apiUrl + '/scope-all').success(function (data) {
+        });
+    }
 });
 
 module.controller('TokenCtrl', function ($scope, Identity) {
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
index e144d1b..788763b 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
@@ -3,7 +3,7 @@
 <hr/>
 <br/>
 <div data-ng-show="!Identity.isAdmin()">
-<a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a>
+<a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a> | <a href="#" id="requestPathWithAnyProtectedScope" ng-click="requestPathWithAnyProtectedScope()">Any Scope Access</a> | <a href="#" id="requestPathWithAllProtectedScope" ng-click="requestPathWithAllProtectedScope()">All Scope Access</a>
 <br/>
 <br/>
 <span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
index bb21e50..7ec7e02 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
@@ -108,7 +108,7 @@
       "redirectUris": [
         "/photoz-html5-client/*"
       ],
-      "webOrigins": ["*"]
+      "webOrigins": ["http://localhost:8280"]
     },
     {
       "clientId": "photoz-restful-api",
@@ -118,7 +118,7 @@
       "redirectUris": [
         "/photoz-restful-api/*"
       ],
-      "webOrigins" : ["*"],
+      "webOrigins" : ["http://localhost:8280"],
       "clientAuthenticatorType": "client-jwt",
       "attributes" : {
         "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg=="
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index a1230d8..7969492 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -99,7 +99,7 @@ public class AlbumService {
     @Path("{id}")
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        List result = this.entityManager.createQuery("from Album where id = " + id).getResultList();
+        List result = this.entityManager.createQuery("from Album where id = " + Long.valueOf(id)).getResultList();
 
         if (result.isEmpty()) {
             return Response.status(Status.NOT_FOUND).build();
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index 5b41d26..f3db78d 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -51,6 +51,28 @@
       {
         "name" : "Admin Resources",
         "path" : "/admin/*"
+      },
+      {
+        "name" : "Scope Protected Resource",
+        "path" : "/scope-any",
+        "methods": [
+          {
+            "method": "GET",
+            "scopes": ["scope-a", "scope-b"],
+            "scopes-enforcement-mode": "ANY"
+          }
+        ]
+      },
+      {
+        "name" : "Scope Protected Resource",
+        "path" : "/scope-all",
+        "methods": [
+          {
+            "method": "GET",
+            "scopes": ["scope-a", "scope-b"],
+            "scopes-enforcement-mode": "ALL"
+          }
+        ]
       }
     ]
   }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
index ba44208..0b621f5 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
@@ -37,6 +37,18 @@
           "name": "urn:photoz.com:scopes:album:admin:manage"
         }
       ]
+    },
+    {
+      "name": "Scope Protected Resource",
+      "uri": "/scope-any",
+      "scopes": [
+        {
+          "name": "scope-a"
+        },
+        {
+          "name": "scope-b"
+        }
+      ]
     }
   ],
   "policies": [
@@ -166,6 +178,37 @@
         "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
         "scopes": "[\"urn:photoz.com:scopes:album:delete\"]"
       }
+    },
+    {
+      "name": "Deny Policy",
+      "type": "js",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "code": "// by default, grants any permission associated with this policy\n$evaluation.deny();"
+      }
+    },
+    {
+      "name": "Protected Scope A Permission",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Scope Protected Resource\"]",
+        "scopes": "[\"scope-a\"]",
+        "applyPolicies": "[\"Any User Policy\"]"
+      }
+    },
+    {
+      "name": "Protected Scope B Permission",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Scope Protected Resource\"]",
+        "scopes": "[\"scope-b\"]",
+        "applyPolicies": "[\"Deny Policy\"]"
+      }
     }
   ],
   "scopes": [
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
index 43ebde4..0a31003 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json
@@ -46,6 +46,18 @@
           "name": "urn:servlet-authz:page:main:actionForPremiumUser"
         }
       ]
+    },
+    {
+      "name": "Resource A",
+      "uri": "/protected/scopes.jsp",
+      "scopes": [
+        {
+          "name": "read"
+        },
+        {
+          "name": "write"
+        }
+      ]
     }
   ],
   "policies": [
@@ -142,6 +154,37 @@
         "scopes": "[\"urn:servlet-authz:page:main:actionForPremiumUser\"]",
         "applyPolicies": "[\"Only Premium User Policy\"]"
       }
+    },
+    {
+      "name": "Deny Policy",
+      "type": "js",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "code": "// by default, grants any permission associated with this policy\n$evaluation.deny();"
+      }
+    },
+    {
+      "name": "Resource A Read Permission",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Resource A\"]",
+        "scopes": "[\"read\"]",
+        "applyPolicies": "[\"Any User Policy\"]"
+      }
+    },
+    {
+      "name": "Resource A Write Permission",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Resource A\"]",
+        "scopes": "[\"write\"]",
+        "applyPolicies": "[\"Deny Policy\"]"
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/scopes.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/scopes.jsp
new file mode 100644
index 0000000..405921b
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/scopes.jsp
@@ -0,0 +1 @@
+Granted
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
index 32cc58f..27df535 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
@@ -160,6 +160,18 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         pause(WAIT_AFTER_OPERATION);
     }
 
+    public void requestResourceProtectedAnyScope() throws InterruptedException {
+        navigateTo();
+        this.driver.findElement(By.id("requestPathWithAnyProtectedScope")).click();
+        pause(WAIT_AFTER_OPERATION);
+    }
+
+    public void requestResourceProtectedAllScope() throws InterruptedException {
+        navigateTo();
+        this.driver.findElement(By.id("requestPathWithAllProtectedScope")).click();
+        pause(WAIT_AFTER_OPERATION);
+    }
+
     public WebElement getOutput() {
         return output;
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index 7223b2a..edd264c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -642,6 +642,20 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
     }
 
+    @Test
+    public void testResourceProtectedWithAnyScope() throws Exception {
+        try {
+            this.deployer.deploy(RESOURCE_SERVER_ID);
+            loginToClientPage("alice", "alice");
+            this.clientPage.requestResourceProtectedAllScope();
+            assertTrue(this.clientPage.wasDenied());
+            this.clientPage.requestResourceProtectedAnyScope();
+            assertFalse(this.clientPage.wasDenied());
+        } finally {
+            this.deployer.undeploy(RESOURCE_SERVER_ID);
+        }
+    }
+
     private void importResourceServerSettings() throws FileNotFoundException {
         ResourceServerRepresentation authSettings = loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
index a46f8f1..63852d0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
@@ -307,4 +307,14 @@ public abstract class AbstractServletAuthzFunctionalAdapterTest extends Abstract
             assertFalse(wasDenied());
         });
     }
+
+    @Test
+    public void testAccessResourceWithAnyScope() throws Exception {
+        performTests(() -> {
+            login("jdoe", "jdoe");
+            driver.navigate().to(getResourceServerUrl() + "/protected/scopes.jsp");
+            WaitUtils.waitForPageToLoad();
+            assertTrue(hasText("Granted"));
+        });
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index dbe05cd..991dedc 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -316,7 +316,7 @@
                             <keycloak.connectionsJpa.url.crossdc>${keycloak.connectionsJpa.url.crossdc}</keycloak.connectionsJpa.url.crossdc>
 
                             <!-- used by PasswordPolicyTest.testBlacklistPasswordPolicyWithTestBlacklist, see KEYCLOAK-5244 -->
-                            <keycloak.password.blacklists.path>${project.build.directory}/test-classes/password-blacklists</keycloak.password.blacklists.path>
+                            <keycloak.password.blacklists.path>${project.build.directory}/test-classes</keycloak.password.blacklists.path>
                         </systemPropertyVariables>
                         <properties>
                             <property>