keycloak-uncached

Changes

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
index d90a4fd..9efa614 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
@@ -49,7 +49,6 @@ class PathMatcher {
 
         PathConfig matchingAnyPath = null;
         PathConfig matchingAnySuffixPath = null;
-        PathConfig matchingPath = null;
 
         for (PathConfig entry : paths.values()) {
             String expectedUri = entry.getPath();
@@ -132,56 +131,77 @@ class PathMatcher {
             return targetUri.startsWith(expectedUri.substring(0, expectedUri.length() - 2));
         }
 
+        String suffix = "/*.";
+        int suffixIndex = expectedUri.indexOf(suffix);
+
+        if (suffixIndex != -1) {
+            return targetUri.endsWith(expectedUri.substring(suffixIndex + suffix.length() - 1));
+        }
+
         return false;
     }
 
     public String buildUriFromTemplate(String expectedUri, String targetUri) {
         int patternStartIndex = expectedUri.indexOf("{");
 
-        if (patternStartIndex >= targetUri.length()) {
+        if (patternStartIndex == -1 || patternStartIndex >= targetUri.length()) {
+            return null;
+        }
+
+        if (expectedUri.split("/").length > targetUri.split("/").length) {
             return null;
         }
 
         char[] expectedUriChars = expectedUri.toCharArray();
         char[] matchingUri = Arrays.copyOfRange(expectedUriChars, 0, patternStartIndex);
+        int matchingUriLastIndex = matchingUri.length;
+        String targetUriParams = targetUri.substring(patternStartIndex);
 
         if (Arrays.equals(matchingUri, Arrays.copyOf(targetUri.toCharArray(), matchingUri.length))) {
-            int matchingLastIndex = matchingUri.length;
-            matchingUri = Arrays.copyOf(matchingUri, targetUri.length()); // +1 so we can add a slash at the end
-            int targetPatternStartIndex = patternStartIndex;
+            matchingUri = Arrays.copyOf(matchingUri, targetUri.length());
+            int paramIndex = 0;
 
-            while (patternStartIndex != -1) {
-                int parameterStartIndex = -1;
+            for (int i = patternStartIndex; i < expectedUriChars.length; i++) {
+                if (matchingUriLastIndex >= matchingUri.length) {
+                    break;
+                }
 
-                for (int i = targetPatternStartIndex; i < targetUri.length(); i++) {
-                    char c = targetUri.charAt(i);
+                char c = expectedUriChars[i];
 
-                    if (c != '/') {
-                        if (parameterStartIndex == -1) {
-                            parameterStartIndex = matchingLastIndex;
+                if (c == '{' || c == '*') {
+                    String[] params = targetUriParams.split("/");
+
+                    for (int k = paramIndex; k <= (c == '*' ? params.length : paramIndex); k++) {
+                        if (k == params.length) {
+                            break;
                         }
-                        matchingUri[matchingLastIndex] = c;
-                        matchingLastIndex++;
-                    }
 
-                    if (c == '/' || ((i + 1 == targetUri.length()))) {
-                        if (matchingUri[matchingLastIndex - 1] != '/' && matchingLastIndex < matchingUri.length) {
-                            matchingUri[matchingLastIndex] = '/';
-                            matchingLastIndex++;
+                        int paramLength = params[k].length();
+
+                        if (matchingUriLastIndex + paramLength > matchingUri.length) {
+                            return null;
+                        }
+
+                        for (int j = 0; j < paramLength; j++) {
+                            matchingUri[matchingUriLastIndex++] = params[k].charAt(j);
                         }
 
-                        targetPatternStartIndex = targetUri.indexOf('/', i) + 1;
-                        break;
+                        if (c == '*' && matchingUriLastIndex < matchingUri.length) {
+                            matchingUri[matchingUriLastIndex++] = '/';
+                        }
                     }
-                }
 
-                if ((patternStartIndex = expectedUri.indexOf('{', patternStartIndex + 1)) == -1) {
-                    break;
+                    i = expectedUri.indexOf('}', i);
+                } else {
+                    if (c == '/') {
+                        paramIndex++;
+                    }
+                    matchingUri[matchingUriLastIndex++] = c;
                 }
+            }
 
-                if ((targetPatternStartIndex == 0 || targetPatternStartIndex == targetUri.length()) && parameterStartIndex != -1) {
-                    return null;
-                }
+            if (matchingUri[matchingUri.length - 1] == '\u0000') {
+                return null;
             }
 
             return String.valueOf(matchingUri);
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 a1bc179..dd94537 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
@@ -196,10 +196,12 @@ public class PolicyEnforcerConfig {
                     '}';
         }
 
+        @JsonIgnore
         public boolean hasPattern() {
             return getPath().indexOf("{") != -1;
         }
 
+        @JsonIgnore
         public boolean isInstance() {
             return this.parentConfig != null;
         }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
index d4dd0ed..c833f01 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
@@ -17,13 +17,16 @@
 package org.keycloak.admin.client.resource;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 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.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 
 /**
@@ -39,4 +42,9 @@ public interface ResourcePermissionsResource {
     @Path("{id}")
     ResourcePermissionResource findById(@PathParam("id") String id);
 
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    ResourcePermissionRepresentation findByName(@QueryParam("name") String name);
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java
index 9ed6226..f9f1f98 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java
@@ -17,13 +17,16 @@
 package org.keycloak.admin.client.resource;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 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.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
 
 /**
@@ -39,4 +42,9 @@ public interface RolePoliciesResource {
     @Path("{id}")
     RolePolicyResource findById(@PathParam("id") String id);
 
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    RolePolicyRepresentation findByName(@QueryParam("name") String name);
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java
index c92b132..ab0dd5c 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java
@@ -17,13 +17,16 @@
 package org.keycloak.admin.client.resource;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 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.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
 
 /**
@@ -38,4 +41,10 @@ public interface ScopePermissionsResource {
 
     @Path("{id}")
     ScopePermissionResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    ScopePermissionRepresentation findByName(@QueryParam("name") String name);
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java
index c1769d7..702995f 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java
@@ -17,13 +17,16 @@
 package org.keycloak.admin.client.resource;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 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.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 
 /**
@@ -39,4 +42,9 @@ public interface UserPoliciesResource {
     @Path("{id}")
     UserPolicyResource findById(@PathParam("id") String id);
 
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    UserPolicyRepresentation findByName(@QueryParam("name") String name);
 }
diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
index 5f7520f..c894674 100644
--- a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
+++ b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
@@ -79,6 +79,7 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
         FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
         di.addFilter(filter);
         di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
+        filter.setAsyncSupported(true);
 
         return di;
     }
diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml
index 53f9980..5cdca9e 100644
--- a/testsuite/integration-arquillian/test-apps/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/pom.xml
@@ -21,6 +21,7 @@
         <module>photoz</module>
         <module>hello-world-authz-service</module>
         <module>servlet-authz</module>
+        <module>servlet-policy-enforcer</module>
         <module>servlets</module>
         <module>app-profile-jee</module>
         <module>cors</module>
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml
new file mode 100755
index 0000000..d6eb833
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak.testsuite</groupId>
+        <artifactId>integration-arquillian-test-apps</artifactId>
+        <version>3.1.0.CR1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-policy-enforcer</artifactId>
+    <packaging>war</packaging>
+
+    <name>Keycloak Authz: Simple Servlet App with Policy Enforcer</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
new file mode 100644
index 0000000..cef6f00
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
@@ -0,0 +1,265 @@
+{
+    "realm": "servlet-policy-enforcer-authz",
+    "enabled": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [
+        "password"
+    ],
+    "users": [
+        {
+            "username": "alice",
+            "enabled": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "alice"
+                }
+            ],
+            "realmRoles": [
+                "uma_authorization"
+            ]
+        },
+        {
+            "username": "jdoe",
+            "enabled": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "jdoe"
+                }
+            ],
+            "realmRoles": [
+                "uma_authorization"
+            ]
+        },
+        {
+            "username": "service-account-servlet-policy-enforcer",
+            "enabled": true,
+            "serviceAccountClientId": "servlet-policy-enforcer",
+            "clientRoles": {
+                "servlet-policy-enforcer": [
+                    "uma_protection"
+                ]
+            }
+        }
+    ],
+    "clients": [
+        {
+            "clientId": "servlet-policy-enforcer",
+            "secret": "secret",
+            "authorizationServicesEnabled": true,
+            "enabled": true,
+            "redirectUris": [
+                "/servlet-policy-enforcer/*"
+            ],
+            "baseUrl": "/servlet-policy-enforcer",
+            "adminUrl": "/servlet-policy-enforcer",
+            "directAccessGrantsEnabled": true,
+            "authorizationSettings": {
+                "allowRemoteResourceManagement": false,
+                "policyEnforcementMode": "ENFORCING",
+                "resources": [
+                    {
+                        "name": "Welcome Resource",
+                        "uri": "",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 1",
+                        "uri": "",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 2",
+                        "uri": "/resource/resource-a",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 3",
+                        "uri": "/resource/resource-b/test",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 4",
+                        "uri": "/resource-c",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 5",
+                        "uri": "/resource/d/resource-d",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 6",
+                        "uri": "",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 7",
+                        "uri": "",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 8",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 9",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 10",
+                        "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 11",
+                        "typedScopes": []
+                    }
+                ],
+                "policies": [
+                    {
+                        "name": "Default Policy",
+                        "type": "js",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n",
+                            "applyPolicies": "[]"
+                        }
+                    },
+                    {
+                        "name": "Deny Policy",
+                        "type": "js",
+                        "logic": "NEGATIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "code": "$evaluation.grant();"
+                        }
+                    },
+                    {
+                        "name": "Pattern 3 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "resources": "[\"Pattern 3\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 2 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "resources": "[\"Pattern 2\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 4 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 4\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 5 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 5\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 7 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "resources": "[\"Pattern 7\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 8 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 8\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 9 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 9\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 6 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "resources": "[\"Pattern 6\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Welcome Resource Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Welcome Resource\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 1 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 1\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 10 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 10\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Pattern 11 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 11\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
+                    }
+                ],
+                "scopes": []
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp
new file mode 100644
index 0000000..ef8b4a6
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp
@@ -0,0 +1,2 @@
+<%@include file="logout-include.jsp"%>
+<p>You can not access this resource</p>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp
new file mode 100644
index 0000000..cf0e035
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp
@@ -0,0 +1,2 @@
+<%@include file="logout-include.jsp"%>
+<p>Welcome</p>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp
new file mode 100644
index 0000000..3c55fd9
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp
@@ -0,0 +1,11 @@
+<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+<%
+    String scheme = request.getScheme();
+    String host = request.getServerName();
+    int port = request.getServerPort();
+    String contextPath = request.getContextPath();
+    String redirectUri = scheme + "://" + host + ":" + port + contextPath;
+%>
+<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8180/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+            .queryParam("redirect_uri", redirectUri).build("servlet-policy-enforcer-authz").toString()%>">Sign Out</a></h2>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..d8742d3
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,62 @@
+{
+    "realm": "servlet-policy-enforcer-authz",
+    "auth-server-url": "http://localhost:8180/auth",
+    "ssl-required": "external",
+    "resource": "servlet-policy-enforcer",
+    "credentials": {
+        "secret": "secret"
+    },
+    "policy-enforcer": {
+        "on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
+        "paths": [
+            {
+                "name": "Welcome Resource",
+                "path": "/index.jsp"
+            },
+            {
+                "name": "Pattern 1",
+                "path": "/resource/{pattern}/{sub-resource}"
+            },
+            {
+                "name": "Pattern 2",
+                "path": "/{pattern}/resource-a"
+            },
+            {
+                "name": "Pattern 3",
+                "path": "/{pattern}/resource-b"
+            },
+            {
+                "name": "Pattern 4",
+                "path": "/resource-c"
+            },
+            {
+                "name": "Pattern 5",
+                "path": "/resource/{pattern}/resource-d"
+            },
+            {
+                "name": "Pattern 6",
+                "path": "/resource/{pattern}"
+            },
+            {
+                "name": "Pattern 7",
+                "path": "/resource/{pattern}/f/{resource}"
+            },
+            {
+                "name": "Pattern 8",
+                "path": "/resource"
+            },
+            {
+                "name": "Pattern 9",
+                "path": "/file/*.suffix"
+            },
+            {
+                "name": "Pattern 10",
+                "path": "/resource/{pattern}/i/{resource}/*"
+            },
+            {
+                "name": "Pattern 11",
+                "path": "/api/{version}/{resource}"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3916450
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+		 version="3.0">
+
+	<module-name>servlet-policy-enforcer</module-name>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>uma_authorization</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<login-config>
+		<auth-method>KEYCLOAK</auth-method>
+		<realm-name>servlet-policy-enforcer</realm-name>
+	</login-config>
+
+	<security-role>
+		<role-name>uma_authorization</role-name>
+	</security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
index ff9f05d..e3200c5 100755
--- a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
@@ -51,6 +51,14 @@
                 <exclude name="**/subsystem-config.xml"/>
             </fileset>
         </copy>
+        <copy todir="target/test-apps/servlet-policy-enforcer" overwrite="true">
+            <fileset dir="../servlet-policy-enforcer">
+                <exclude name="**/target/**"/>
+                <exclude name="**/*.iml"/>
+                <exclude name="**/*.unconfigured"/>
+                <exclude name="**/subsystem-config.xml"/>
+            </fileset>
+        </copy>
         <copy todir="target/test-apps/cors" overwrite="true">
             <fileset dir="../cors">
                 <exclude name="**/target/**"/>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
new file mode 100644
index 0000000..aaeee4f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.ResourcePermissionsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleAdapterTest {
+
+    protected static final String REALM_NAME = "servlet-policy-enforcer-authz";
+    protected static final String RESOURCE_SERVER_ID = "servlet-policy-enforcer";
+
+    @BeforeClass
+    public static void enabled() { ProfileAssume.assumePreview(); }
+
+    @ArquillianResource
+    private Deployer deployer;
+
+    @Override
+    public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(
+                loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json")));
+    }
+
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+    public static WebArchive deployment() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID);
+    }
+
+    @Test
+    public void testPattern1() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource/a/b");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 1 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/b");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 1 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/b");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern2() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/a/resource-a");
+            assertFalse(wasDenied());
+            navigateTo("/b/resource-a");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 2 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/a/resource-a");
+            assertTrue(wasDenied());
+            navigateTo("/b/resource-a");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/b/resource-a");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern3() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/a/resource-b");
+            assertFalse(wasDenied());
+            navigateTo("/b/resource-b");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/a/resource-b");
+            assertTrue(wasDenied());
+            navigateTo("/b/resource-b");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 3 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/b/resource-b");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/b/resource-a");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/a/resource-b");
+            assertTrue(wasDenied());
+            navigateTo("/b/resource-a");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern4() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource-c");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 4 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource-c");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 4 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource-c");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern5() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource/a/resource-d");
+            assertFalse(wasDenied());
+            navigateTo("/resource/b/resource-d");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/resource-d");
+            assertTrue(wasDenied());
+            navigateTo("/resource/b/resource-d");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource/b/resource-d");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern6() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource/a");
+            assertFalse(wasDenied());
+            navigateTo("/resource/b");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 6 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a");
+            assertTrue(wasDenied());
+            navigateTo("/resource/b");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 6 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource/b");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern7() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource/a/f/b");
+            assertFalse(wasDenied());
+            navigateTo("/resource/c/f/d");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 7 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/f/b");
+            assertTrue(wasDenied());
+            navigateTo("/resource/c/f/d");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 7 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource/c/f/d");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern8() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 8 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 8 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern9() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/file/*.suffix");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 9 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/file/*.suffix");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 9 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/file/*.suffix");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern10() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+
+            navigateTo("/resource/a/i/b/c/d/e");
+            navigateTo("/resource/a/i/b/c/");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 10 Permission", "Deny Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/i/b/c/d/e");
+            navigateTo("/resource/a/i/b/c/d");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 10 Permission", "Default Policy");
+            login("alice", "alice");
+            navigateTo("/resource/a/i/b/c/d");
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testPattern11UsingResourceInstancePermission() throws Exception {
+        performTests(() -> {
+            login("alice", "alice");
+            navigateTo("/api/v1/resource-a");
+            assertFalse(wasDenied());
+            navigateTo("/api/v1/resource-b");
+            assertFalse(wasDenied());
+
+            ResourceRepresentation resource = new ResourceRepresentation("/api/v1/resource-c");
+
+            resource.setUri(resource.getName());
+
+            getAuthorizationResource().resources().create(resource);
+
+            createResourcePermission(resource.getName() + " permission", resource.getName(), "Default Policy");
+
+            login("alice", "alice");
+            navigateTo(resource.getUri());
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies(resource.getName() + " permission", "Deny Policy");
+
+            login("alice", "alice");
+            navigateTo(resource.getUri());
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies(resource.getName() + " permission", "Default Policy");
+
+            login("alice", "alice");
+            navigateTo(resource.getUri());
+            assertFalse(wasDenied());
+
+            navigateTo("/api/v1");
+            assertTrue(wasDenied());
+            navigateTo("/api/v1/");
+            assertTrue(wasDenied());
+            navigateTo("/api");
+            assertTrue(wasDenied());
+            navigateTo("/api/");
+            assertTrue(wasDenied());
+        });
+    }
+
+    private void navigateTo(String path) {
+        this.driver.navigate().to(getResourceServerUrl() + path);
+    }
+
+    private void performTests(ExceptionRunnable assertion) {
+        performTests(() -> {}, assertion);
+    }
+
+    private void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
+        try {
+            beforeDeploy.run();
+            deployer.deploy(RESOURCE_SERVER_ID);
+            assertion.run();
+        } catch (FileNotFoundException cause) {
+            throw new RuntimeException("Failed to import authorization settings", cause);
+        } catch (Exception cause) {
+            throw new RuntimeException("Error while executing tests", cause);
+        } finally {
+            deployer.undeploy(RESOURCE_SERVER_ID);
+        }
+    }
+
+    private AuthorizationResource getAuthorizationResource() {
+        return getClientResource(RESOURCE_SERVER_ID).authorization();
+    }
+
+    private ClientResource getClientResource(String clientId) {
+        ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
+        ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
+        return clients.get(resourceServer.getId());
+    }
+
+    private void logOut() {
+        navigateTo();
+        By by = By.xpath("//a[text() = 'Sign Out']");
+        WaitUtils.waitUntilElement(by);
+        this.driver.findElement(by).click();
+        pause(500);
+    }
+
+    private  void login(String username, String password) {
+        try {
+            navigateTo();
+            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
+                logOut();
+                navigateTo();
+            }
+            this.loginPage.form().login(username, password);
+            navigateTo();
+            assertFalse(wasDenied());
+        } catch (Exception cause) {
+            throw new RuntimeException("Login failed", cause);
+        }
+    }
+
+    private void navigateTo() {
+        this.driver.navigate().to(getResourceServerUrl());
+        WaitUtils.waitUntilElement(By.xpath("//p[text() = 'Welcome']"));
+    }
+
+    private boolean wasDenied() {
+        return this.driver.getPageSource().contains("You can not access this resource");
+    }
+
+    private URL getResourceServerUrl() {
+        try {
+            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Could not obtain resource server url.", e);
+        }
+    }
+
+    private void updatePermissionPolicies(String permissionName, String... policyNames) {
+        ResourcePermissionsResource permissions = getAuthorizationResource().permissions().resource();
+        ResourcePermissionRepresentation permission = permissions.findByName(permissionName);
+
+        permission.addPolicy(policyNames);
+
+        permissions.findById(permission.getId()).update(permission);
+    }
+
+    private void createResourcePermission(String name, String resourceName, String... policyNames) {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+
+        permission.setName(name);
+        permission.addResource(resourceName);
+        permission.addPolicy(policyNames);
+
+        getAuthorizationResource().permissions().resource().create(permission);
+    }
+
+    private interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java
new file mode 100644
index 0000000..8ad7eb1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@RunAsClient
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyServletPolicyEnforcerAdapterTest extends AbstractServletPolicyEnforcerTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 9e13566..75a68ea 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -352,6 +352,12 @@
                                             </artifactItem>
                                             <artifactItem>
                                                 <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>servlet-policy-enforcer</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>war</type>
+                                            </artifactItem>
+                                            <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
                                                 <artifactId>integration-arquillian-test-apps-cors-angular-product</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>