keycloak-uncached
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java 106(+56 -50)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java 15(+6 -9)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java 142(+79 -63)
authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java 218(+0 -218)
authz/client/src/main/java/org/keycloak/authorization/client/representation/ScopeRepresentation.java 98(+0 -98)
authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java 39(+32 -7)
core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java 54(+41 -13)
core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java 39(+34 -5)
services/src/main/java/org/keycloak/authorization/protection/resource/RegistrationResponse.java 50(+0 -50)
services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java 176(+0 -176)
services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaScopeRepresentation.java 98(+0 -98)
services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java 119(+31 -88)
services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java 72(+72 -0)
services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java 2(+1 -1)
testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json 78(+78 -0)
testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java 4(+2 -2)
testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json 15(+15 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json 3(+2 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java 14(+10 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java 211(+211 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java 12(+1 -11)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java 88(+88 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleNoLazyLoadPathsAdapterTest.java 42(+16 -26)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java 193(+8 -185)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java 10(+1 -9)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java 58(+58 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java 8(+4 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java 73(+61 -12)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java 29(+29 -0)
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 96fbe5d..452583b 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
@@ -19,7 +19,6 @@ package org.keycloak.adapters.authorization;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.jboss.logging.Logger;
@@ -30,10 +29,12 @@ import org.keycloak.adapters.spi.HttpFacade.Request;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessToken.Authorization;
 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.adapters.config.PolicyEnforcerConfig.ScopeEnforcementMode;
 import org.keycloak.representations.idm.authorization.Permission;
 
 /**
@@ -42,31 +43,23 @@ import org.keycloak.representations.idm.authorization.Permission;
 public abstract class AbstractPolicyEnforcer {
 
     private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
-    private final PolicyEnforcerConfig enforcerConfig;
-    private final PolicyEnforcer policyEnforcer;
+    private static final String HTTP_METHOD_DELETE = "DELETE";
 
-    private Map<String, PathConfig> paths;
-    private AuthzClient authzClient;
-    private PathMatcher pathMatcher;
+    private final PolicyEnforcer policyEnforcer;
 
-    public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+    protected AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
         this.policyEnforcer = policyEnforcer;
-        this.enforcerConfig = policyEnforcer.getEnforcerConfig();
-        this.authzClient = policyEnforcer.getClient();
-        this.pathMatcher = policyEnforcer.getPathMatcher();
-        this.paths = policyEnforcer.getPaths();
     }
 
     public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
-        EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
+        EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
 
         if (EnforcementMode.DISABLED.equals(enforcementMode)) {
             return createEmptyAuthorizationContext(true);
         }
 
         Request request = httpFacade.getRequest();
-        String path = getPath(request);
-        PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
+        PathConfig pathConfig = getPathConfig(request);
         KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
 
         if (securityContext == null) {
@@ -79,16 +72,20 @@ public abstract class AbstractPolicyEnforcer {
         AccessToken accessToken = securityContext.getToken();
 
         if (accessToken != null) {
-            LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+            }
 
             if (pathConfig == null) {
                 if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
                     return createAuthorizationContext(accessToken, null);
                 }
 
-                LOGGER.debugf("Could not find a configuration for path [%s]", path);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debugf("Could not find a configuration for path [%s]", getPath(request));
+                }
 
-                if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+                if (isDefaultAccessDeniedUri(request)) {
                     return createAuthorizationContext(accessToken, null);
                 }
 
@@ -111,10 +108,18 @@ public abstract class AbstractPolicyEnforcer {
                 }
             }
 
-            LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            if (methodConfig != null && ScopeEnforcementMode.DISABLED.equals(methodConfig.getScopesEnforcementMode())) {
+                return createEmptyAuthorizationContext(true);
+            }
+
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            }
 
             if (!challenge(pathConfig, methodConfig, httpFacade)) {
-                LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+                }
                 handleAccessDenied(httpFacade);
             }
         }
@@ -126,22 +131,21 @@ public abstract class AbstractPolicyEnforcer {
 
     protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
         Request request = httpFacade.getRequest();
-        PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
 
-        if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+        if (isDefaultAccessDeniedUri(request)) {
             return true;
         }
 
-        AccessToken.Authorization authorization = accessToken.getAuthorization();
+        Authorization authorization = accessToken.getAuthorization();
 
         if (authorization == null) {
             return false;
         }
 
-        List<Permission> permissions = authorization.getPermissions();
         boolean hasPermission = false;
+        List<Permission> grantedPermissions = authorization.getPermissions();
 
-        for (Permission permission : permissions) {
+        for (Permission permission : grantedPermissions) {
             if (permission.getResourceId() != null) {
                 if (isResourcePermission(actualPathConfig, permission)) {
                     hasPermission = true;
@@ -151,9 +155,11 @@ public abstract class AbstractPolicyEnforcer {
                     }
 
                     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);
+                        if (LOGGER.isDebugEnabled()) {
+                            LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
+                        }
+                        if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
+                            policyEnforcer.getPaths().remove(actualPathConfig);
                         }
                         return true;
                     }
@@ -170,7 +176,9 @@ public abstract class AbstractPolicyEnforcer {
             return true;
         }
 
-        LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, grantedPermissions);
+        }
 
         return false;
     }
@@ -179,15 +187,21 @@ public abstract class AbstractPolicyEnforcer {
         httpFacade.getResponse().sendError(403);
     }
 
-    private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
-        String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();
+    protected AuthzClient getAuthzClient() {
+        return policyEnforcer.getClient();
+    }
 
-        if (accessDeniedPath != null) {
-            if (request.getURI().contains(accessDeniedPath)) {
-                return true;
-            }
-        }
-        return false;
+    protected PolicyEnforcerConfig getEnforcerConfig() {
+        return policyEnforcer.getEnforcerConfig();
+    }
+
+    protected PolicyEnforcer getPolicyEnforcer() {
+        return policyEnforcer;
+    }
+
+    private boolean isDefaultAccessDeniedUri(Request request) {
+        String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
+        return accessDeniedPath != null && request.getURI().contains(accessDeniedPath);
     }
 
     private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
@@ -215,20 +229,8 @@ public abstract class AbstractPolicyEnforcer {
         return requiredScopes.isEmpty();
     }
 
-    protected AuthzClient getAuthzClient() {
-        return this.authzClient;
-    }
-
-    protected PolicyEnforcerConfig getEnforcerConfig() {
-        return enforcerConfig;
-    }
-
-    protected PolicyEnforcer getPolicyEnforcer() {
-        return policyEnforcer;
-    }
-
     private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
-        return new ClientAuthorizationContext(authzClient) {
+        return new ClientAuthorizationContext(getAuthzClient()) {
             @Override
             public boolean hasPermission(String resourceName, String scopeName) {
                 return granted;
@@ -279,7 +281,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
-        return new ClientAuthorizationContext(accessToken, pathConfig, this.paths, authzClient);
+        return new ClientAuthorizationContext(accessToken, pathConfig, policyEnforcer.getPaths(), getAuthzClient());
     }
 
     private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
@@ -297,4 +299,8 @@ public abstract class AbstractPolicyEnforcer {
     private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
         return permission.getResourceId().equals(actualPathConfig.getId());
     }
+
+    private PathConfig getPathConfig(Request request) {
+        return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
+    }
 }
                diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
index e699203..cf8815c 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
@@ -24,6 +24,8 @@ import java.util.concurrent.locks.LockSupport;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 
 /**
+ * A simple LRU cache implementation supporting expiration and maximum number of entries.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PathCache {
@@ -43,15 +45,6 @@ public class PathCache {
      * Creates a new instance.
      *
      * @param maxEntries the maximum number of entries to keep in the cache
-     */
-    public PathCache(int maxEntries) {
-        this(maxEntries, -1);
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param maxEntries the maximum number of entries to keep in the cache
      * @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire
      */
     public PathCache(final int maxEntries, long maxAge) {
@@ -80,6 +73,10 @@ public class PathCache {
         }
     }
 
+    public boolean containsKey(String uri) {
+        return cache.containsKey(uri);
+    }
+
     public PathConfig get(String uri) {
         if (parkForReadAndCheckInterrupt()) {
             return null;
                diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index fe8aa1a..2d5f0cc 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -17,11 +17,11 @@
  */
 package org.keycloak.adapters.authorization;
 
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -34,13 +34,13 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectedResource;
+import org.keycloak.common.util.PathMatcher;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -69,8 +69,9 @@ public class PolicyEnforcer {
                 }
             }
         });
-        this.pathMatcher = new PathMatcher(this.authzClient);
+
         this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
+        this.pathMatcher = createPathMatcher(authzClient);
 
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Initialization complete. Path configurations:");
@@ -104,11 +105,11 @@ public class PolicyEnforcer {
         return context;
     }
 
-    PolicyEnforcerConfig getEnforcerConfig() {
+    public PolicyEnforcerConfig getEnforcerConfig() {
         return enforcerConfig;
     }
 
-    AuthzClient getClient() {
+    public AuthzClient getClient() {
         return authzClient;
     }
 
@@ -116,11 +117,11 @@ public class PolicyEnforcer {
         return paths;
     }
 
-    void addPath(PathConfig pathConfig) {
-        paths.put(pathConfig.getPath(), pathConfig);
+    public PathMatcher<PathConfig> getPathMatcher() {
+        return pathMatcher;
     }
 
-    KeycloakDeployment getDeployment() {
+    public KeycloakDeployment getDeployment() {
         return deployment;
     }
 
@@ -144,7 +145,7 @@ public class PolicyEnforcer {
     }
 
     private Map<String, PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
-        Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
+        Map<String, PathConfig> paths = Collections.synchronizedMap(new LinkedHashMap<String, PathConfig>());
 
         for (PathConfig pathConfig : enforcerConfig.getPaths()) {
             ResourceRepresentation resource;
@@ -168,36 +169,11 @@ public class PolicyEnforcer {
             }
 
             if (resource == null) {
-                if (enforcerConfig.isCreateResources()) {
-                    LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
-                    ResourceRepresentation representation = new ResourceRepresentation();
-
-                    representation.setName(resourceName);
-                    representation.setType(pathConfig.getType());
-                    representation.setUri(path);
-
-                    HashSet<ScopeRepresentation> scopes = new HashSet<>();
-
-                    for (String scopeName : pathConfig.getScopes()) {
-                        ScopeRepresentation scope = new ScopeRepresentation();
-
-                        scope.setName(scopeName);
-
-                        scopes.add(scope);
-                    }
-
-                    representation.setScopes(scopes);
-
-                    ResourceRepresentation registrationResponse = protectedResource.create(representation);
-
-                    pathConfig.setId(registrationResponse.getId());
-                } else {
-                    throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
-                }
-            } else {
-                pathConfig.setId(resource.getId());
+                throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
             }
 
+            pathConfig.setId(resource.getId());
+
             PathConfig existingPath = null;
 
             for (PathConfig current : paths.values()) {
@@ -222,45 +198,85 @@ public class PolicyEnforcer {
         LOGGER.info("Querying the server for all resources associated with this application.");
         Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
 
-        for (String id : protectedResource.findAll()) {
-            ResourceRepresentation resourceDescription = protectedResource.findById(id);
+        if (!enforcerConfig.getLazyLoadPaths()) {
+            for (String id : protectedResource.findAll()) {
+                ResourceRepresentation resourceDescription = protectedResource.findById(id);
 
-            if (resourceDescription.getUri() != null) {
-                PathConfig pathConfig = createPathConfig(resourceDescription);
-                paths.put(pathConfig.getPath(), pathConfig);
+                if (resourceDescription.getUri() != null) {
+                    PathConfig pathConfig = PathConfig.createPathConfig(resourceDescription);
+                    paths.put(pathConfig.getPath(), pathConfig);
+                }
             }
         }
 
         return paths;
     }
 
-    static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
-        PathConfig pathConfig = new PathConfig();
+    private PathMatcher<PathConfig> createPathMatcher(final AuthzClient authzClient) {
+        final PathCache pathCache = new PathCache(100, 30000);
 
-        pathConfig.setId(resourceDescription.getId());
-        pathConfig.setName(resourceDescription.getName());
+        return new PathMatcher<PathConfig>() {
+            @Override
+            public PathConfig matches(String targetUri) {
+                PathConfig pathConfig = pathCache.get(targetUri);
 
-        String uri = resourceDescription.getUri();
+                if (pathCache.containsKey(targetUri) || pathConfig != null) {
+                    return pathConfig;
+                }
 
-        if (uri == null || "".equals(uri.trim())) {
-            throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
-        }
+                pathConfig = super.matches(targetUri);
 
-        pathConfig.setPath(uri);
+                if (enforcerConfig.getLazyLoadPaths() && (pathConfig == null || pathConfig.getPath().contains("*"))) {
+                    try {
+                        List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
 
-        List<String> scopeNames = new ArrayList<>();
+                        if (!matchingResources.isEmpty()) {
+                            pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
+                            paths.put(pathConfig.getPath(), pathConfig);
+                        }
+                    } catch (Exception cause) {
+                        LOGGER.errorf(cause, "Could not lazy load paths from server");
+                        return null;
+                    }
+                }
 
-        for (ScopeRepresentation scope : resourceDescription.getScopes()) {
-            scopeNames.add(scope.getName());
-        }
+                pathCache.put(targetUri, pathConfig);
 
-        pathConfig.setScopes(scopeNames);
-        pathConfig.setType(resourceDescription.getType());
+                return pathConfig;
+            }
 
-        return pathConfig;
-    }
+            @Override
+            protected String getPath(PathConfig entry) {
+                return entry.getPath();
+            }
 
-    public PathMatcher getPathMatcher() {
-        return pathMatcher;
+            @Override
+            protected Collection<PathConfig> getPaths() {
+                return paths.values();
+            }
+
+            @Override
+            protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
+                if (originalConfig.hasPattern()) {
+                    ProtectedResource resource = authzClient.protection().resource();
+                    List<ResourceRepresentation> search = resource.findByUri(path);
+
+                    if (!search.isEmpty()) {
+                        // resource does exist on the server, cache it
+                        ResourceRepresentation targetResource = search.get(0);
+                        PathConfig config = PathConfig.createPathConfig(targetResource);
+
+                        config.setScopes(originalConfig.getScopes());
+                        config.setMethods(originalConfig.getMethods());
+                        config.setParentConfig(originalConfig);
+                        config.setEnforcementMode(originalConfig.getEnforcementMode());
+
+                        return config;
+                    }
+                }
+
+                return null;
+            }
+        };
     }
 }
                diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
index cf2e91a..80c9e42 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -23,11 +23,11 @@ import java.util.List;
 import java.util.concurrent.Callable;
 
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.util.Http;
 import org.keycloak.authorization.client.util.Throwables;
 import org.keycloak.authorization.client.util.TokenCallable;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -124,11 +124,11 @@ public class ProtectedResource {
     /**
      * Query the server for a resource given its <code>name</code> where the owner is the resource server itself.
      *
-     * @param id the resource name
+     * @param name the resource name
      * @return a {@link ResourceRepresentation}
      */
     public ResourceRepresentation findByName(String name) {
-        String[] representations = find(null, name, null, configuration.getResource(), null, null, null, null);
+        String[] representations = find(null, name, null, configuration.getResource(), null, null, false, null, null);
 
         if (representations.length == 0) {
             return null;
@@ -145,7 +145,7 @@ public class ProtectedResource {
      * @return a {@link ResourceRepresentation}
      */
     public ResourceRepresentation findByName(String name, String ownerId) {
-        String[] representations = find(null, name, null, ownerId, null, null, null, null);
+        String[] representations = find(null, name, null, ownerId, null, null, false, null, null);
 
         if (representations.length == 0) {
             return null;
@@ -163,11 +163,12 @@ public class ProtectedResource {
      * @param owner the resource owner
      * @param type the resource type
      * @param scope the resource scope
+     * @param matchingUri the resource uri. Use this parameter to lookup a resource that best match the given uri
      * @param firstResult the position of the first resource to retrieve
      * @param maxResult the maximum number of resources to retrieve
      * @return an array of strings with the resource ids
      */
-    public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final Integer firstResult, final Integer maxResult) {
+    public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final Integer firstResult, final Integer maxResult) {
         Callable<String[]> callable = new Callable<String[]>() {
             @Override
             public String[] call() throws Exception {
@@ -179,6 +180,7 @@ public class ProtectedResource {
                         .param("owner", owner)
                         .param("type", type)
                         .param("scope", scope)
+                        .param("matchingUri", Boolean.valueOf(matchingUri).toString())
                         .param("deep", Boolean.FALSE.toString())
                         .param("first", firstResult != null ? firstResult.toString() : null)
                         .param("max", maxResult != null ? maxResult.toString() : null)
@@ -199,7 +201,7 @@ public class ProtectedResource {
      */
     public String[] findAll() {
         try {
-            return find(null,null , null, null, null, null, null, null);
+            return find(null,null , null, null, null, null, false, null, null);
         } catch (Exception cause) {
             throw Throwables.handleWrapException("Could not find resource", cause);
         }
@@ -233,7 +235,30 @@ public class ProtectedResource {
      * @param uri the resource uri
      */
     public List<ResourceRepresentation> findByUri(String uri) {
-        String[] ids = find(null, null, uri, null, null, null, null, null);
+        String[] ids = find(null, null, uri, null, null, null, false, null, null);
+
+        if (ids.length == 0) {
+            return Collections.emptyList();
+        }
+
+        List<ResourceRepresentation> representations = new ArrayList<>();
+
+        for (String id : ids) {
+            representations.add(findById(id));
+        }
+
+        return representations;
+    }
+
+    /**
+     * Returns a list of resources that best matches the given {@code uri}. This method queries the server for resources whose
+     * {@link ResourceRepresentation#uri} best matches the given {@code uri}.
+     *
+     * @param uri the resource uri to match
+     * @return a list of resources
+     */
+    public List<ResourceRepresentation> findByMatchingUri(String uri) {
+        String[] ids = find(null, null, uri, null, null, null, true, null, null);
 
         if (ids.length == 0) {
             return Collections.emptyList();
                diff --git a/core/src/main/java/org/keycloak/AuthorizationContext.java b/core/src/main/java/org/keycloak/AuthorizationContext.java
index 0a9b332..538a70f 100644
--- a/core/src/main/java/org/keycloak/AuthorizationContext.java
+++ b/core/src/main/java/org/keycloak/AuthorizationContext.java
@@ -59,53 +59,31 @@ public class AuthorizationContext {
             return false;
         }
 
-        if (current != null) {
-            if (current.getName().equals(resourceName)) {
-                return true;
-            }
-        }
+        for (Permission permission : authorization.getPermissions()) {
+            if (resourceName.equalsIgnoreCase(permission.getResourceName()) || resourceName.equalsIgnoreCase(permission.getResourceId())) {
+                if (scopeName == null) {
+                    return true;
+                }
 
-        if (hasResourcePermission(resourceName)) {
-            for (Permission permission : authorization.getPermissions()) {
-                for (PathConfig pathHolder : paths.values()) {
-                    if (pathHolder.getId().equals(permission.getResourceId())) {
-                        if (permission.getScopes().contains(scopeName)) {
-                            return true;
-                        }
-                    }
+                if (permission.getScopes().contains(scopeName)) {
+                    return true;
                 }
             }
         }
 
-        return false;
-    }
-
-    public boolean hasResourcePermission(String resourceName) {
-        if (this.authzToken == null) {
-            return false;
-        }
-
-        Authorization authorization = this.authzToken.getAuthorization();
-
-        if (authorization == null) {
-            return false;
-        }
-
         if (current != null) {
             if (current.getName().equals(resourceName)) {
                 return true;
             }
         }
 
-        for (Permission permission : authorization.getPermissions()) {
-            if (permission.getResourceName().equals(resourceName) || permission.getResourceId().equals(resourceName)) {
-                return true;
-            }
-        }
-
         return false;
     }
 
+    public boolean hasResourcePermission(String resourceName) {
+        return hasPermission(resourceName, null);
+    }
+
     public boolean hasScopePermission(String scopeName) {
         if (this.authzToken == null) {
             return false;
                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 89dadbf..71b44a4 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
@@ -23,16 +23,14 @@ import java.util.List;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PolicyEnforcerConfig {
 
-    @JsonProperty("create-resources")
-    @JsonInclude(JsonInclude.Include.NON_NULL)
-    private Boolean createResources = Boolean.FALSE;
-
     @JsonProperty("enforcement-mode")
     private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
 
@@ -40,6 +38,9 @@ public class PolicyEnforcerConfig {
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<PathConfig> paths = new ArrayList<>();
 
+    @JsonProperty("lazy-load-paths")
+    private Boolean lazyLoadPaths = Boolean.FALSE;
+
     @JsonProperty("on-deny-redirect-to")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String onDenyRedirectTo;
@@ -48,14 +49,18 @@ public class PolicyEnforcerConfig {
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private UserManagedAccessConfig userManagedAccess;
 
-    public Boolean isCreateResources() {
-        return this.createResources;
-    }
-
     public List<PathConfig> getPaths() {
         return this.paths;
     }
 
+    public Boolean getLazyLoadPaths() {
+        return lazyLoadPaths;
+    }
+
+    public void setLazyLoadPaths(Boolean lazyLoadPaths) {
+        this.lazyLoadPaths = lazyLoadPaths;
+    }
+
     public EnforcementMode getEnforcementMode() {
         return this.enforcementMode;
     }
@@ -68,10 +73,6 @@ public class PolicyEnforcerConfig {
         return this.userManagedAccess;
     }
 
-    public void setCreateResources(Boolean createResources) {
-        this.createResources = createResources;
-    }
-
     public void setPaths(List<PathConfig> paths) {
         this.paths = paths;
     }
@@ -90,6 +91,32 @@ public class PolicyEnforcerConfig {
 
     public static class PathConfig {
 
+        public static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
+            PathConfig pathConfig = new PathConfig();
+
+            pathConfig.setId(resourceDescription.getId());
+            pathConfig.setName(resourceDescription.getName());
+
+            String uri = resourceDescription.getUri();
+
+            if (uri == null || "".equals(uri.trim())) {
+                throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
+            }
+
+            pathConfig.setPath(uri);
+
+            List<String> scopeNames = new ArrayList<>();
+
+            for (ScopeRepresentation scope : resourceDescription.getScopes()) {
+                scopeNames.add(scope.getName());
+            }
+
+            pathConfig.setScopes(scopeNames);
+            pathConfig.setType(resourceDescription.getType());
+
+            return pathConfig;
+        }
+
         private String name;
         private String type;
         private String path;
@@ -231,7 +258,8 @@ public class PolicyEnforcerConfig {
 
     public enum ScopeEnforcementMode {
         ALL,
-        ANY
+        ANY,
+        DISABLED
     }
 
     public static class UserManagedAccessConfig {
                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 071bc32..92f4717 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
@@ -24,8 +24,10 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import org.keycloak.json.StringListMapDeserializer;
 
@@ -45,6 +47,7 @@ public class ResourceRepresentation {
     private String uri;
     private String type;
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("scopes")
     private Set<ScopeRepresentation> scopes;
 
     @JsonProperty("icon_uri")
@@ -52,9 +55,6 @@ public class ResourceRepresentation {
     private ResourceOwnerRepresentation owner;
     private Boolean ownerManagedAccess;
 
-    @JsonInclude(JsonInclude.Include.NON_EMPTY)
-    private List<PolicyRepresentation> policies;
-
     private String displayName;
 
     @JsonDeserialize(using = StringListMapDeserializer.class)
@@ -162,17 +162,31 @@ public class ResourceRepresentation {
     }
 
     public void setUri(String uri) {
-        this.uri = uri;
+        if (uri != null && !"".equalsIgnoreCase(uri.trim())) {
+            this.uri = uri;
+        }
     }
 
     public void setType(String type) {
-        this.type = type;
+        if (type != null && !"".equalsIgnoreCase(type.trim())) {
+            this.type = type;
+        }
     }
 
     public void setScopes(Set<ScopeRepresentation> scopes) {
         this.scopes = scopes;
     }
 
+    /**
+     * TODO: This is a workaround to allow deserialization of UMA resource representation. Jackson 2.19+ support aliases, once we upgrade, change this.
+     *
+     * @param scopes
+     */
+    @JsonSetter("resource_scopes")
+    private void setScopesUma(Set<ScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
     public void setIconUri(String iconUri) {
         this.iconUri = iconUri;
     }
@@ -181,10 +195,25 @@ public class ResourceRepresentation {
         return this.owner;
     }
 
+    @JsonProperty
     public void setOwner(ResourceOwnerRepresentation owner) {
         this.owner = owner;
     }
 
+    @JsonIgnore
+    public void setOwner(String ownerId) {
+        if (ownerId == null) {
+            owner = null;
+            return;
+        }
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+        }
+
+        owner.setId(ownerId);
+    }
+
     public Boolean getOwnerManagedAccess() {
         return ownerManagedAccess;
     }
                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 0c82dc0..b9cecf7 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
@@ -181,6 +181,10 @@ public class JPAResourceStore implements ResourceStore {
                 predicates.add(root.join("scopes").get("id").in(value));
             } else if ("ownerManagedAccess".equals(name)) {
                 predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
+            } else if ("uri".equals(name)) {
+                predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
+            } else if ("uri_not_null".equals(name)) {
+                predicates.add(builder.isNotNull(root.get("uri")));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
                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 538dcef..06545a2 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
@@ -2314,7 +2314,7 @@ public class RepresentationToModel {
         String ownerId = owner.getId();
 
         if (ownerId == null) {
-            throw new RuntimeException("No owner specified for resource [" + resource.getName() + "].");
+            ownerId = resourceServer.getId();
         }
 
         if (!resourceServer.getId().equals(ownerId)) {
                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 b69d991..edde9cc 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -21,6 +21,8 @@ import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
 import static org.keycloak.models.utils.RepresentationToModel.toModel;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -46,6 +48,7 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
@@ -54,6 +57,7 @@ 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.PathMatcher;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
 import org.keycloak.models.ClientModel;
@@ -64,7 +68,7 @@ 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.ErrorResponseException;
 import org.keycloak.services.resources.admin.AdminEventBuilder;
 import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
 
@@ -90,32 +94,18 @@ public class ResourceSetService {
     @Consumes("application/json")
     @Produces("application/json")
     public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource) {
-        return create(uriInfo, resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
-            ResourceRepresentation representation = new ResourceRepresentation();
-
-            representation.setId(resource1.getId());
+        if (resource == null) {
+            return Response.status(Status.BAD_REQUEST).build();
+        }
 
-            return representation;
-        });
-    }
+        ResourceRepresentation newResource = create(resource);
 
-    public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
-        Response response = create(resource, toRepresentation);
         audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
-        return response;
-    }
-
-    public Response create(ResourceRepresentation resource) {
-        return create(resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
-            ResourceRepresentation representation = new ResourceRepresentation();
-
-            representation.setId(resource1.getId());
 
-            return representation;
-        });
+        return Response.status(Status.CREATED).entity(newResource).build();
     }
 
-    public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
+    public ResourceRepresentation create(ResourceRepresentation resource) {
         requireManage();
         StoreFactory storeFactory = this.authorization.getStoreFactory();
         ResourceOwnerRepresentation owner = resource.getOwner();
@@ -123,21 +113,22 @@ public class ResourceSetService {
         if (owner == null) {
             owner = new ResourceOwnerRepresentation();
             owner.setId(resourceServer.getId());
+            resource.setOwner(owner);
         }
 
         String ownerId = owner.getId();
 
         if (ownerId == null) {
-            return ErrorResponse.error("You must specify the resource owner.", Status.BAD_REQUEST);
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "You must specify the resource owner.", Status.BAD_REQUEST);
         }
 
         Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), ownerId, this.resourceServer.getId());
 
         if (existingResource != null) {
-            return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Resource with name [" + resource.getName() + "] already exists.", Status.CONFLICT);
         }
 
-        return Response.status(Status.CREATED).entity(toRepresentation.apply(toModel(resource, this.resourceServer, authorization))).build();
+        return toRepresentation(toModel(resource, this.resourceServer, authorization), resourceServer, authorization);
     }
 
     @Path("{id}")
@@ -198,10 +189,10 @@ public class ResourceSetService {
     @NoCache
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        return findById(id, (Function<Resource, ResourceRepresentation>) resource -> toRepresentation(resource, resourceServer, authorization, true));
+        return findById(id, resource -> toRepresentation(resource, resourceServer, authorization, true));
     }
 
-    public Response findById(@PathParam("id") String id, Function<Resource, ?> toRepresentation) {
+    public Response findById(String id, Function<Resource, ? extends ResourceRepresentation> toRepresentation) {
         requireView();
         StoreFactory storeFactory = authorization.getStoreFactory();
         Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
@@ -340,10 +331,11 @@ public class ResourceSetService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult) {
-        return find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
+        return find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
     }
 
     public Response find(@QueryParam("_id") String id,
@@ -352,6 +344,7 @@ public class ResourceSetService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult,
@@ -413,9 +406,38 @@ public class ResourceSetService {
             search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
         }
 
+        List<Resource> resources = storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
+
+        if (matchingUri != null && matchingUri && resources.isEmpty()) {
+            HashMap<String, String[]> attributes = new HashMap<>();
+
+            attributes.put("uri_not_null", new String[] {"true"});
+            attributes.put("owner", new String[] {resourceServer.getId()});
+
+            List<Resource> serverResources = storeFactory.getResourceStore().findByResourceServer(attributes, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
+            PathMatcher<Resource> pathMatcher = new PathMatcher<Resource>() {
+                @Override
+                protected String getPath(Resource entry) {
+                    return entry.getUri();
+                }
+
+                @Override
+                protected Collection<Resource> getPaths() {
+                    return serverResources;
+                }
+            };
+
+            Resource matches = pathMatcher.matches(uri);
+
+            if (matches != null) {
+                resources = Arrays.asList(matches);
+            }
+        }
+
         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()
+                resources.stream()
                         .map(resource -> toRepresentation.apply(resource, finalDeep))
                         .collect(Collectors.toList()))
                 .build();
@@ -437,7 +459,7 @@ public class ResourceSetService {
         audit(uriInfo, resource, null, operation);
     }
 
-    private void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
+    public void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
         if (authorization.getRealm().isAdminEventsEnabled()) {
             if (id != null) {
                 adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
                diff --git a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
index 8a811f1..80226cf 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
@@ -65,7 +65,7 @@ public class ProtectionService {
 
         ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
 
-        ResourceService resource = new ResourceService(resourceServer, identity, resourceManager, this.authorization);
+        ResourceService resource = new ResourceService(resourceServer, identity, resourceManager);
 
         ResteasyProviderFactory.getInstance().injectProperties(resource);
 
                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 21fd27c..1fbe5f9 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
@@ -18,8 +18,6 @@
 package org.keycloak.authorization.protection.resource;
 
 import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -36,16 +34,13 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
-import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.ResourceSetService;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
-import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation;
+import org.keycloak.events.admin.OperationType;
 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.ErrorResponseException;
 
 /**
@@ -56,39 +51,51 @@ public class ResourceService {
     private final ResourceServer resourceServer;
     private final ResourceSetService resourceManager;
     private final Identity identity;
-    private final AuthorizationProvider authorization;
 
-    public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager, AuthorizationProvider authorization) {
+    public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager) {
         this.identity = identity;
         this.resourceServer = resourceServer;
         this.resourceManager = resourceManager;
-        this.authorization = authorization;
     }
 
     @POST
     @Consumes("application/json")
     @Produces("application/json")
-    public Response create(@Context  UriInfo uriInfo, UmaResourceRepresentation umaResource) {
+    public Response create(@Context  UriInfo uriInfo, UmaResourceRepresentation resource) {
         checkResourceServerSettings();
-        if (umaResource == null) {
+
+        if (resource == null) {
             return Response.status(Status.BAD_REQUEST).build();
         }
-        return this.resourceManager.create(uriInfo, toResourceRepresentation(umaResource), (Function<Resource, UmaResourceRepresentation>) this::toUmaRepresentation);
+
+        ResourceOwnerRepresentation owner = resource.getOwner();
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+            resource.setOwner(owner);
+        }
+
+        String ownerId = owner.getId();
+
+        if (ownerId == null) {
+            ownerId = this.identity.getId();
+        }
+
+        owner.setId(ownerId);
+
+        ResourceRepresentation newResource = resourceManager.create(resource);
+
+        resourceManager.audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
+
+        return Response.status(Status.CREATED).entity(new UmaResourceRepresentation(newResource)).build();
     }
 
     @Path("{id}")
     @PUT
     @Consumes("application/json")
     @Produces("application/json")
-    public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, UmaResourceRepresentation representation) {
-        ResourceRepresentation resource = toResourceRepresentation(representation);
-        Response response = this.resourceManager.update(uriInfo, id, resource);
-
-        if (response.getEntity() instanceof ResourceRepresentation) {
-            return Response.noContent().build();
-        }
-
-        return response;
+    public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ResourceRepresentation resource) {
+        return this.resourceManager.update(uriInfo, id, resource);
     }
 
     @Path("/{id}")
@@ -102,7 +109,7 @@ public class ResourceService {
     @GET
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        return this.resourceManager.findById(id, (Function<Resource, UmaResourceRepresentation>) resource -> toUmaRepresentation(resource));
+        return this.resourceManager.findById(id, UmaResourceRepresentation::new);
     }
 
     @GET
@@ -114,75 +121,11 @@ public class ResourceService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult) {
-        return resourceManager.find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
-    }
-
-    private ResourceRepresentation toResourceRepresentation(UmaResourceRepresentation umaResource) {
-        ResourceRepresentation resource = new ResourceRepresentation();
-
-        resource.setId(umaResource.getId());
-        resource.setIconUri(umaResource.getIconUri());
-        resource.setName(umaResource.getName());
-        resource.setUri(umaResource.getUri());
-        resource.setType(umaResource.getType());
-        resource.setOwnerManagedAccess(umaResource.getOwnerManagedAccess());
-
-        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
-        String ownerId = umaResource.getOwner();
-
-        if (ownerId == null) {
-            ownerId = this.identity.getId();
-        }
-
-        owner.setId(ownerId);
-        resource.setOwner(owner);
-
-        resource.setScopes(umaResource.getScopes().stream().map(representation -> {
-            ScopeRepresentation scopeRepresentation = new ScopeRepresentation();
-
-            scopeRepresentation.setId(representation.getId());
-            scopeRepresentation.setName(representation.getName());
-            scopeRepresentation.setIconUri(representation.getIconUri());
-
-            return scopeRepresentation;
-        }).collect(Collectors.toSet()));
-
-        resource.setAttributes(umaResource.getAttributes());
-
-        return resource;
-    }
-
-    private UmaResourceRepresentation toUmaRepresentation(Resource model) {
-        if (model == null) {
-            return null;
-        }
-
-        UmaResourceRepresentation resource = new UmaResourceRepresentation();
-
-        resource.setId(model.getId());
-        resource.setIconUri(model.getIconUri());
-        resource.setName(model.getName());
-        resource.setUri(model.getUri());
-        resource.setType(model.getType());
-
-        if (model.getOwner() != null) {
-            resource.setOwner(model.getOwner());
-        }
-
-        resource.setScopes(model.getScopes().stream().map(scopeRepresentation -> {
-            UmaScopeRepresentation umaScopeRep = new UmaScopeRepresentation();
-            umaScopeRep.setId(scopeRepresentation.getId());
-            umaScopeRep.setName(scopeRepresentation.getName());
-            umaScopeRep.setIconUri(scopeRepresentation.getIconUri());
-            return umaScopeRep;
-        }).collect(Collectors.toSet()));
-
-        resource.setAttributes(model.getAttributes());
-
-        return resource;
+        return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
     }
 
     private void checkResourceServerSettings() {
                diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java b/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java
new file mode 100644
index 0000000..302dd96
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.protection.resource;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaResourceRepresentation extends ResourceRepresentation {
+
+    public UmaResourceRepresentation() {
+
+    }
+
+    public UmaResourceRepresentation(ResourceRepresentation resource) {
+        setId(resource.getId());
+        setName(resource.getName());
+        setType(resource.getType());
+        setUri(resource.getUri());
+        setIconUri(resource.getIconUri());
+        setOwner(resource.getOwner());
+        setScopes(resource.getScopes());
+        setDisplayName(resource.getDisplayName());
+        setOwnerManagedAccess(resource.getOwnerManagedAccess());
+    }
+
+    public UmaResourceRepresentation(Resource resource) {
+        setId(resource.getId());
+        setName(resource.getName());
+        setType(resource.getType());
+        setUri(resource.getUri());
+        setIconUri(resource.getIconUri());
+        setOwner(resource.getOwner());
+        setScopes(resource.getScopes().stream().map(scope -> new ScopeRepresentation(scope.getName())).collect(Collectors.toSet()));
+        setDisplayName(resource.getDisplayName());
+        setOwnerManagedAccess(resource.isOwnerManagedAccess());
+        setAttributes(resource.getAttributes());
+    }
+
+    @JsonProperty("resource_scopes")
+    @Override
+    public Set<ScopeRepresentation> getScopes() {
+        return super.getScopes();
+    }
+
+    @JsonProperty("resource_scopes")
+    @Override
+    public void setScopes(Set<ScopeRepresentation> scopes) {
+        super.setScopes(scopes);
+    }
+}
                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 f6beac5..a84765b 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -68,6 +68,7 @@ import org.keycloak.representations.idm.ScopeMappingRepresentation;
 import org.keycloak.representations.idm.UserConsentRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 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.ResourceServerRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -315,7 +316,7 @@ public class ExportUtils {
                     ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
 
                     if (rep.getOwner().getId().equals(settingsModel.getId())) {
-                        rep.setOwner(null);
+                        rep.setOwner((ResourceOwnerRepresentation) null);
                     } else {
                         rep.getOwner().setId(null);
                     }
                diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
index 60970d8..8dadaaf 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
@@ -155,7 +155,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
             PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
 
             enforcerConfig.setEnforcementMode(null);
-            enforcerConfig.setCreateResources(null);
+            enforcerConfig.setLazyLoadPaths(null);
 
             rep.setEnforcerConfig(enforcerConfig);
 
                diff --git a/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
new file mode 100644
index 0000000..47437dc
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
@@ -0,0 +1,78 @@
+{
+  "realm": "photoz",
+  "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8180/auth",
+  "ssl-required": "external",
+  "resource": "photoz-restful-api",
+  "bearer-only" : true,
+  "credentials": {
+    "jwt": {
+      "client-key-password": "password",
+      "client-keystore-file": "classpath:keystore.jks",
+      "client-keystore-password": "password",
+      "client-key-alias": "secure-portal",
+      "token-timeout": 10,
+      "client-keystore-type": "jks"
+    }
+  },
+  "policy-enforcer": {
+    "enforcement-mode": "PERMISSIVE",
+    "user-managed-access": {},
+    "lazy-load-paths": true,
+    "paths": [
+      {
+        "name" : "Album Resource",
+        "path" : "/album",
+        "methods" : [
+          {
+            "method": "GET",
+            "scopes-enforcement-mode" : "DISABLED"
+          }
+        ]
+      },
+      {
+        "name" : "Album Resource",
+        "path" : "/album/{id}",
+        "methods" : [
+          {
+            "method": "DELETE",
+            "scopes" : ["album:delete"]
+          },
+          {
+            "method": "GET",
+            "scopes" : ["album:view"]
+          }
+        ]
+      },
+      {
+        "path" : "/profile"
+      },
+      {
+        "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"
+          }
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file
                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 94feb72..40b1242 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
@@ -3,8 +3,8 @@ package org.keycloak.example.photoz.album;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.example.photoz.entity.Album;
 import org.keycloak.example.photoz.util.Transaction;
                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 a0f8711..dd0cab1 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
@@ -21,6 +21,16 @@
     "paths": [
       {
         "name" : "Album Resource",
+        "path" : "/album",
+        "methods" : [
+          {
+            "method": "GET",
+            "scopes-enforcement-mode" : "DISABLED"
+          }
+        ]
+      },
+      {
+        "name" : "Album Resource",
         "path" : "/album/{id}",
         "methods" : [
           {
                diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json
new file mode 100644
index 0000000..35f76d2
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json
@@ -0,0 +1,15 @@
+{
+  "realm": "servlet-authz",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8180/auth",
+  "ssl-required" : "external",
+  "resource" : "servlet-authz-app",
+  "public-client" : false,
+  "credentials": {
+    "secret": "secret"
+  },
+  "policy-enforcer": {
+    "on-deny-redirect-to" : "/servlet-authz-app/accessDenied.jsp",
+    "lazy-load-paths": true
+  }
+}
\ 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
index 0dd6a14..d6e7f00 100644
--- 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
@@ -8,6 +8,7 @@
     },
     "policy-enforcer": {
         "on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
+        "lazy-load-paths": false,
         "paths": [
             {
                 "name": "Welcome Resource",
@@ -31,7 +32,7 @@
             },
             {
                 "name": "Pattern 5",
-                "path": "/resource/{pattern}/resource-d"
+                "path": "/a/{pattern}/resource-d"
             },
             {
                 "name": "Pattern 6",
                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 f87d481..90a6692 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
@@ -203,10 +203,16 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
 
         this.loginPage.form().login(username, password);
         waitForPageToLoad();//guess
-        
-        // simple check if we are at the consent page, if so just click 'Yes'
-        if (this.consentPage.isCurrent()) {
-            consentPage.confirm();
+
+        try {
+            if (!isCurrent()) {
+                // simple check if we are at the consent page, if so just click 'Yes'
+                if (this.consentPage.isCurrent()) {
+                    consentPage.confirm();
+                }
+            }
+        } catch (Exception ignore) {
+            // ignore errors when checking consent page, if an error tests will also fail
         }
 
         pause(WAIT_AFTER_OPERATION);
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java
new file mode 100644
index 0000000..7f47bb9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.BeforeClass;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractBaseServletAuthzAdapterTest extends AbstractExampleAdapterTest {
+
+    protected static final String REALM_NAME = "servlet-authz";
+    protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
+
+    @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-authz-app/servlet-authz-realm.json")));
+    }
+
+    protected void performTests(ExceptionRunnable assertion) {
+        performTests(() -> importResourceServerSettings(), assertion);
+    }
+
+    protected 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);
+        }
+    }
+
+    protected boolean hasLink(String text) {
+        return getLink(text) != null;
+    }
+
+    protected boolean hasText(String text) {
+        return this.driver.getPageSource().contains(text);
+    }
+
+    private WebElement getLink(String text) {
+        return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
+    }
+
+    protected void importResourceServerSettings() throws FileNotFoundException {
+        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
+    }
+
+    protected AuthorizationResource getAuthorizationResource() {
+        return getClientResource(RESOURCE_SERVER_ID).authorization();
+    }
+
+    protected 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);
+    }
+
+    protected void login(String username, String password) {
+        try {
+            navigateTo();
+            Thread.sleep(2000);
+            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
+                Thread.sleep(2000);
+                logOut();
+                navigateTo();
+            }
+
+            Thread.sleep(2000);
+
+            this.loginPage.form().login(username, password);
+        } catch (Exception cause) {
+            throw new RuntimeException("Login failed", cause);
+        }
+    }
+
+    private void navigateTo() {
+        this.driver.navigate().to(getResourceServerUrl());
+        WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
+    }
+
+    protected boolean wasDenied() {
+        return this.driver.getPageSource().contains("You can not access this resource.");
+    }
+
+    protected URL getResourceServerUrl() {
+        try {
+            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Could not obtain resource server url.", e);
+        }
+    }
+
+    protected void navigateToDynamicMenuPage() {
+        navigateTo();
+        getLink("Dynamic Menu").click();
+    }
+
+    protected void navigateToUserPremiumPage() {
+        navigateTo();
+        getLink("User Premium").click();
+    }
+
+    protected void navigateToAdminPage() {
+        navigateTo();
+        getLink("Administration").click();
+    }
+
+    protected void updatePermissionPolicies(String permissionName, String... policyNames) {
+        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
+            if (permissionName.equalsIgnoreCase(policy.getName())) {
+                StringBuilder policies = new StringBuilder("[");
+
+                for (String policyName : policyNames) {
+                    if (policies.length() > 1) {
+                        policies.append(",");
+                    }
+                    policies.append("\"").append(policyName).append("\"");
+
+                }
+
+                policies.append("]");
+
+                policy.getConfig().put("applyPolicies", policies.toString());
+                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
+            }
+        }
+    }
+
+    protected void createUserPolicy(String name, String... userNames) {
+        UserPolicyRepresentation policy = new UserPolicyRepresentation();
+
+        policy.setName(name);
+
+        for (String userName : userNames) {
+            policy.addUser(userName);
+        }
+
+        assertFalse(policy.getUsers().isEmpty());
+
+        Response response = getAuthorizationResource().policies().user().create(policy);
+        response.close();
+    }
+
+    protected interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+}
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
index e1ad409..052150c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
@@ -28,7 +28,7 @@ import org.junit.Test;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractPermissiveModeAdapterTest extends AbstractServletAuthzAdapterTest {
+public abstract class AbstractPermissiveModeAdapterTest extends AbstractBaseServletAuthzAdapterTest {
 
     @Deployment(name = RESOURCE_SERVER_ID, managed = false)
     public static WebArchive deployment() throws IOException {
                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 4cc9d4a..80ddd05 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
@@ -75,7 +75,7 @@ import org.keycloak.util.JsonSerialization;
 public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAdapterTest {
 
     private static final String REALM_NAME = "photoz";
-    private static final String RESOURCE_SERVER_ID = "photoz-restful-api";
+    protected static final String RESOURCE_SERVER_ID = "photoz-restful-api";
     private static final int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
 
     @ArquillianResource
@@ -118,16 +118,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         testRealms.add(realm);
     }
 
-    @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
-    public static WebArchive deploymentClient() throws IOException {
-        return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
-    }
-
-    @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
-    public static WebArchive deploymentResourceServer() throws IOException {
-        return exampleDeployment(RESOURCE_SERVER_ID);
-    }
-
     @Override
     public void beforeAbstractKeycloakTest() throws Exception {
         super.beforeAbstractKeycloakTest();
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..3e35a33
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Before;
+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.ResourcesResource;
+import org.keycloak.admin.client.resource.RoleResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
+
+    @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
+    public static WebArchive deploymentClient() throws IOException {
+        return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
+    }
+
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
+    public static WebArchive deploymentResourceServer() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID)
+                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/photoz/keycloak-lazy-load-path-authz-service.json"), "keycloak.json");
+    }
+
+}
\ No newline at end of file
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
index f6a8bb2..71297a9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
  * and other contributors as indicated by the @author tags.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,196 +16,19 @@
  */
 package org.keycloak.testsuite.adapter.example.authorization;
 
-import static org.junit.Assert.assertFalse;
-import static org.keycloak.testsuite.util.IOUtil.loadJson;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.WaitUtils.pause;
+import java.io.IOException;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-import javax.ws.rs.core.Response;
-
-import org.jboss.arquillian.container.test.api.Deployer;
-import org.jboss.arquillian.test.api.ArquillianResource;
-import org.junit.BeforeClass;
-import org.keycloak.admin.client.resource.AuthorizationResource;
-import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.ClientsResource;
-import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.authorization.PolicyRepresentation;
-import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
-import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
-import org.keycloak.testsuite.ProfileAssume;
-import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
-import org.keycloak.testsuite.util.WaitUtils;
-import org.openqa.selenium.By;
-import org.openqa.selenium.WebElement;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAdapterTest {
-
-    protected static final String REALM_NAME = "servlet-authz";
-    protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
-
-    @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-authz-app/servlet-authz-realm.json")));
-    }
-
-    protected void performTests(ExceptionRunnable assertion) {
-        performTests(() -> importResourceServerSettings(), assertion);
-    }
-
-    protected 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);
-        }
-    }
-
-    protected boolean hasLink(String text) {
-        return getLink(text) != null;
-    }
-
-    protected boolean hasText(String text) {
-        return this.driver.getPageSource().contains(text);
-    }
-
-    private WebElement getLink(String text) {
-        return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
-    }
-
-    protected void importResourceServerSettings() throws FileNotFoundException {
-        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
-    }
-
-    protected AuthorizationResource getAuthorizationResource() {
-        return getClientResource(RESOURCE_SERVER_ID).authorization();
-    }
-
-    protected 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);
-    }
-
-    protected void login(String username, String password) {
-        try {
-            navigateTo();
-            Thread.sleep(2000);
-            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
-                Thread.sleep(2000);
-                logOut();
-                navigateTo();
-            }
+public abstract class AbstractServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
 
-            Thread.sleep(2000);
-
-            this.loginPage.form().login(username, password);
-        } catch (Exception cause) {
-            throw new RuntimeException("Login failed", cause);
-        }
-    }
-
-    private void navigateTo() {
-        this.driver.navigate().to(getResourceServerUrl());
-        WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
-    }
-
-    protected boolean wasDenied() {
-        return this.driver.getPageSource().contains("You can not access this resource.");
-    }
-
-    protected URL getResourceServerUrl() {
-        try {
-            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
-        } catch (MalformedURLException e) {
-            throw new RuntimeException("Could not obtain resource server url.", e);
-        }
-    }
-
-    protected void navigateToDynamicMenuPage() {
-        navigateTo();
-        getLink("Dynamic Menu").click();
-    }
-
-    protected void navigateToUserPremiumPage() {
-        navigateTo();
-        getLink("User Premium").click();
-    }
-
-    protected void navigateToAdminPage() {
-        navigateTo();
-        getLink("Administration").click();
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+    public static WebArchive deployment() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID);
     }
 
-    protected void updatePermissionPolicies(String permissionName, String... policyNames) {
-        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
-            if (permissionName.equalsIgnoreCase(policy.getName())) {
-                StringBuilder policies = new StringBuilder("[");
-
-                for (String policyName : policyNames) {
-                    if (policies.length() > 1) {
-                        policies.append(",");
-                    }
-                    policies.append("\"").append(policyName).append("\"");
-
-                }
-
-                policies.append("]");
-
-                policy.getConfig().put("applyPolicies", policies.toString());
-                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
-            }
-        }
-    }
-
-    protected void createUserPolicy(String name, String... userNames) {
-        UserPolicyRepresentation policy = new UserPolicyRepresentation();
-
-        policy.setName(name);
-
-        for (String userName : userNames) {
-            policy.addUser(userName);
-        }
-
-        assertFalse(policy.getUsers().isEmpty());
-
-        Response response = getAuthorizationResource().policies().user().create(policy);
-        response.close();
-    }
-
-    protected interface ExceptionRunnable {
-        void run() throws Exception;
-    }
 }
                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 63852d0..35c936b 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
@@ -19,14 +19,11 @@ package org.keycloak.testsuite.adapter.example.authorization;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
 
-import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientPoliciesResource;
 import org.keycloak.admin.client.resource.RealmResource;
@@ -46,12 +43,7 @@ import org.keycloak.testsuite.util.WaitUtils;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractServletAuthzAdapterTest {
-
-    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
-    public static WebArchive deployment() throws IOException {
-        return exampleDeployment(RESOURCE_SERVER_ID);
-    }
+public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractBaseServletAuthzAdapterTest {
 
     @Test
     public void testCanNotAccessWhenEnforcing() throws Exception {
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..0989aa5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientPoliciesResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RoleScopeResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.util.WaitUtils;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
+
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+    public static WebArchive deployment() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID)
+                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/keycloak-lazy-load-authz-service.json"), "keycloak.json");
+    }
+
+}
                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
index 5c6b0eb..9afed01 100644
--- 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
@@ -177,21 +177,21 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
         performTests(() -> {
             login("alice", "alice");
 
-            navigateTo("/resource/a/resource-d");
+            navigateTo("/a/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");
+            navigateTo("/a/a/resource-d");
             assertTrue(wasDenied());
-            navigateTo("/resource/b/resource-d");
+            navigateTo("/a/b/resource-d");
             assertTrue(wasDenied());
 
             updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
             login("alice", "alice");
-            navigateTo("/resource/b/resource-d");
+            navigateTo("/a/b/resource-d");
             assertFalse(wasDenied());
         });
     }
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
index 32865ec..bbff687 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
@@ -17,12 +17,17 @@
 
 package org.keycloak.testsuite.admin.client.authorization;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
 import java.util.stream.Collectors;
 
+import org.junit.Test;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.util.JsonSerialization;
@@ -35,12 +40,60 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
 
     private AuthzClient authzClient;
 
+    @Test
+    public void testFindMatchingUri() {
+        doCreateResource(new ResourceRepresentation("/*", Collections.emptySet(), "/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/*", Collections.emptySet(), "/resources/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resource", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+
+        AuthzClient authzClient = getAuthzClient();
+
+        List<ResourceRepresentation> resources = authzClient.protection().resource().findByMatchingUri("/test");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/test");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/{pattern}/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/sub-resources/c/d");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/sub-resources/{pattern}/*", resources.get(0).getUri());
+    }
+
     @Override
     protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation resource = toResourceRepresentation(newResource);
+        ResourceRepresentation resource = toResourceRepresentation(newResource);
 
         AuthzClient authzClient = getAuthzClient();
-        org.keycloak.authorization.client.representation.ResourceRepresentation response = authzClient.protection().resource().create(resource);
+        ResourceRepresentation response = authzClient.protection().resource().create(resource);
 
         return toResourceRepresentation(authzClient, response.getId());
     }
@@ -60,7 +113,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
     }
 
     private ResourceRepresentation toResourceRepresentation(AuthzClient authzClient, String id) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation created = authzClient.protection().resource().findById(id);
+        ResourceRepresentation created = authzClient.protection().resource().findById(id);
         ResourceRepresentation resourceRepresentation = new ResourceRepresentation();
 
         resourceRepresentation.setId(created.getId());
@@ -68,11 +121,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         resourceRepresentation.setIconUri(created.getIconUri());
         resourceRepresentation.setUri(created.getUri());
         resourceRepresentation.setType(created.getType());
-        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
-
-        owner.setId(created.getOwner());
-
-        resourceRepresentation.setOwner(owner);
+        resourceRepresentation.setOwner(created.getOwner());
         resourceRepresentation.setScopes(created.getScopes().stream().map(scopeRepresentation -> {
             ScopeRepresentation scope = new ScopeRepresentation();
 
@@ -88,8 +137,8 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         return resourceRepresentation;
     }
 
-    private org.keycloak.authorization.client.representation.ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation resource = new org.keycloak.authorization.client.representation.ResourceRepresentation();
+    private ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
+        ResourceRepresentation resource = new ResourceRepresentation();
 
         resource.setId(newResource.getId());
         resource.setName(newResource.getName());
@@ -102,7 +151,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         }
 
         resource.setScopes(newResource.getScopes().stream().map(scopeRepresentation -> {
-            org.keycloak.authorization.client.representation.ScopeRepresentation scope = new org.keycloak.authorization.client.representation.ScopeRepresentation();
+            ScopeRepresentation scope = new ScopeRepresentation();
 
             scope.setName(scopeRepresentation.getName());
             scope.setIconUri(scopeRepresentation.getIconUri());
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
index 7fece47..63726b5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -42,7 +42,6 @@ import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.authorization.client.util.HttpResponseException;
 import org.keycloak.jose.jws.JWSInput;
@@ -56,6 +55,7 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
index b55f348..7393a94 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
@@ -38,8 +38,6 @@ import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.representations.AccessToken;
@@ -48,7 +46,9 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.UserBuilder;
                diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
index 42cde44..6139c22 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
@@ -24,6 +24,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
  */
 @AppServerContainer("app-server-wildfly")
 //@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleAdapterTest {
+public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleNoLazyLoadPathsAdapterTest {
 
 }
                diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..a23ba15
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleLazyLoadPathsAdapterTest {
+
+}
                diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
index 13a444f..7a7223d 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
@@ -26,6 +26,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
 @RunAsClient
 @AppServerContainer("app-server-wildfly")
 //@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
+public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
 
 }
                diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..7964736
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunAsClient
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzLazyLoadPathsAdapterTest {
+
+}