keycloak-memoizeit

Changes

authz/client/pom.xml 101(+101 -0)

authz/pom.xml 30(+30 -0)

distribution/docs-dist/build.xml 139(+0 -139)

docbook/auth-server-docs/pom.xml 159(+0 -159)

docbook/auth-server-docs/reference/en/en-US/master.xml 181(+0 -181)

docbook/auth-server-docs/reference/en/en-US/modules/access-types.xml 71(+0 -71)

docbook/auth-server-docs/reference/en/en-US/modules/adapter_error_handling.xml 75(+0 -75)

docbook/auth-server-docs/reference/en/en-US/modules/adapter-config.xml 421(+0 -421)

docbook/auth-server-docs/reference/en/en-US/modules/adapter-context.xml 12(+0 -12)

docbook/auth-server-docs/reference/en/en-US/modules/admin-permissions.xml 102(+0 -102)

docbook/auth-server-docs/reference/en/en-US/modules/admin-rest-api.xml 35(+0 -35)

docbook/auth-server-docs/reference/en/en-US/modules/application-clustering.xml 253(+0 -253)

docbook/auth-server-docs/reference/en/en-US/modules/auth-spi.xml 1030(+0 -1030)

docbook/auth-server-docs/reference/en/en-US/modules/cache.xml 81(+0 -81)

docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml 232(+0 -232)

docbook/auth-server-docs/reference/en/en-US/modules/clients.xml 57(+0 -57)

docbook/auth-server-docs/reference/en/en-US/modules/clustering.xml 235(+0 -235)

docbook/auth-server-docs/reference/en/en-US/modules/cors.xml 72(+0 -72)

docbook/auth-server-docs/reference/en/en-US/modules/custom-attributes.xml 167(+0 -167)

docbook/auth-server-docs/reference/en/en-US/modules/direct-access.xml 185(+0 -185)

docbook/auth-server-docs/reference/en/en-US/modules/email.xml 57(+0 -57)

docbook/auth-server-docs/reference/en/en-US/modules/events.xml 120(+0 -120)

docbook/auth-server-docs/reference/en/en-US/modules/export-import.xml 160(+0 -160)

docbook/auth-server-docs/reference/en/en-US/modules/fuse-adapter.xml 69(+0 -69)

docbook/auth-server-docs/reference/en/en-US/modules/groups.xml 48(+0 -48)

docbook/auth-server-docs/reference/en/en-US/modules/identity-broker.xml 1551(+0 -1551)

docbook/auth-server-docs/reference/en/en-US/modules/installed-applications.xml 42(+0 -42)

docbook/auth-server-docs/reference/en/en-US/modules/jaas.xml 59(+0 -59)

docbook/auth-server-docs/reference/en/en-US/modules/javascript-adapter.xml 429(+0 -429)

docbook/auth-server-docs/reference/en/en-US/modules/jboss-adapter.xml 303(+0 -303)

docbook/auth-server-docs/reference/en/en-US/modules/jetty8-adapter.xml 67(+0 -67)

docbook/auth-server-docs/reference/en/en-US/modules/jetty9-adapter.xml 173(+0 -173)

docbook/auth-server-docs/reference/en/en-US/modules/kerberos.xml 297(+0 -297)

docbook/auth-server-docs/reference/en/en-US/modules/logout.xml 26(+0 -26)

docbook/auth-server-docs/reference/en/en-US/modules/MigrationFromOlderVersions.xml 675(+0 -675)

docbook/auth-server-docs/reference/en/en-US/modules/multi-tenancy.xml 73(+0 -73)

docbook/auth-server-docs/reference/en/en-US/modules/openshift.xml 87(+0 -87)

docbook/auth-server-docs/reference/en/en-US/modules/Overview.xml 188(+0 -188)

docbook/auth-server-docs/reference/en/en-US/modules/per-realm-admin-permissions.xml 82(+0 -82)

docbook/auth-server-docs/reference/en/en-US/modules/protocol-mappers.xml 34(+0 -34)

docbook/auth-server-docs/reference/en/en-US/modules/providers.xml 410(+0 -410)

docbook/auth-server-docs/reference/en/en-US/modules/proxy.xml 419(+0 -419)

docbook/auth-server-docs/reference/en/en-US/modules/recaptcha.xml 43(+0 -43)

docbook/auth-server-docs/reference/en/en-US/modules/roles.xml 48(+0 -48)

docbook/auth-server-docs/reference/en/en-US/modules/saml.xml 355(+0 -355)

docbook/auth-server-docs/reference/en/en-US/modules/security-vulnerabilities.xml 194(+0 -194)

docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml 1001(+0 -1001)

docbook/auth-server-docs/reference/en/en-US/modules/service-accounts.xml 75(+0 -75)

docbook/auth-server-docs/reference/en/en-US/modules/servlet-filter-adapter.xml 77(+0 -77)

docbook/auth-server-docs/reference/en/en-US/modules/social-config.xml 60(+0 -60)

docbook/auth-server-docs/reference/en/en-US/modules/social-facebook.xml 62(+0 -62)

docbook/auth-server-docs/reference/en/en-US/modules/social-github.xml 44(+0 -44)

docbook/auth-server-docs/reference/en/en-US/modules/social-google.xml 67(+0 -67)

docbook/auth-server-docs/reference/en/en-US/modules/social-spi.xml 29(+0 -29)

docbook/auth-server-docs/reference/en/en-US/modules/social-twitter.xml 55(+0 -55)

docbook/auth-server-docs/reference/en/en-US/modules/spring-boot-adapter.xml 89(+0 -89)

docbook/auth-server-docs/reference/en/en-US/modules/spring-security-adapter.xml 312(+0 -312)

docbook/auth-server-docs/reference/en/en-US/modules/themes.xml 318(+0 -318)

docbook/auth-server-docs/reference/en/en-US/modules/timeouts.xml 117(+0 -117)

docbook/auth-server-docs/reference/en/en-US/modules/tomcat-adapter.xml 109(+0 -109)

docbook/auth-server-docs/reference/en/en-US/modules/user-federation.xml 342(+0 -342)

docbook/auth-server-docs/reference/en/images/add-provider-dialog.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/add-provider-select.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/domain-mode.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/email-simple-example.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/identity_broker_flow.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/update-server-config-dialog.png 0(+0 -0)

docbook/auth-server-docs/reference/en/images/update-server-config-select.png 0(+0 -0)

docbook/saml-adapter-docs/pom.xml 151(+0 -151)

docbook/saml-adapter-docs/reference/en/en-US/master.xml 84(+0 -84)

docbook/saml-adapter-docs/reference/en/en-US/modules/adapter_error_handling.xml 59(+0 -59)

docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml 515(+0 -515)

docbook/saml-adapter-docs/reference/en/en-US/modules/assertion-api.xml 126(+0 -126)

docbook/saml-adapter-docs/reference/en/en-US/modules/debugging.xml 25(+0 -25)

docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml 293(+0 -293)

docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml 66(+0 -66)

docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml 123(+0 -123)

docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml 27(+0 -27)

docbook/saml-adapter-docs/reference/en/en-US/modules/MigrationFromOlderVersions.xml 38(+0 -38)

docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml 73(+0 -73)

docbook/saml-adapter-docs/reference/en/en-US/modules/overview.xml 26(+0 -26)

docbook/saml-adapter-docs/reference/en/en-US/modules/servlet-filter-adapter.xml 84(+0 -84)

docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml 109(+0 -109)

examples/pom.xml 1(+1 -0)

model/jpa/pom.xml 10(+10 -0)

pom.xml 41(+39 -2)

services/pom.xml 2(+2 -0)

testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java 402(+0 -402)

testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java 272(+0 -272)

testsuite/performance/pom.xml 271(+0 -271)

testsuite/performance/README.md 44(+0 -44)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java 188(+0 -188)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java 104(+0 -104)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java 129(+0 -129)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/PerfTestUtils.java 66(+0 -66)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java 146(+0 -146)

testsuite/performance/src/test/java/org/keycloak/testsuite/performance/RemoveUsersWorker.java 88(+0 -88)

testsuite/performance/src/test/jmeter/keycloak_perf_test.jmx 39(+0 -39)

testsuite/performance/src/test/jmeter/system.properties 102(+0 -102)

testsuite/stress/pom.xml 575(+0 -575)

testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java 138(+0 -138)

testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java 65(+0 -65)

testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java 62(+0 -62)

testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java 37(+0 -37)

testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java 11(+0 -11)

testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java 9(+0 -9)

testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java 104(+0 -104)

testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java 96(+0 -96)

Details

diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml
index 015fbf4..ff3a205 100755
--- a/adapters/oidc/adapter-core/pom.xml
+++ b/adapters/oidc/adapter-core/pom.xml
@@ -66,6 +66,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-core</artifactId>
             <scope>provided</scope>
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
index 7ff049a..d7985b0 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
@@ -456,6 +456,16 @@ public class AdapterDeploymentContext {
         public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
             delegate.setTurnOffChangeSessionIdOnLogin(turnOffChangeSessionIdOnLogin);
         }
+
+        @Override
+        public int getTokenMinimumTimeToLive() {
+            return delegate.getTokenMinimumTimeToLive();
+        }
+
+        @Override
+        public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
+            delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive);
+        }
     }
 
     protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
index 18c3f4a..121adf1 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
@@ -18,10 +18,12 @@
 package org.keycloak.adapters;
 
 import org.jboss.logging.Logger;
+import org.keycloak.AuthorizationContext;
 import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.common.util.UriUtils;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.representations.AccessToken;
-import org.keycloak.common.util.UriUtils;
 
 import java.io.IOException;
 import java.util.Set;
@@ -55,6 +57,9 @@ public class AuthenticatedActionsHandler {
             queryBearerToken();
             return true;
         }
+        if (!isAuthorized()) {
+            return true;
+        }
         return false;
     }
 
@@ -124,4 +129,24 @@ public class AuthenticatedActionsHandler {
         }
         return false;
     }
+
+    private boolean isAuthorized() {
+        PolicyEnforcer policyEnforcer = this.deployment.getPolicyEnforcer();
+
+        if (policyEnforcer == null) {
+            log.debugv("Policy enforcement is disabled.");
+            return true;
+        }
+        try {
+            OIDCHttpFacade facade = (OIDCHttpFacade) this.facade;
+            AuthorizationContext authorizationContext = policyEnforcer.enforce(facade);
+            RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) facade.getSecurityContext();
+
+            session.setAuthorizationContext(authorizationContext);
+
+            return  authorizationContext.isGranted();
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to enforce policy decisions.", e);
+        }
+    }
 }
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
new file mode 100644
index 0000000..3ae286f
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -0,0 +1,256 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.adapters.authorization;
+
+import org.jboss.logging.Logger;
+import org.keycloak.AuthorizationContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.spi.HttpFacade.Request;
+import org.keycloak.adapters.spi.HttpFacade.Response;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.resource.ProtectedResource;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+import org.keycloak.representations.authorization.Permission;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPolicyEnforcer {
+
+    private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
+    private final PolicyEnforcerConfig enforcerConfig;
+    private final PolicyEnforcer policyEnforcer;
+
+    private List<PathConfig> paths;
+    private AuthzClient authzClient;
+    private PathMatcher pathMatcher;
+
+    public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+        this.policyEnforcer = policyEnforcer;
+        this.enforcerConfig = policyEnforcer.getEnforcerConfig();
+        this.authzClient = policyEnforcer.getClient();
+        this.pathMatcher = new PathMatcher();
+        this.paths = policyEnforcer.getPaths();
+    }
+
+    public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
+        EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
+
+        if (EnforcementMode.DISABLED.equals(enforcementMode)) {
+            return createEmptyAuthorizationContext(true);
+        }
+
+        AccessToken accessToken = httpFacade.getSecurityContext().getToken();
+        Request request = httpFacade.getRequest();
+        Response response = httpFacade.getResponse();
+        String pathInfo = URI.create(request.getURI()).getPath().substring(1);
+        String path = pathInfo.substring(pathInfo.indexOf('/'), pathInfo.length());
+        PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
+
+        LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+
+        if (pathConfig == null) {
+            if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
+                return createAuthorizationContext(accessToken);
+            }
+
+            LOGGER.debugf("Could not find a configuration for path [%s]", path);
+            response.sendError(403, "Could not find a configuration for path [" + path + "].");
+
+            return createEmptyAuthorizationContext(false);
+        }
+
+        PathConfig actualPathConfig = resolvePathConfig(pathConfig, request);
+        Set<String> requiredScopes = getRequiredScopes(actualPathConfig, request);
+
+        if (isAuthorized(actualPathConfig, requiredScopes, accessToken, httpFacade)) {
+            try {
+                return createAuthorizationContext(accessToken);
+            } catch (Exception e) {
+                throw new RuntimeException("Error processing path [" + actualPathConfig.getPath() + "].", e);
+            }
+        }
+
+        if (!challenge(actualPathConfig, requiredScopes, httpFacade)) {
+            LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            response.sendError(403, "Authorization failed.");
+        }
+
+        return createEmptyAuthorizationContext(false);
+    }
+
+    protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
+
+    protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+        Request request = httpFacade.getRequest();
+        PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
+        String accessDeniedPath = enforcerConfig.getAccessDeniedPath();
+
+        if (accessDeniedPath != null) {
+            if (request.getURI().contains(accessDeniedPath)) {
+                return true;
+            }
+        }
+
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        if (authorization == null) {
+            return false;
+        }
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        for (Permission permission : permissions) {
+            Set<String> allowedScopes = permission.getScopes();
+
+            if (permission.getResourceSetId() != null) {
+                if (isResourcePermission(actualPathConfig, permission)) {
+                    if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
+                        LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
+                        if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
+                            this.paths.remove(actualPathConfig);
+                        }
+                        return true;
+                    }
+                }
+            } else {
+                if ((allowedScopes.isEmpty() && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
+                    LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
+                    return true;
+                }
+            }
+        }
+
+        LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
+
+        return false;
+    }
+
+    protected AuthzClient getAuthzClient() {
+        return this.authzClient;
+    }
+
+    protected PolicyEnforcerConfig getEnforcerConfig() {
+        return enforcerConfig;
+    }
+
+    protected PolicyEnforcer getPolicyEnforcer() {
+        return policyEnforcer;
+    }
+
+    private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
+        return new AuthorizationContext() {
+            @Override
+            public boolean hasPermission(String resourceName, String scopeName) {
+                return granted;
+            }
+
+            @Override
+            public boolean hasResourcePermission(String resourceName) {
+                return granted;
+            }
+
+            @Override
+            public boolean hasScopePermission(String scopeName) {
+                return granted;
+            }
+
+            @Override
+            public List<Permission> getPermissions() {
+                return Collections.EMPTY_LIST;
+            }
+
+            @Override
+            public boolean isGranted() {
+                return granted;
+            }
+        };
+    }
+
+    private PathConfig resolvePathConfig(PathConfig originalConfig, Request request) {
+        if (originalConfig.hasPattern()) {
+            String pathInfo = URI.create(request.getURI()).getPath().substring(1);
+            String path = pathInfo.substring(pathInfo.indexOf('/'), pathInfo.length());
+            ProtectedResource resource = this.authzClient.protection().resource();
+            Set<String> search = resource.findByFilter("uri=" + path);
+
+            if (!search.isEmpty()) {
+                // resource does exist on the server, cache it
+                ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
+                PathConfig config = new PathConfig();
+
+                config.setId(targetResource.getId());
+                config.setName(targetResource.getName());
+                config.setType(targetResource.getType());
+                config.setPath(targetResource.getUri());
+                config.setScopes(originalConfig.getScopes());
+                config.setMethods(originalConfig.getMethods());
+                config.setInstance(true);
+                config.setParentConfig(originalConfig);
+
+                this.paths.add(config);
+
+                return config;
+            }
+        }
+
+        return originalConfig;
+    }
+
+    private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
+        Set<String> requiredScopes = new HashSet<>();
+
+        requiredScopes.addAll(pathConfig.getScopes());
+
+        String method = request.getMethod();
+
+        for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
+            if (methodConfig.getMethod().equals(method)) {
+                requiredScopes.addAll(methodConfig.getScopes());
+            }
+        }
+
+        return requiredScopes;
+    }
+
+    private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
+        return new AuthorizationContext(accessToken, this.paths);
+    }
+
+    private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
+        // first we try a match using resource id
+        boolean resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getId());
+
+        // as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
+        if (!resourceMatch && actualPathConfig.isInstance()) {
+            resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getParentConfig().getId());
+        }
+
+        return resourceMatch;
+    }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
new file mode 100644
index 0000000..efa2b2b
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.adapters.authorization;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.PermissionRequest;
+import org.keycloak.authorization.client.resource.PermissionResource;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
+
+    private static Logger LOGGER = Logger.getLogger(BearerTokenPolicyEnforcer.class);
+
+    public BearerTokenPolicyEnforcer(PolicyEnforcer enforcer) {
+        super(enforcer);
+    }
+
+    @Override
+    protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+        if (getEnforcerConfig().getUmaProtocolConfig() != null) {
+            challengeUmaAuthentication(pathConfig, requiredScopes, facade);
+        } else {
+            challengeEntitlementAuthentication(facade);
+        }
+        return true;
+    }
+
+    private void challengeEntitlementAuthentication(OIDCHttpFacade facade) {
+        HttpFacade.Response response = facade.getResponse();
+        AuthzClient authzClient = getAuthzClient();
+        String clientId = authzClient.getConfiguration().getClientId();
+        String  authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
+        response.setStatus(401);
+        response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
+    }
+
+    private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+        HttpFacade.Response response = facade.getResponse();
+        AuthzClient authzClient = getAuthzClient();
+        String ticket = getPermissionTicket(pathConfig, requiredScopes, authzClient);
+        String clientId = authzClient.getConfiguration().getClientId();
+        String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
+        response.setStatus(401);
+        response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
+    }
+
+    private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
+        ProtectionResource protection = authzClient.protection();
+        PermissionResource permission = protection.permission();
+        PermissionRequest permissionRequest = new PermissionRequest();
+        permissionRequest.setResourceSetId(pathConfig.getId());
+        permissionRequest.setScopes(requiredScopes);
+        return permission.forResource(permissionRequest).getTicket();
+    }
+}
\ No newline at end of file
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
new file mode 100644
index 0000000..e151f76
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -0,0 +1,119 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.adapters.authorization;
+
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.AuthorizationRequest;
+import org.keycloak.authorization.client.representation.AuthorizationResponse;
+import org.keycloak.authorization.client.representation.EntitlementResponse;
+import org.keycloak.authorization.client.representation.PermissionRequest;
+import org.keycloak.authorization.client.representation.PermissionResponse;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
+
+    private static Logger LOGGER = Logger.getLogger(KeycloakAdapterPolicyEnforcer.class);
+
+    public KeycloakAdapterPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+        super(policyEnforcer);
+    }
+
+    @Override
+    protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+        int retry = 2;
+        AccessToken original = accessToken;
+
+        while (retry >= 0) {
+            if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
+                original.setAuthorization(accessToken.getAuthorization());
+                return true;
+            }
+
+            accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
+
+            if (accessToken == null) {
+                return false;
+            }
+
+            retry--;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+        String accessDeniedPath = getEnforcerConfig().getAccessDeniedPath();
+        HttpFacade.Response response = facade.getResponse();
+
+        if (accessDeniedPath != null) {
+            response.setStatus(302);
+            response.setHeader("Location", accessDeniedPath);
+        } else {
+            response.sendError(403);
+        }
+
+        return true;
+    }
+
+    private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
+        try {
+            String accessToken = httpFacade.getSecurityContext().getTokenString();
+            AuthzClient authzClient = getAuthzClient();
+            KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
+
+            if (getEnforcerConfig().getUmaProtocolConfig() != null) {
+                LOGGER.debug("Obtaining authorization for  authenticated user.");
+                PermissionRequest permissionRequest = new PermissionRequest();
+
+                permissionRequest.setResourceSetId(pathConfig.getId());
+                permissionRequest.setScopes(requiredScopes);
+
+                PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
+                AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
+                AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
+
+                if (authzResponse != null) {
+                    return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
+                }
+
+                return null;
+            } else {
+                LOGGER.debug("Obtaining entitlements for authenticated user.");
+                EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId());
+                return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
+            }
+        } catch (AuthorizationDeniedException e) {
+            return null;
+        } catch (Exception e) {
+            throw new RuntimeException("Unexpected error during authorization request.", e);
+        }
+    }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
new file mode 100644
index 0000000..5ac1b79
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
@@ -0,0 +1,103 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.adapters.authorization;
+
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class PathMatcher {
+
+    private static final String ANY_RESOURCE_PATTERN = "/*";
+
+    PathConfig matches(final String requestedUri, List<PathConfig> paths) {
+        PathConfig actualConfig = null;
+
+        for (PathConfig entry : paths) {
+            String protectedUri = entry.getPath();
+            String selectedUri = null;
+
+            if (protectedUri.equals(ANY_RESOURCE_PATTERN) && actualConfig == null) {
+                selectedUri = protectedUri;
+            }
+
+            int suffixIndex = protectedUri.indexOf(ANY_RESOURCE_PATTERN + ".");
+
+            if (suffixIndex != -1) {
+                String protectedSuffix = protectedUri.substring(suffixIndex + ANY_RESOURCE_PATTERN.length());
+
+                if (requestedUri.endsWith(protectedSuffix)) {
+                    selectedUri = protectedUri;
+                }
+            }
+
+            if (protectedUri.equals(requestedUri)) {
+                selectedUri = protectedUri;
+            }
+
+            if (protectedUri.endsWith(ANY_RESOURCE_PATTERN)) {
+                String formattedPattern = removeWildCardsFromUri(protectedUri);
+
+                if (!formattedPattern.equals("/") && requestedUri.startsWith(formattedPattern)) {
+                    selectedUri = protectedUri;
+                }
+
+                if (!formattedPattern.equals("/") && formattedPattern.endsWith("/") && formattedPattern.substring(0, formattedPattern.length() - 1).equals(requestedUri)) {
+                    selectedUri = protectedUri;
+                }
+            }
+
+            int startRegex = protectedUri.indexOf('{');
+
+            if (startRegex != -1) {
+                String prefix = protectedUri.substring(0, startRegex);
+
+                if (requestedUri.startsWith(prefix)) {
+                    selectedUri = protectedUri;
+                }
+            }
+
+            if (selectedUri != null) {
+                selectedUri = protectedUri;
+            }
+
+            if (selectedUri != null) {
+                if (actualConfig == null) {
+                    actualConfig = entry;
+                } else {
+                    if (actualConfig.equals(ANY_RESOURCE_PATTERN)) {
+                        actualConfig = entry;
+                    }
+
+                    if (protectedUri.startsWith(removeWildCardsFromUri(actualConfig.getPath()))) {
+                        actualConfig = entry;
+                    }
+                }
+            }
+        }
+
+        return actualConfig;
+    }
+
+    private String removeWildCardsFromUri(String protectedUri) {
+        return protectedUri.replaceAll("/[*]", "/");
+    }
+}
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
new file mode 100644
index 0000000..d413327
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -0,0 +1,205 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.adapters.authorization;
+
+import org.jboss.logging.Logger;
+import org.keycloak.AuthorizationContext;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.RegistrationResponse;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.authorization.client.resource.ProtectedResource;
+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.authorization.Permission;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEnforcer {
+
+    private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class);
+
+    private final KeycloakDeployment deployment;
+    private final PathMatcher pathMatcher;
+    private final AuthzClient authzClient;
+    private final PolicyEnforcerConfig enforcerConfig;
+    private final List<PathConfig> paths;
+
+    public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
+        this.deployment = deployment;
+        this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
+        this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()));
+        this.pathMatcher = new PathMatcher();
+        this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
+
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Initialization complete. Path configurations:");
+            for (PathConfig pathConfig : this.paths) {
+                LOGGER.debug(pathConfig);
+            }
+        }
+    }
+
+    public AuthorizationContext enforce(OIDCHttpFacade facade) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debugv("Policy enforcement is enable. Enforcing policy decisions for path [{0}].", facade.getRequest().getURI());
+        }
+
+        AuthorizationContext context;
+
+        if (deployment.isBearerOnly()) {
+            context = new BearerTokenPolicyEnforcer(this).authorize(facade);
+        } else {
+            context = new KeycloakAdapterPolicyEnforcer(this).authorize(facade);
+        }
+
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debugv("Policy enforcement result for path [{0}] is : {1}", facade.getRequest().getURI(), context.isGranted() ? "GRANTED" : "DENIED");
+            LOGGER.debugv("Returning authorization context with permissions:");
+            for (Permission permission : context.getPermissions()) {
+                LOGGER.debug(permission);
+            }
+        }
+
+        return context;
+    }
+
+    PolicyEnforcerConfig getEnforcerConfig() {
+        return enforcerConfig;
+    }
+
+    AuthzClient getClient() {
+        return authzClient;
+    }
+
+    List<PathConfig> getPaths() {
+        return paths;
+    }
+
+    KeycloakDeployment getDeployment() {
+        return deployment;
+    }
+
+    private List<PathConfig> configurePaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
+        if (enforcerConfig.getPaths().isEmpty()) {
+            LOGGER.info("No path provided in configuration.");
+            return configureAllPathsForResourceServer(protectedResource);
+        } else {
+            LOGGER.info("Paths provided in configuration.");
+            return configureDefinedPaths(protectedResource, enforcerConfig);
+        }
+    }
+
+    private List<PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
+        List<PathConfig> paths = new ArrayList<>();
+
+        for (PathConfig pathConfig : enforcerConfig.getPaths()) {
+            Set<String> search;
+            String resourceName = pathConfig.getName();
+            String path = pathConfig.getPath();
+
+            if (resourceName != null) {
+                LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path);
+                search = protectedResource.findByFilter("name=" + resourceName);
+            } else {
+                LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
+                search = protectedResource.findByFilter("uri=" + path);
+            }
+
+            if (search.isEmpty()) {
+                if (enforcerConfig.isCreateResources()) {
+                    LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
+                    ResourceRepresentation resource = new ResourceRepresentation();
+
+                    resource.setName(resourceName);
+                    resource.setType(pathConfig.getType());
+                    resource.setUri(path);
+
+                    HashSet<ScopeRepresentation> scopes = new HashSet<>();
+
+                    for (String scopeName : pathConfig.getScopes()) {
+                        ScopeRepresentation scope = new ScopeRepresentation();
+
+                        scope.setName(scopeName);
+
+                        scopes.add(scope);
+                    }
+
+                    resource.setScopes(scopes);
+
+                    RegistrationResponse registrationResponse = protectedResource.create(resource);
+
+                    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(search.iterator().next());
+            }
+
+            paths.add(pathConfig);
+        }
+
+        return paths;
+    }
+
+    private List<PathConfig> configureAllPathsForResourceServer(ProtectedResource protectedResource) {
+        LOGGER.info("Querying the server for all resources associated with this application.");
+        List<PathConfig> paths = new ArrayList<>();
+
+        for (String id : protectedResource.findAll()) {
+            RegistrationResponse response = protectedResource.findById(id);
+            ResourceRepresentation resourceDescription = response.getResourceDescription();
+
+            if (resourceDescription.getUri() != null) {
+                paths.add(createPathConfig(resourceDescription));
+            }
+        }
+
+        return paths;
+    }
+
+    private PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
+        PathConfig pathConfig = new PathConfig();
+
+        pathConfig.setId(resourceDescription.getId());
+        pathConfig.setName(resourceDescription.getName());
+        pathConfig.setPath(resourceDescription.getUri());
+
+        List<String> scopeNames = new ArrayList<>();
+
+        for (ScopeRepresentation scope : resourceDescription.getScopes()) {
+            scopeNames.add(scope.getName());
+        }
+
+        pathConfig.setScopes(scopeNames);
+        pathConfig.setType(resourceDescription.getType());
+
+        return pathConfig;
+    }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index cca0adf..901b3ea 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -20,6 +20,7 @@ package org.keycloak.adapters;
 import org.apache.http.client.HttpClient;
 import org.jboss.logging.Logger;
 import org.keycloak.adapters.authentication.ClientCredentialsProvider;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
 import org.keycloak.constants.ServiceUrlConstants;
 import org.keycloak.common.enums.RelativeUrlsUsed;
 import org.keycloak.common.enums.SslRequired;
@@ -77,6 +78,8 @@ public class KeycloakDeployment {
     protected boolean turnOffChangeSessionIdOnLogin;
 
     protected volatile int notBefore;
+    protected int tokenMinimumTimeToLive;
+    private PolicyEnforcer policyEnforcer;
 
     public KeycloakDeployment() {
     }
@@ -357,4 +360,20 @@ public class KeycloakDeployment {
     public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
         this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
     }
+
+    public int getTokenMinimumTimeToLive() {
+        return tokenMinimumTimeToLive;
+    }
+
+    public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
+        this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
+    }
+
+    public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+        this.policyEnforcer = policyEnforcer;
+    }
+
+    public PolicyEnforcer getPolicyEnforcer() {
+        return policyEnforcer;
+    }
 }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index ab77491..5d54df9 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -21,10 +21,12 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.jboss.logging.Logger;
 import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
 import org.keycloak.common.enums.SslRequired;
+import org.keycloak.common.util.PemUtils;
 import org.keycloak.enums.TokenStore;
 import org.keycloak.representations.adapters.config.AdapterConfig;
-import org.keycloak.common.util.PemUtils;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.util.SystemPropertiesJsonParserFactory;
 
 import java.io.IOException;
@@ -94,6 +96,7 @@ public class KeycloakDeploymentBuilder {
         deployment.setAlwaysRefreshToken(adapterConfig.isAlwaysRefreshToken());
         deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
         deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
+        deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
 
         if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
             throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
@@ -109,6 +112,12 @@ public class KeycloakDeploymentBuilder {
             deployment.setTurnOffChangeSessionIdOnLogin(adapterConfig.getTurnOffChangeSessionIdOnLogin());
         }
 
+        PolicyEnforcerConfig policyEnforcerConfig = adapterConfig.getPolicyEnforcerConfig();
+
+        if (policyEnforcerConfig != null) {
+            deployment.setPolicyEnforcer(new PolicyEnforcer(deployment, adapterConfig));
+        }
+
         log.debug("Use authServerUrl: " + deployment.getAuthServerBaseUrl() + ", tokenUrl: " + deployment.getTokenUrl() + ", relativeUrls: " + deployment.getRelativeUrls());
         return deployment;
     }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
index bdb5776..7cfc7a6 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
@@ -18,9 +18,11 @@
 package org.keycloak.adapters;
 
 import org.jboss.logging.Logger;
+import org.keycloak.AuthorizationContext;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.common.VerificationException;
+import org.keycloak.common.util.Time;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
@@ -74,7 +76,11 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext 
     }
 
     public boolean isActive() {
-        return token != null && this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
+        return token != null && this.token.isActive() && deployment!=null && this.token.getIssuedAt() > deployment.getNotBefore();
+    }
+
+    public boolean isTokenTimeToLiveSufficient(AccessToken token) {
+        return token != null && (token.getExpiration() - this.deployment.getTokenMinimumTimeToLive()) > Time.currentTime();
     }
 
     public KeycloakDeployment getDeployment() {
@@ -95,7 +101,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext 
             if (log.isTraceEnabled()) {
                 log.trace("checking whether to refresh.");
             }
-            if (isActive()) return true;
+            if (isActive() && isTokenTimeToLiveSufficient(this.token)) return true;
         }
 
         if (this.deployment == null || refreshToken == null) return false; // Might be serialized in HttpSession?
@@ -130,6 +136,13 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext 
             log.error("failed verification of token");
             return false;
         }
+
+        // If the TTL is greater-or-equal to the expire time on the refreshed token, have to abort or go into an infinite refresh loop
+        if (!isTokenTimeToLiveSufficient(token)) {
+            log.error("failed to refresh the token with a longer time-to-live than the minimum");
+            return false;
+        }
+
         if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
             deployment.setNotBefore(response.getNotBeforePolicy());
         }
@@ -145,4 +158,8 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext 
         tokenStore.refreshCallback(this);
         return true;
     }
+
+    public void setAuthorizationContext(AuthorizationContext authorizationContext) {
+        this.authorizationContext = authorizationContext;
+    }
 }
diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
index 93d1ea7..4be5110 100644
--- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
+++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
@@ -61,6 +61,7 @@ public class KeycloakDeploymentBuilderTest {
         assertEquals(1000, deployment.getRegisterNodePeriod());
         assertEquals(TokenStore.COOKIE, deployment.getTokenStore());
         assertEquals("email", deployment.getPrincipalAttribute());
+        assertEquals(10, deployment.getTokenMinimumTimeToLive());
     }
 
     @Test
diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/RefreshableKeycloakSecurityContextTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/RefreshableKeycloakSecurityContextTest.java
new file mode 100644
index 0000000..2310b21
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/RefreshableKeycloakSecurityContextTest.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.keycloak.representations.oidc.TokenMetadataRepresentation;
+/**
+ * @author github.com/tubbynl
+ *
+ */
+public class RefreshableKeycloakSecurityContextTest {
+
+	@Test
+	public void isActive() {
+		TokenMetadataRepresentation token = new TokenMetadataRepresentation();
+		token.setActive(true);
+		token.issuedNow();
+		RefreshableKeycloakSecurityContext sut = new RefreshableKeycloakSecurityContext(null,null,null,token,null, null, null);
+		
+		// verify false if null deployment (KEYCLOAK-3050; yielded a npe)
+		assertFalse(sut.isActive());
+	}
+}
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
index 5a41841..7bf269f 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
@@ -28,5 +28,6 @@
     "register-node-at-startup": true,
     "register-node-period": 1000,
     "token-store": "cookie",
-    "principal-attribute": "email"
+    "principal-attribute": "email",
+    "token-minimum-time-to-live": 10
 }
\ No newline at end of file
diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml
index 315f4b7..b03ccea 100755
--- a/adapters/oidc/js/pom.xml
+++ b/adapters/oidc/js/pom.xml
@@ -58,6 +58,25 @@
                             <goal>minify</goal>
                         </goals>
                     </execution>
+                    <execution>
+                        <id>min-authz-js</id>
+                        <phase>compile</phase>
+                        <configuration>
+                            <charset>utf-8</charset>
+                            <webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
+                            <jsSourceDir>.</jsSourceDir>
+                            <jsSourceFiles>
+                                <jsSourceFile>keycloak-authz.js</jsSourceFile>
+                            </jsSourceFiles>
+
+                            <webappTargetDir>${project.build.directory}/classes</webappTargetDir>
+                            <jsTargetDir>.</jsTargetDir>
+                            <jsFinalFile>keycloak-authz.js</jsFinalFile>
+                        </configuration>
+                        <goals>
+                            <goal>minify</goal>
+                        </goals>
+                    </execution>
                 </executions>
             </plugin>
 		</plugins>
diff --git a/adapters/oidc/js/src/main/resources/keycloak-authz.js b/adapters/oidc/js/src/main/resources/keycloak-authz.js
new file mode 100644
index 0000000..7658352
--- /dev/null
+++ b/adapters/oidc/js/src/main/resources/keycloak-authz.js
@@ -0,0 +1,170 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+(function( window, undefined ) {
+    
+    var KeycloakAuthorization = function (keycloak) {
+        var _instance = this;
+        this.rpt = null;
+
+        this.init = function () {
+            var request = new XMLHttpRequest();
+
+            request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
+            request.onreadystatechange = function () {
+                if (request.readyState == 4) {
+                    if (request.status == 200) {
+                        _instance.config = JSON.parse(request.responseText);
+                    } else {
+                        console.error('Could not obtain configuration from server.');
+                    }
+                }
+            }
+
+            request.send(null);
+        };
+
+        /**
+         * This method enables client applications to better integrate with resource servers protected by a Keycloak
+         * policy enforcer.
+         *
+         * In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
+         * necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
+         * depending on how the policy enforcer at the resource server was configured.
+         */
+        this.authorize = function (wwwAuthenticateHeader) {
+            this.then = function (onGrant, onDeny, onError) {
+                if (wwwAuthenticateHeader.startsWith('UMA')) {
+                    var params = wwwAuthenticateHeader.split(',');
+
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'ticket') {
+                            var request = new XMLHttpRequest();
+
+                            request.open('POST', _instance.config.rpt_endpoint, true);
+                            request.setRequestHeader('Content-Type', 'application/json')
+                            request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
+
+                            request.onreadystatechange = function () {
+                                if (request.readyState == 4) {
+                                    var status = request.status;
+
+                                    if (status >= 200 && status < 300) {
+                                        var rpt = JSON.parse(request.responseText).rpt;
+                                        _instance.rpt = rpt;
+                                        onGrant(rpt);
+                                    } else if (status == 403) {
+                                        if (onDeny) {
+                                            onDeny();
+                                        } else {
+                                            console.error('Authorization request was denied by the server.');
+                                        }
+                                    } else {
+                                        if (onError) {
+                                            onError();
+                                        } else {
+                                            console.error('Could not obtain authorization data from server.');
+                                        }
+                                    }
+                                }
+                            };
+
+                            var ticket = param[1].substring(1, param[1].length - 1).trim();
+
+                            request.send(JSON.stringify(
+                                {
+                                    ticket: ticket,
+                                    rpt: _instance.rpt
+                                }
+                            ));
+                        }
+                    }
+                } else if (wwwAuthenticateHeader.startsWith('KC_ETT')) {
+                    var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
+                    var clientId = null;
+
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'realm') {
+                            clientId = param[1].substring(1, param[1].length - 1).trim();
+                        }
+                    }
+
+                    _instance.entitlement(clientId).then(onGrant, onDeny, onError);
+                }
+            };
+
+            /**
+             * Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
+             */
+            this.entitlement = function (resourceSeververId) {
+                this.then = function (onGrant, onDeny, onError) {
+                    var request = new XMLHttpRequest();
+
+                    request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
+                    request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
+
+                    request.onreadystatechange = function () {
+                        if (request.readyState == 4) {
+                            var status = request.status;
+
+                            if (status >= 200 && status < 300) {
+                                var rpt = JSON.parse(request.responseText).rpt;
+                                _instance.rpt = rpt;
+                                onGrant(rpt);
+                            } else if (status == 403) {
+                                if (onDeny) {
+                                    onDeny();
+                                } else {
+                                    console.error('Authorization request was denied by the server.');
+                                }
+                            } else {
+                                if (onError) {
+                                    onError();
+                                } else {
+                                    console.error('Could not obtain authorization data from server.');
+                                }
+                            }
+                        }
+                    };
+
+                    request.send(null);
+                };
+
+                return this;
+            };
+
+            return this;
+        };
+
+        this.init(this);
+    };
+
+    if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+        module.exports = KeycloakAuthorization;
+    } else {
+        window.KeycloakAuthorization = KeycloakAuthorization;
+
+        if ( typeof define === "function" && define.amd ) {
+            define( "keycloak-authorization", [], function () { return KeycloakAuthorization; } );
+        }
+    }
+})( window );
\ No newline at end of file
diff --git a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCServletUndertowHttpFacade.java b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCServletUndertowHttpFacade.java
index 3dc705f..27ddae8 100755
--- a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCServletUndertowHttpFacade.java
+++ b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCServletUndertowHttpFacade.java
@@ -17,16 +17,16 @@
 package org.keycloak.adapters.undertow;
 
 import io.undertow.server.HttpServerExchange;
-import io.undertow.util.AttachmentKey;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.OIDCHttpFacade;
 
+import static org.keycloak.adapters.undertow.OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class OIDCServletUndertowHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
-    public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
 
     public OIDCServletUndertowHttpFacade(HttpServerExchange exchange) {
         super(exchange);

authz/client/pom.xml 101(+101 -0)

diff --git a/authz/client/pom.xml b/authz/client/pom.xml
new file mode 100644
index 0000000..0479ddf
--- /dev/null
+++ b/authz/client/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-client</artifactId>
+    <packaging>jar</packaging>
+
+    <name>KeyCloak Authz: Client API</name>
+    <description>KeyCloak AuthZ: Client API</description>
+
+    <properties>
+        <keycloak.osgi.export>
+            org.keycloak.authorization.client.*
+        </keycloak.osgi.export>
+        <keycloak.osgi.import>
+            org.keycloak.*;version="${project.version}",
+            org.apache.http.*;version=${apache.httpcomponents.version},
+            *;resolution:=optional
+        </keycloak.osgi.import>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>${jboss.logging.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <instructions>
+                        <Bundle-ClassPath>.</Bundle-ClassPath>
+                        <Bundle-Name>${project.name}</Bundle-Name>
+                        <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>${keycloak.osgi.import}</Import-Package>
+                        <Export-Package>${keycloak.osgi.export}</Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java
new file mode 100644
index 0000000..ffab2b5
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthorizationDeniedException.java
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client;
+
+import org.keycloak.authorization.client.util.HttpResponseException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationDeniedException extends RuntimeException {
+    public AuthorizationDeniedException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
new file mode 100644
index 0000000..0221af6
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client;
+
+import org.keycloak.authorization.client.representation.ServerConfiguration;
+import org.keycloak.authorization.client.resource.AuthorizationResource;
+import org.keycloak.authorization.client.resource.EntitlementResource;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+/**
+ * <p>This is class serves as an entry point for clients looking for access to Keycloak Authorization Services.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthzClient {
+
+    private final Http http;
+
+    public static AuthzClient create() {
+        InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("keycloak.json");
+
+        if (configStream == null) {
+            throw new RuntimeException("Could not find any keycloak.json file in classpath.");
+        }
+
+        try {
+            return create(JsonSerialization.readValue(configStream, Configuration.class));
+        } catch (IOException e) {
+            throw new RuntimeException("Could not parse configuration.", e);
+        }
+    }
+
+    public static AuthzClient create(Configuration configuration) {
+        return new AuthzClient(configuration);
+    }
+
+    private final ServerConfiguration serverConfiguration;
+    private final Configuration deployment;
+
+    private AuthzClient(Configuration configuration) {
+        if (configuration == null) {
+            throw new IllegalArgumentException("Client configuration can not be null.");
+        }
+
+        String configurationUrl = configuration.getAuthServerUrl();
+
+        if (configurationUrl == null) {
+            throw new IllegalArgumentException("Configuration URL can not be null.");
+        }
+
+        configurationUrl += "/realms/" + configuration.getRealm() + "/.well-known/uma-configuration";
+
+        this.http = new Http(configuration);
+
+        try {
+            this.serverConfiguration = this.http.<ServerConfiguration>get(URI.create(configurationUrl))
+                    .response().json(ServerConfiguration.class)
+                    .execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not obtain configuration from server [" + configurationUrl + "].", e);
+        }
+
+        this.http.setServerConfiguration(this.serverConfiguration);
+
+        this.deployment = configuration;
+    }
+
+    public ProtectionResource protection() {
+        return new ProtectionResource(this.http, obtainAccessToken().getToken());
+    }
+
+    public AuthorizationResource authorization(String accesstoken) {
+        return new AuthorizationResource(this.http, accesstoken);
+    }
+
+    public AuthorizationResource authorization(String userName, String password) {
+        return new AuthorizationResource(this.http, obtainAccessToken(userName, password).getToken());
+    }
+
+    public EntitlementResource entitlement(String eat) {
+        return new EntitlementResource(this.http, eat);
+    }
+
+    public AccessTokenResponse obtainAccessToken() {
+        return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
+                .authentication()
+                    .oauth2ClientCredentials()
+                .response()
+                    .json(AccessTokenResponse.class)
+                .execute();
+    }
+
+    public AccessTokenResponse obtainAccessToken(String userName, String password) {
+        return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
+                .authentication()
+                    .oauth2ResourceOwnerPassword(userName, password)
+                .response()
+                    .json(AccessTokenResponse.class)
+                .execute();
+    }
+
+    public ServerConfiguration getServerConfiguration() {
+        return this.serverConfiguration;
+    }
+
+    public Configuration getConfiguration() {
+        return this.deployment;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java
new file mode 100644
index 0000000..076c2db
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client;
+
+import java.util.HashMap;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ClientAuthenticator {
+    void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders);
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java b/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java
new file mode 100644
index 0000000..835c830
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.keycloak.util.BasicAuthHelper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Configuration {
+
+    @JsonIgnore
+    private HttpClient httpClient;
+
+    @JsonProperty("auth-server-url")
+    protected String authServerUrl;
+
+    @JsonProperty("realm")
+    protected String realm;
+
+    @JsonProperty("resource")
+    protected String clientId;
+
+    @JsonProperty("credentials")
+    protected Map<String, Object> clientCredentials = new HashMap<>();
+
+    public Configuration() {
+
+    }
+
+    public Configuration(String authServerUrl, String realm, String clientId, Map<String, Object> clientCredentials, HttpClient httpClient) {
+        this.authServerUrl = authServerUrl;
+        this.realm = realm;
+        this.clientId = clientId;
+        this.clientCredentials = clientCredentials;
+        this.httpClient = httpClient;
+    }
+
+    @JsonIgnore
+    private ClientAuthenticator clientAuthenticator = new ClientAuthenticator() {
+        @Override
+        public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
+            String secret = (String) clientCredentials.get("secret");
+
+            if (secret == null) {
+                throw new RuntimeException("Client secret not provided.");
+            }
+
+            requestHeaders.put("Authorization", BasicAuthHelper.createHeader(clientId, secret));
+        }
+    };
+
+    public HttpClient getHttpClient() {
+        if (this.httpClient == null) {
+            this.httpClient = HttpClients.createDefault();
+        }
+
+        return httpClient;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public String getAuthServerUrl() {
+        return authServerUrl;
+    }
+
+    public ClientAuthenticator getClientAuthenticator() {
+        return this.clientAuthenticator;
+    }
+
+    public Map<String, Object> getClientCredentials() {
+        return clientCredentials;
+    }
+
+    public String getRealm() {
+        return realm;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationRequest.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationRequest.java
new file mode 100644
index 0000000..ef53586
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationRequest.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationRequest {
+
+    private String ticket;
+    private String rpt;
+
+    public AuthorizationRequest(String ticket, String rpt) {
+        this.ticket = ticket;
+        this.rpt = rpt;
+    }
+
+    public AuthorizationRequest(String ticket) {
+        this(ticket, null);
+    }
+
+    public AuthorizationRequest() {
+        this(null, null);
+    }
+
+    public String getTicket() {
+        return this.ticket;
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationResponse.java
new file mode 100644
index 0000000..472c89a
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationResponse.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationResponse {
+
+    private String rpt;
+
+    public AuthorizationResponse(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public AuthorizationResponse() {
+        this(null);
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+
+    public void setRpt(final String rpt) {
+        this.rpt = rpt;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementRequest.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementRequest.java
new file mode 100644
index 0000000..daec233
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementRequest.java
@@ -0,0 +1,34 @@
+package org.keycloak.authorization.client.representation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementRequest {
+
+    private String rpt;
+
+    private List<PermissionRequest> permissions = new ArrayList<>();
+
+    public List<PermissionRequest> getPermissions() {
+        return permissions;
+    }
+
+    public String getRpt() {
+        return rpt;
+    }
+
+    public void setRpt(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public void setPermissions(List<PermissionRequest> permissions) {
+        this.permissions = permissions;
+    }
+
+    public void addPermission(PermissionRequest request) {
+        getPermissions().add(request);
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementResponse.java
new file mode 100644
index 0000000..747c144
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementResponse.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementResponse {
+
+    private String rpt;
+
+    public EntitlementResponse(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public EntitlementResponse() {
+        this(null);
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+
+    public void setRpt(final String rpt) {
+        this.rpt = rpt;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ErrorResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ErrorResponse.java
new file mode 100644
index 0000000..fd3dc63
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ErrorResponse.java
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ErrorResponse {
+
+    private String error;
+
+    @JsonProperty("error_description")
+    private String description;
+
+    @JsonProperty("error_uri")
+    private String uri;
+
+    public ErrorResponse(final String error, final String description, final String uri) {
+        this.error = error;
+        this.description = description;
+        this.uri = uri;
+    }
+
+    public ErrorResponse(final String error) {
+        this(error, null, null);
+    }
+
+    public ErrorResponse() {
+        this(null, null, null);
+    }
+
+    public String getError() {
+        return this.error;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
+    public String getUri() {
+        return this.uri;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionRequest.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionRequest.java
new file mode 100644
index 0000000..bc9037e
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionRequest.java
@@ -0,0 +1,62 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionRequest {
+
+    @JsonProperty("resource_set_id")
+    private String resourceSetId;
+
+    @JsonProperty("resource_set_name")
+    private String resourceSetName;
+
+    private Set<String> scopes;
+
+    public String getResourceSetId() {
+        return this.resourceSetId;
+    }
+
+    public void setResourceSetId(String resourceSetId) {
+        this.resourceSetId = resourceSetId;
+    }
+
+    public Set<String> getScopes() {
+        return this.scopes;
+    }
+
+    public void setScopes(Set<String> scopes) {
+        this.scopes = scopes;
+    }
+
+    public String getResourceSetName() {
+        return this.resourceSetName;
+    }
+
+    public void setResourceSetName(String resourceSetName) {
+        this.resourceSetName = resourceSetName;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionResponse.java
new file mode 100644
index 0000000..1002bb5
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionResponse.java
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionResponse {
+
+    private final String ticket;
+
+    public PermissionResponse(String ticket) {
+        this.ticket = ticket;
+    }
+
+    public PermissionResponse() {
+        this(null);
+    }
+
+    public String getTicket() {
+        return this.ticket;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/RegistrationResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/RegistrationResponse.java
new file mode 100644
index 0000000..0f279bc
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/RegistrationResponse.java
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RegistrationResponse {
+
+    private final ResourceRepresentation resourceDescription;
+
+    public RegistrationResponse(ResourceRepresentation resourceDescription) {
+        this.resourceDescription = resourceDescription;
+    }
+
+    public RegistrationResponse() {
+        this(null);
+    }
+
+    @JsonUnwrapped
+    public ResourceRepresentation getResourceDescription() {
+        return this.resourceDescription;
+    }
+
+    public String getId() {
+        if (this.resourceDescription != null) {
+            return this.resourceDescription.getId();
+        }
+
+        return null;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java
new file mode 100644
index 0000000..b0b6948
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java
@@ -0,0 +1,184 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * <p>One or more resources that the resource server manages as a set of protected resources.
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.2">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceRepresentation {
+
+    @JsonProperty("_id")
+    private String id;
+
+    private String name;
+    private String uri;
+    private String type;
+    private Set<ScopeRepresentation> scopes;
+
+    @JsonProperty("icon_uri")
+    private String iconUri;
+    private String owner;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     * @param iconUri a {@link URI} for a graphic icon representing the resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type, String iconUri) {
+        this.name = name;
+        this.scopes = scopes;
+        this.uri = uri;
+        this.type = type;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type) {
+        this(name, scopes, uri, type, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param serverUri a {@link URI} that identifies this resource server
+     * @param scopes the available scopes for this resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes) {
+        this(name, scopes, null, null, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     */
+    public ResourceRepresentation() {
+        this(null, null, null, null, null);
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getUri() {
+        return this.uri;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public Set<ScopeRepresentation> getScopes() {
+        return Collections.unmodifiableSet(this.scopes);
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setScopes(Set<ScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public void addScope(ScopeRepresentation scopeRepresentation) {
+        if (this.scopes == null) {
+            this.scopes = new HashSet<>();
+        }
+        this.scopes.add(scopeRepresentation);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ResourceRepresentation that = (ResourceRepresentation) o;
+        return Objects.equals(id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+
+    @Override
+    public String toString() {
+        return "ResourceRepresentation{" +
+                "id='" + id + '\'' +
+                ", name='" + name + '\'' +
+                ", uri='" + uri + '\'' +
+                ", type='" + type + '\'' +
+                ", owner='" + owner + '\'' +
+                ", scopes=" + scopes +
+                '}';
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ScopeRepresentation.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ScopeRepresentation.java
new file mode 100644
index 0000000..4fcfc35
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ScopeRepresentation.java
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import java.net.URI;
+import java.util.Objects;
+
+/**
+ * <p>A bounded extent of access that is possible to perform on a resource set. In authorization policy terminology,
+ * a scope is one of the potentially many "verbs" that can logically apply to a resource set ("object").
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.1">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeRepresentation {
+
+    private String id;
+    private String name;
+    private String iconUri;
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     * @param iconUri a {@link URI} for a graphic icon representing the scope
+     */
+    public ScopeRepresentation(String name, String iconUri) {
+        this.name = name;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     */
+    public ScopeRepresentation(String name) {
+        this(name, null);
+    }
+
+    /**
+     * Creates an instance.
+     */
+    public ScopeRepresentation() {
+        this(null, null);
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ScopeRepresentation scope = (ScopeRepresentation) o;
+        return Objects.equals(getName(), scope.getName());
+    }
+
+    public int hashCode() {
+        return Objects.hash(getName());
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+}
\ No newline at end of file
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
new file mode 100644
index 0000000..6716165
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
@@ -0,0 +1,235 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.net.URI;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ServerConfiguration {
+
+    private String version;
+    private URI issuer;
+
+    @JsonProperty("pat_profiles_supported")
+    private Set<String> patProfiles;
+
+    @JsonProperty("pat_grant_types_supported")
+    private Set<String> patGrantTypes;
+
+    @JsonProperty("aat_profiles_supported")
+    private Set<String> aatProfiles;
+
+    @JsonProperty("aat_grant_types_supported")
+    private Set<String> aatGrantTypes;
+
+    @JsonProperty("rpt_profiles_supported")
+    private Set<String> rptProfiles;
+
+    @JsonProperty("claim_token_profiles_supported")
+    private Set<String> claimTokenProfiles;
+
+    @JsonProperty("dynamic_client_endpoint")
+    private URI dynamicClientEndpoint;
+
+    @JsonProperty("token_endpoint")
+    private URI tokenEndpoint;
+
+    @JsonProperty("authorization_endpoint")
+    private URI authorizationEndpoint;
+
+    @JsonProperty("requesting_party_claims_endpoint")
+    private URI requestingPartyClaimsEndpoint;
+
+    @JsonProperty("resource_set_registration_endpoint")
+    private URI resourceSetRegistrationEndpoint;
+
+    @JsonProperty("introspection_endpoint")
+    private URI introspectionEndpoint;
+
+    @JsonProperty("permission_registration_endpoint")
+    private URI permissionRegistrationEndpoint;
+
+    @JsonProperty("rpt_endpoint")
+    private URI rptEndpoint;
+
+    /**
+     * Non-standard, Keycloak specific configuration options
+     */
+    private String realm;
+
+    private String realmPublicKey;
+
+    private URI serverUrl;
+
+    public String getVersion() {
+        return this.version;
+    }
+
+    void setVersion(final String version) {
+        this.version = version;
+    }
+
+    public URI getIssuer() {
+        return this.issuer;
+    }
+
+    void setIssuer(final URI issuer) {
+        this.issuer = issuer;
+    }
+
+    public Set<String> getPatProfiles() {
+        return this.patProfiles;
+    }
+
+    void setPatProfiles(final Set<String> patProfiles) {
+        this.patProfiles = patProfiles;
+    }
+
+    public Set<String> getPatGrantTypes() {
+        return this.patGrantTypes;
+    }
+
+    void setPatGrantTypes(final Set<String> patGrantTypes) {
+        this.patGrantTypes = patGrantTypes;
+    }
+
+    public Set<String> getAatProfiles() {
+        return this.aatProfiles;
+    }
+
+    void setAatProfiles(final Set<String> aatProfiles) {
+        this.aatProfiles = aatProfiles;
+    }
+
+    public Set<String> getAatGrantTypes() {
+        return this.aatGrantTypes;
+    }
+
+    void setAatGrantTypes(final Set<String> aatGrantTypes) {
+        this.aatGrantTypes = aatGrantTypes;
+    }
+
+    public Set<String> getRptProfiles() {
+        return this.rptProfiles;
+    }
+
+    void setRptProfiles(final Set<String> rptProfiles) {
+        this.rptProfiles = rptProfiles;
+    }
+
+    public Set<String> getClaimTokenProfiles() {
+        return this.claimTokenProfiles;
+    }
+
+    void setClaimTokenProfiles(final Set<String> claimTokenProfiles) {
+        this.claimTokenProfiles = claimTokenProfiles;
+    }
+
+    public URI getDynamicClientEndpoint() {
+        return this.dynamicClientEndpoint;
+    }
+
+    void setDynamicClientEndpoint(final URI dynamicClientEndpoint) {
+        this.dynamicClientEndpoint = dynamicClientEndpoint;
+    }
+
+    public URI getTokenEndpoint() {
+        return this.tokenEndpoint;
+    }
+
+    void setTokenEndpoint(final URI tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    public URI getAuthorizationEndpoint() {
+        return this.authorizationEndpoint;
+    }
+
+    void setAuthorizationEndpoint(final URI authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    public URI getRequestingPartyClaimsEndpoint() {
+        return this.requestingPartyClaimsEndpoint;
+    }
+
+    void setRequestingPartyClaimsEndpoint(final URI requestingPartyClaimsEndpoint) {
+        this.requestingPartyClaimsEndpoint = requestingPartyClaimsEndpoint;
+    }
+
+    public URI getResourceSetRegistrationEndpoint() {
+        return this.resourceSetRegistrationEndpoint;
+    }
+
+    void setResourceSetRegistrationEndpoint(final URI resourceSetRegistrationEndpoint) {
+        this.resourceSetRegistrationEndpoint = resourceSetRegistrationEndpoint;
+    }
+
+    public URI getIntrospectionEndpoint() {
+        return this.introspectionEndpoint;
+    }
+
+    void setIntrospectionEndpoint(final URI introspectionEndpoint) {
+        this.introspectionEndpoint = introspectionEndpoint;
+    }
+
+    public URI getPermissionRegistrationEndpoint() {
+        return this.permissionRegistrationEndpoint;
+    }
+
+    void setPermissionRegistrationEndpoint(final URI permissionRegistrationEndpoint) {
+        this.permissionRegistrationEndpoint = permissionRegistrationEndpoint;
+    }
+
+    public URI getRptEndpoint() {
+        return this.rptEndpoint;
+    }
+
+    void setRptEndpoint(final URI rptEndpoint) {
+        this.rptEndpoint = rptEndpoint;
+    }
+
+    public String getRealm() {
+        return this.realm;
+    }
+
+    public void setRealm(final String realm) {
+        this.realm = realm;
+    }
+
+    public String getRealmPublicKey() {
+        return this.realmPublicKey;
+    }
+
+    public void setRealmPublicKey(String realmPublicKey) {
+        this.realmPublicKey = realmPublicKey;
+    }
+
+    public URI getServerUrl() {
+        return this.serverUrl;
+    }
+
+    public void setServerUrl(URI serverUrl) {
+        this.serverUrl = serverUrl;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java
new file mode 100644
index 0000000..7eaccb4
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.representations.JsonWebToken;
+import org.keycloak.representations.authorization.Permission;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TokenIntrospectionResponse extends JsonWebToken {
+
+    @JsonProperty
+    private Boolean active;
+
+    private List<Permission> permissions;
+
+    public Boolean getActive() {
+        return this.active;
+    }
+
+    public List<Permission> getPermissions() {
+        return this.permissions;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java
new file mode 100644
index 0000000..49b6d2d
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java
@@ -0,0 +1,58 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.resource;
+
+
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.representation.AuthorizationRequest;
+import org.keycloak.authorization.client.representation.AuthorizationResponse;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationResource {
+
+    private final Http http;
+    private final String accessToken;
+
+    public AuthorizationResource(Http http, String aat) {
+        this.http = http;
+        this.accessToken = aat;
+    }
+
+    public AuthorizationResponse authorize(AuthorizationRequest request) {
+        try {
+            return this.http.<AuthorizationResponse>post("/authz/authorize")
+                    .authorizationBearer(this.accessToken)
+                    .json(JsonSerialization.writeValueAsBytes(request))
+                    .response().json(AuthorizationResponse.class).execute();
+        } catch (HttpResponseException e) {
+            if (403 == e.getStatusCode()) {
+                throw new AuthorizationDeniedException(e);
+            }
+            throw new RuntimeException("Failed to obtain authorization data.", e);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to obtain authorization data.", e);
+        }
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java
new file mode 100644
index 0000000..55c0abd
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java
@@ -0,0 +1,54 @@
+package org.keycloak.authorization.client.resource;
+
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.representation.EntitlementRequest;
+import org.keycloak.authorization.client.representation.EntitlementResponse;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementResource {
+
+    private final Http http;
+    private final String eat;
+
+    public EntitlementResource(Http http, String eat) {
+        this.http = http;
+        this.eat = eat;
+    }
+
+    public EntitlementResponse getAll(String resourceServerId) {
+        try {
+            return this.http.<EntitlementResponse>get("/authz/entitlement/" + resourceServerId)
+                    .authorizationBearer(this.eat)
+                    .response()
+                        .json(EntitlementResponse.class).execute();
+        } catch (HttpResponseException e) {
+            if (403 == e.getStatusCode()) {
+                throw new AuthorizationDeniedException(e);
+            }
+            throw new RuntimeException("Failed to obtain entitlements.", e);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to obtain entitlements.", e);
+        }
+    }
+
+    public EntitlementResponse get(String resourceServerId, EntitlementRequest request) {
+        try {
+            return this.http.<EntitlementResponse>post("/authz/entitlement/" + resourceServerId)
+                    .authorizationBearer(this.eat)
+                    .json(JsonSerialization.writeValueAsBytes(request))
+                    .response().json(EntitlementResponse.class).execute();
+        } catch (HttpResponseException e) {
+            if (403 == e.getStatusCode()) {
+                throw new AuthorizationDeniedException(e);
+            }
+            throw new RuntimeException("Failed to obtain entitlements.", e);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to obtain entitlements.", e);
+        }
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
new file mode 100644
index 0000000..72247b2
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.resource;
+
+import org.keycloak.authorization.client.representation.PermissionRequest;
+import org.keycloak.authorization.client.representation.PermissionResponse;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionResource {
+
+    private final Http http;
+    private final String pat;
+
+    public PermissionResource(Http http, String pat) {
+        this.http = http;
+        this.pat = pat;
+    }
+
+    public PermissionResponse forResource(PermissionRequest request) {
+        try {
+            return this.http.<PermissionResponse>post("/authz/protection/permission")
+                    .authorizationBearer(this.pat)
+                    .json(JsonSerialization.writeValueAsBytes(request))
+                    .response().json(PermissionResponse.class).execute();
+        } catch (IOException e) {
+            throw new RuntimeException("Error obtaining permission ticket.", e);
+        }
+    }
+}
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
new file mode 100644
index 0000000..e237642
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -0,0 +1,91 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.client.resource;
+
+import org.keycloak.authorization.client.representation.RegistrationResponse;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.util.JsonSerialization;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ProtectedResource {
+
+    private final Http http;
+    private final String pat;
+
+    public ProtectedResource(Http http, String pat) {
+        this.http = http;
+        this.pat = pat;
+    }
+
+    public RegistrationResponse create(ResourceRepresentation resource) {
+        try {
+            return this.http.<RegistrationResponse>post("/authz/protection/resource_set")
+                    .authorizationBearer(this.pat)
+                    .json(JsonSerialization.writeValueAsBytes(resource))
+                    .response().json(RegistrationResponse.class).execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not create resource.", e);
+        }
+    }
+
+    public RegistrationResponse findById(String id) {
+        try {
+            return this.http.<RegistrationResponse>get("/authz/protection/resource_set/" + id)
+                    .authorizationBearer(this.pat)
+                    .response().json(RegistrationResponse.class).execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not find resource.", e);
+        }
+    }
+
+    public Set<String> findByFilter(String filter) {
+        try {
+            return this.http.<Set>get("/authz/protection/resource_set")
+                    .authorizationBearer(this.pat)
+                    .param("filter", filter)
+                    .response().json(Set.class).execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not find resource.", e);
+        }
+    }
+
+    public Set<String> findAll() {
+        try {
+            return this.http.<Set>get("/authz/protection/resource_set")
+                    .authorizationBearer(this.pat)
+                    .response().json(Set.class).execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not find resource.", e);
+        }
+    }
+
+    public void delete(String id) {
+        try {
+            this.http.delete("/authz/protection/resource_set/" + id)
+                    .authorizationBearer(this.pat)
+                    .execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not delete resource.", e);
+        }
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
new file mode 100644
index 0000000..536b188
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
@@ -0,0 +1,57 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.resource;
+
+import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
+import org.keycloak.authorization.client.util.Http;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ProtectionResource {
+
+    private final String pat;
+    private final Http http;
+
+    public ProtectionResource(Http http, String pat) {
+        if (pat == null) {
+            throw new RuntimeException("No access token was provided when creating client for Protection API.");
+        }
+
+        this.http = http;
+        this.pat = pat;
+    }
+
+    public ProtectedResource resource() {
+        return new ProtectedResource(http, pat);
+    }
+
+    public PermissionResource permission() {
+        return new PermissionResource(http, pat);
+    }
+
+    public TokenIntrospectionResponse introspectRequestingPartyToken(String rpt) {
+        return this.http.<TokenIntrospectionResponse>post("/protocol/openid-connect/token/introspect")
+                .authentication()
+                    .oauth2ClientCredentials()
+                .form()
+                    .param("token_type_hint", "requesting_party_token")
+                    .param("token", rpt)
+                .response().json(TokenIntrospectionResponse.class).execute();
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java
new file mode 100644
index 0000000..503772f
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+import org.apache.http.client.methods.RequestBuilder;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
+
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Http {
+
+    private final Configuration configuration;
+    private ServerConfiguration serverConfiguration;
+
+    public Http(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    public <R> HttpMethod<R> get(String path) {
+        return method(RequestBuilder.get(this.serverConfiguration.getIssuer() + path));
+    }
+
+    public <R> HttpMethod<R> get(URI path) {
+        return method(RequestBuilder.get(path));
+    }
+
+    public <R> HttpMethod<R> post(URI path) {
+        return method(RequestBuilder.post(path));
+    }
+
+    public <R> HttpMethod<R> post(String path) {
+        return method(RequestBuilder.post(this.serverConfiguration.getIssuer() + path));
+    }
+
+    public <R> HttpMethod<R> delete(String path) {
+        return method(RequestBuilder.delete(this.serverConfiguration.getIssuer() + path));
+    }
+
+    private <R> HttpMethod<R> method(RequestBuilder builder) {
+        return new HttpMethod(this.configuration, builder);
+    }
+
+    public void setServerConfiguration(ServerConfiguration serverConfiguration) {
+        this.serverConfiguration = serverConfiguration;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java
new file mode 100644
index 0000000..be83987
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.keycloak.authorization.client.Configuration;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class HttpMethod<R> {
+
+    private final HttpClient httpClient;
+    private final RequestBuilder builder;
+    protected final Configuration configuration;
+    protected final HashMap<String, String> headers;
+    protected final HashMap<String, String> params;
+    private HttpMethodResponse<R> response;
+
+    public HttpMethod(Configuration configuration, RequestBuilder builder) {
+        this(configuration, builder, new HashMap<>(), new HashMap<>());
+    }
+
+    public HttpMethod(Configuration configuration, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
+        this.configuration = configuration;
+        this.httpClient = configuration.getHttpClient();
+        this.builder = builder;
+        this.params = params;
+        this.headers = headers;
+    }
+
+    public void execute() {
+        execute(new HttpResponseProcessor<R>() {
+            @Override
+            public R process(byte[] entity) {
+                return null;
+            }
+        });
+    }
+
+    public R execute(HttpResponseProcessor<R> responseProcessor) {
+        byte[] bytes = null;
+
+        try {
+            for (Map.Entry<String, String> header : this.headers.entrySet()) {
+                this.builder.setHeader(header.getKey(), header.getValue());
+            }
+
+            preExecute(this.builder);
+
+            HttpResponse response = this.httpClient.execute(this.builder.build());
+            HttpEntity entity = response.getEntity();
+
+            if (entity != null) {
+                bytes = EntityUtils.toByteArray(entity);
+            }
+
+            StatusLine statusLine = response.getStatusLine();
+            int statusCode = statusLine.getStatusCode();
+
+            if (statusCode < 200 || statusCode >= 300) {
+                throw new HttpResponseException("Unexpected response from server: " + statusCode + " / " + statusLine.getReasonPhrase(), statusCode, statusLine.getReasonPhrase(), bytes);
+            }
+
+            if (bytes == null) {
+                return null;
+            }
+
+            return responseProcessor.process(bytes);
+        } catch (HttpResponseException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException("Error executing http method [" + builder + "]. Response : " + new String(bytes), e);
+        }
+    }
+
+    protected void preExecute(RequestBuilder builder) {
+        for (Map.Entry<String, String> param : params.entrySet()) {
+            builder.addParameter(param.getKey(), param.getValue());
+        }
+    }
+
+    public HttpMethod<R> authorizationBearer(String bearer) {
+        this.builder.addHeader("Authorization", "Bearer " + bearer);
+        return this;
+    }
+
+    public HttpMethodResponse<R> response() {
+        this.response = new HttpMethodResponse(this);
+        return this.response;
+    }
+
+    public HttpMethodAuthenticator<R> authentication() {
+        return new HttpMethodAuthenticator<R>(this);
+    }
+
+    public HttpMethod<R> param(String name, String value) {
+        this.params.put(name, value);
+        return this;
+    }
+
+    public HttpMethod<R> json(byte[] entity) {
+        this.builder.addHeader("Content-Type", "application/json");
+        this.builder.setEntity(new ByteArrayEntity(entity));
+        return this;
+    }
+
+    public HttpMethod<R> form() {
+        return new HttpMethod<R>(this.configuration, this.builder, this.params, this.headers) {
+            @Override
+            protected void preExecute(RequestBuilder builder) {
+                if (params != null) {
+                    List<NameValuePair> formparams = new ArrayList<>();
+
+                    for (Map.Entry<String, String> param : params.entrySet()) {
+                        formparams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
+                    }
+
+                    try {
+                        builder.setEntity(new UrlEncodedFormEntity(formparams, "UTF-8"));
+                    } catch (UnsupportedEncodingException e) {
+                        throw new RuntimeException("Error creating form parameters");
+                    }
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
new file mode 100644
index 0000000..d67090a
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+import org.keycloak.OAuth2Constants;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class HttpMethodAuthenticator<R> {
+
+    private final HttpMethod<R> method;
+
+    public HttpMethodAuthenticator(HttpMethod<R> method) {
+        this.method = method;
+    }
+
+    public HttpMethod<R> oauth2ClientCredentials() {
+        this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS);
+        configureClientCredentials();
+        return this.method;
+    }
+
+    public HttpMethod<R> oauth2ResourceOwnerPassword(String userName, String password) {
+        this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+        this.method.params.put("username", userName);
+        this.method.params.put("password", password);
+        configureClientCredentials();
+        return this.method;
+    }
+
+    private void configureClientCredentials() {
+        this.method.configuration.getClientAuthenticator().configureClientCredentials(this.method.params, this.method.headers);
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java
new file mode 100644
index 0000000..4155240
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java
@@ -0,0 +1,61 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class HttpMethodResponse<R> {
+
+    private final HttpMethod<R> method;
+
+    public HttpMethodResponse(HttpMethod method) {
+        this.method = method;
+    }
+
+    public R execute() {
+        return this.method.execute(new HttpResponseProcessor<R>() {
+            @Override
+            public R process(byte[] entity) {
+                return null;
+            }
+        });
+    }
+
+    public HttpMethodResponse<R> json(Class<R> responseType) {
+        return new HttpMethodResponse<R>(this.method) {
+            @Override
+            public R execute() {
+                return method.execute(new HttpResponseProcessor<R>() {
+                    @Override
+                    public R process(byte[] entity) {
+                        try {
+                            return JsonSerialization.readValue(entity, responseType);
+                        } catch (IOException e) {
+                            throw new RuntimeException("Error parsing JSON response.", e);
+                        }
+                    }
+                });
+            }
+        };
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java
new file mode 100644
index 0000000..3531f40
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class HttpResponseException extends RuntimeException {
+
+    private final int statusCode;
+    private final String reasonPhrase;
+    private final byte[] bytes;
+
+    public HttpResponseException(String message, int statusCode, String reasonPhrase, byte[] bytes) {
+        super(message);
+        this.statusCode = statusCode;
+        this.reasonPhrase = reasonPhrase;
+        this.bytes = bytes;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public String getReasonPhrase() {
+        return reasonPhrase;
+    }
+
+    public byte[] getBytes() {
+        return bytes;
+    }
+}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseProcessor.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseProcessor.java
new file mode 100644
index 0000000..72eba04
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseProcessor.java
@@ -0,0 +1,26 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.client.util;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface HttpResponseProcessor<R> {
+
+    R process(byte[] entity);
+}
diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml
new file mode 100644
index 0000000..279867c
--- /dev/null
+++ b/authz/policy/common/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-provider-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-policy-common</artifactId>
+    <packaging>jar</packaging>
+
+    <name>KeyCloak AuthZ: Common Policy Providers</name>
+    <description>KeyCloak AuthZ: Common Policy Providers</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-server-spi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java
new file mode 100644
index 0000000..0cfb0f9
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.keycloak.authorization.policy.provider.aggregated;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyAdminResource implements PolicyProviderAdminService {
+
+    private final ResourceServer resourceServer;
+
+    public AggregatePolicyAdminResource(ResourceServer resourceServer) {
+        this.resourceServer = resourceServer;
+    }
+
+    @Override
+    public void onCreate(Policy policy) {
+        verifyCircularReference(policy, new ArrayList<>());
+    }
+
+    @Override
+    public void onUpdate(Policy policy) {
+        verifyCircularReference(policy, new ArrayList<>());
+    }
+
+    private void verifyCircularReference(Policy policy, List<String> ids) {
+        if (!policy.getType().equals("aggregate")) {
+            return;
+        }
+
+        if (ids.contains(policy.getId())) {
+            throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
+        }
+
+        ids.add(policy.getId());
+
+        for (Policy associated : policy.getAssociatedPolicies()) {
+            verifyCircularReference(associated, ids);
+        }
+    }
+
+    @Override
+    public void onRemove(Policy policy) {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java
new file mode 100644
index 0000000..b1e668a
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProvider.java
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.policy.provider.aggregated;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
+import org.keycloak.authorization.policy.evaluation.DefaultEvaluation;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyProvider implements PolicyProvider {
+
+    private final Policy policy;
+    private final AuthorizationProvider authorization;
+
+    public AggregatePolicyProvider(Policy policy, AuthorizationProvider authorization) {
+        this.policy = policy;
+        this.authorization = authorization;
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+        //TODO: need to detect deep recursions
+        DecisionResultCollector decision = new DecisionResultCollector() {
+            @Override
+            protected void onComplete(List<Result> results) {
+                if (results.isEmpty()) {
+                    evaluation.deny();
+                } else {
+                    Result result = results.iterator().next();
+
+                    if (Effect.PERMIT.equals(result.getEffect())) {
+                        evaluation.grant();
+                    }
+                }
+            }
+        };
+
+        this.policy.getAssociatedPolicies().forEach(associatedPolicy -> {
+            PolicyProviderFactory providerFactory = authorization.getProviderFactory(associatedPolicy.getType());
+            PolicyProvider policyProvider = providerFactory.create(associatedPolicy, authorization);
+            policyProvider.evaluate(new DefaultEvaluation(evaluation.getPermission(), evaluation.getContext(), policy, associatedPolicy, decision));
+        });
+
+        decision.onComplete();
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
new file mode 100644
index 0000000..9c7faad
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
@@ -0,0 +1,79 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.policy.provider.aggregated;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "Aggregated";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Others";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new AggregatePolicyProvider(policy, authorization);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return new AggregatePolicyAdminResource(resourceServer);
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "aggregate";
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java
new file mode 100644
index 0000000..eeeb3ea
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProvider.java
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.policy.provider.js;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyProvider implements PolicyProvider {
+
+    private final Policy policy;
+
+    public JSPolicyProvider(Policy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        ScriptEngine engine = manager.getEngineByName("nashorn");
+
+        engine.put("$evaluation", evaluation);
+
+        try {
+            engine.eval(policy.getConfig().get("code"));
+        } catch (ScriptException e) {
+            throw new RuntimeException("Error evaluating JS Policy [" + policy.getName() + "].", e);
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
new file mode 100644
index 0000000..8134d95
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
@@ -0,0 +1,62 @@
+package org.keycloak.authorization.policy.provider.js;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "JavaScript";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Rule Based";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new JSPolicyProvider(policy);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return null;
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "js";
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java
new file mode 100644
index 0000000..c76a989
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProvider.java
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.policy.provider.resource;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePolicyProvider implements PolicyProvider {
+
+    public ResourcePolicyProvider(Policy policy) {
+
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
new file mode 100644
index 0000000..8ea4800
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
@@ -0,0 +1,62 @@
+package org.keycloak.authorization.policy.provider.resource;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "Resource-Based";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Permission";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new ResourcePolicyProvider(policy);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return null;
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "resource";
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
new file mode 100644
index 0000000..af5f322
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.policy.provider.role;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+
+import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyProvider implements PolicyProvider {
+
+    private final Policy policy;
+    private final AuthorizationProvider authorization;
+
+    public RolePolicyProvider(Policy policy, AuthorizationProvider authorization) {
+        this.policy = policy;
+        this.authorization = authorization;
+    }
+
+    public RolePolicyProvider() {
+        this(null, null);
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+        EvaluationContext context = evaluation.getContext();
+        String[] roleIds = getRoles(this.policy);
+
+        if (roleIds.length > 0) {
+            Identity identity = context.getIdentity();
+
+            for (String roleId : roleIds) {
+                RoleModel role = getCurrentRealm().getRoleById(roleId);
+
+                if (role != null && identity.hasRole(role.getName())) {
+                    evaluation.grant();
+                    break;
+                }
+            }
+        }
+    }
+
+    private RealmModel getCurrentRealm() {
+        return this.authorization.getKeycloakSession().getContext().getRealm();
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
new file mode 100644
index 0000000..598a41e
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
@@ -0,0 +1,132 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.keycloak.authorization.policy.provider.role;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
+import org.keycloak.models.RoleModel;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "Role-Based";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Identity Based";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new RolePolicyProvider(policy, authorization);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return null;
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return new RolePolicyProvider();
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+        factory.register(event -> {
+            if (event instanceof RoleRemovedEvent) {
+                KeycloakSession keycloakSession = ((RoleRemovedEvent) event).getKeycloakSession();
+                AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
+                PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
+                RoleModel removedRole = ((RoleRemovedEvent) event).getRole();
+
+                policyStore.findByType(getId()).forEach(policy -> {
+                    List<String> roles = new ArrayList<>();
+
+                    for (String roleId : getRoles(policy)) {
+                        if (!roleId.equals(removedRole.getId())) {
+                            roles.add(roleId);
+                        }
+                    }
+
+                    try {
+                        if (roles.isEmpty()) {
+                            policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
+                                dependentPolicy.removeAssociatedPolicy(policy);
+                            });
+                            policyStore.delete(policy.getId());
+                        } else {
+                            policy.getConfig().put("roles", JsonSerialization.writeValueAsString(roles));
+                        }
+                    } catch (IOException e) {
+                        throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
+                    }
+                });
+            }
+        });
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "role";
+    }
+
+    static String[] getRoles(Policy policy) {
+        String roles = policy.getConfig().get("roles");
+
+        if (roles != null) {
+            try {
+                return JsonSerialization.readValue(roles.getBytes(), String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
+            }
+        }
+
+        return new String[]{};
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java
new file mode 100644
index 0000000..5c10cc3
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProvider.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.policy.provider.scope;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePolicyProvider implements PolicyProvider {
+
+    private final Policy policy;
+
+    public ScopePolicyProvider(Policy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
new file mode 100644
index 0000000..6ed0cd5
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
@@ -0,0 +1,62 @@
+package org.keycloak.authorization.policy.provider.scope;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "Scope-Based";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Permission";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new ScopePolicyProvider(policy);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return null;
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "scope";
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
new file mode 100644
index 0000000..75de911
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.provider.time;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyAdminResource implements PolicyProviderAdminService {
+
+    @Override
+    public void onCreate(Policy policy) {
+        validateConfig(policy);
+    }
+
+    private void validateConfig(Policy policy) {
+        String nbf = policy.getConfig().get("nbf");
+        String noa = policy.getConfig().get("noa");
+
+        if (nbf == null && noa == null) {
+            throw new RuntimeException("You must provide NotBefore, NotOnOrAfter or both.");
+        }
+
+        validateFormat(nbf);
+        validateFormat(noa);
+    }
+
+    @Override
+    public void onUpdate(Policy policy) {
+        validateConfig(policy);
+    }
+
+    @Override
+    public void onRemove(Policy policy) {
+    }
+
+    private void validateFormat(String date) {
+        try {
+            new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date));
+        } catch (Exception e) {
+            throw new RuntimeException("Could not parse a date using format [" + date + "]");
+        }
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
new file mode 100644
index 0000000..dc6af9f
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.policy.provider.time;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyProvider implements PolicyProvider {
+
+    static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss";
+
+    private final Policy policy;
+    private final SimpleDateFormat dateFormat;
+    private final Date currentDate;
+
+    public TimePolicyProvider(Policy policy) {
+        this.policy = policy;
+        this.dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
+        this.currentDate = new Date();
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+        try {
+            String notBefore = this.policy.getConfig().get("nbf");
+
+            if (notBefore != null) {
+
+                if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) {
+                    evaluation.deny();
+                    return;
+                }
+            }
+
+            String notOnOrAfter = this.policy.getConfig().get("noa");
+
+            if (notOnOrAfter != null) {
+                if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
+                    evaluation.deny();
+                    return;
+                }
+            }
+
+            evaluation.grant();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not evaluate time-based policy [" + this.policy.getName() + "].", e);
+        }
+    }
+
+    static String format(String notBefore) {
+        String trimmed = notBefore.trim();
+
+        if (trimmed.length() == 10) {
+            notBefore = trimmed + " 00:00:00";
+        }
+
+        return notBefore;
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
new file mode 100644
index 0000000..efe3cd2
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
@@ -0,0 +1,62 @@
+package org.keycloak.authorization.policy.provider.time;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "Time";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Time Based";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new TimePolicyProvider(policy);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return new TimePolicyAdminResource();
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "time";
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
new file mode 100644
index 0000000..a6fc0a4
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
@@ -0,0 +1,57 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.policy.provider.user;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+import static org.keycloak.authorization.policy.provider.user.UserPolicyProviderFactory.getUsers;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyProvider implements PolicyProvider {
+
+    private final Policy policy;
+
+    public UserPolicyProvider(Policy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluation) {
+        EvaluationContext context = evaluation.getContext();
+        String[] userIds = getUsers(this.policy);
+
+        if (userIds.length > 0) {
+            for (String userId : userIds) {
+                if (context.getIdentity().getId().equals(userId)) {
+                    evaluation.grant();
+                    break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
new file mode 100644
index 0000000..9461474
--- /dev/null
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
@@ -0,0 +1,132 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.keycloak.authorization.policy.provider.user;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserModel.UserRemovedEvent;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyProviderFactory implements PolicyProviderFactory {
+
+    @Override
+    public String getName() {
+        return "User-Based";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Identity Based";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        return new UserPolicyProvider(policy);
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return null;
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+        factory.register(event -> {
+            if (event instanceof UserRemovedEvent) {
+                KeycloakSession keycloakSession = ((UserRemovedEvent) event).getKeycloakSession();
+                AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
+                PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
+                UserModel removedUser = ((UserRemovedEvent) event).getUser();
+
+                policyStore.findByType(getId()).forEach(policy -> {
+                    List<String> users = new ArrayList<>();
+
+                    for (String userId : getUsers(policy)) {
+                        if (!userId.equals(removedUser.getId())) {
+                            users.add(userId);
+                        }
+                    }
+
+                    try {
+                        if (users.isEmpty()) {
+                            policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
+                                dependentPolicy.removeAssociatedPolicy(policy);
+                            });
+                            policyStore.delete(policy.getId());
+                        } else {
+                            policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
+                        }
+                    } catch (IOException e) {
+                        throw new RuntimeException("Error while synchronizing users with policy [" + policy.getName() + "].", e);
+                    }
+                });
+            }
+        });
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "user";
+    }
+
+    static String[] getUsers(Policy policy) {
+        String roles = policy.getConfig().get("users");
+
+        if (roles != null) {
+            try {
+                return JsonSerialization.readValue(roles.getBytes(), String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
+            }
+        }
+
+        return new String[]{};
+    }
+}
diff --git a/authz/policy/common/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory b/authz/policy/common/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory
new file mode 100644
index 0000000..1e8dfd2
--- /dev/null
+++ b/authz/policy/common/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory
@@ -0,0 +1,43 @@
+#
+#  Copyright 2016 Red Hat, Inc. and/or its affiliates
+#  and other contributors as indicated by the @author tags.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+#
+
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.authorization.policy.provider.aggregated.AggregatePolicyProviderFactory
+org.keycloak.authorization.policy.provider.js.JSPolicyProviderFactory
+org.keycloak.authorization.policy.provider.resource.ResourcePolicyProviderFactory
+org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory
+org.keycloak.authorization.policy.provider.scope.ScopePolicyProviderFactory
+org.keycloak.authorization.policy.provider.time.TimePolicyProviderFactory
+org.keycloak.authorization.policy.provider.user.UserPolicyProviderFactory
\ No newline at end of file
diff --git a/authz/policy/drools/pom.xml b/authz/policy/drools/pom.xml
new file mode 100644
index 0000000..0f13d2a
--- /dev/null
+++ b/authz/policy/drools/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-provider-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-policy-drools</artifactId>
+    <packaging>jar</packaging>
+
+    <name>KeyCloak AuthZ: Drools Policy Provider</name>
+    <description>KeyCloak AuthZ: Drools Policy Provider</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-server-spi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.spec.javax.ws.rs</groupId>
+            <artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicy.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicy.java
new file mode 100644
index 0000000..c8848c1
--- /dev/null
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicy.java
@@ -0,0 +1,71 @@
+package org.keycloak.authorization.policy.provider.drools;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.kie.api.KieServices;
+import org.kie.api.builder.KieScanner;
+import org.kie.api.runtime.KieContainer;
+import org.kie.api.runtime.KieSession;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class DroolsPolicy {
+
+    private static final int SESSION_POOL_SIZE = 10;
+
+    private final KieContainer kc;
+    private final KieScanner kcs;
+    private final String sessionName;
+
+    DroolsPolicy(KieServices ks, Policy associatedPolicy) {
+        String groupId = associatedPolicy.getConfig().get("mavenArtifactGroupId");
+        String artifactId = associatedPolicy.getConfig().get("mavenArtifactId");
+        String version = associatedPolicy.getConfig().get("mavenArtifactVersion");
+        String scannerPeriod = associatedPolicy.getConfig().get("scannerPeriod");
+        String scannerPeriodUnit = associatedPolicy.getConfig().get("scannerPeriodUnit");
+        this.sessionName = associatedPolicy.getConfig().get("sessionName");
+
+        this.kc = ks.newKieContainer(ks.newReleaseId(groupId, artifactId, version));
+        this.kcs = ks.newKieScanner(this.kc);
+        this.kcs.start(toMillis(scannerPeriod, scannerPeriodUnit));
+
+        KieSession session = this.kc.newKieSession(this.sessionName);
+
+        if (session == null) {
+            throw new RuntimeException("Could not obtain session with name [" + this.sessionName + "].");
+        }
+
+        session.dispose();
+    }
+
+    void evaluate(Evaluation evaluation) {
+        KieSession session = this.kc.newKieSession(this.sessionName);
+
+        session.insert(evaluation);
+        session.fireAllRules();
+
+        session.dispose();
+    }
+
+    void dispose() {
+        this.kcs.stop();
+    }
+
+    private long toMillis(final String scannerPeriod, final String scannerPeriodUnit) {
+        switch (scannerPeriodUnit) {
+            case "Seconds":
+                return TimeUnit.SECONDS.toMillis(Integer.valueOf(scannerPeriod));
+            case "Minutes":
+                return TimeUnit.MINUTES.toMillis(Integer.valueOf(scannerPeriod));
+            case "Hours":
+                return TimeUnit.HOURS.toMillis(Integer.valueOf(scannerPeriod));
+            case "Days":
+                return TimeUnit.DAYS.toMillis(Integer.valueOf(scannerPeriod));
+        }
+
+        throw new RuntimeException("Invalid time period [" + scannerPeriodUnit + "].");
+    }
+}
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java
new file mode 100644
index 0000000..1ee1d34
--- /dev/null
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java
@@ -0,0 +1,65 @@
+package org.keycloak.authorization.policy.provider.drools;
+
+import org.keycloak.authorization.admin.representation.PolicyRepresentation;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.kie.api.runtime.KieContainer;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DroolsPolicyAdminResource implements PolicyProviderAdminService {
+
+    private final ResourceServer resourceServer;
+    private final DroolsPolicyProviderFactory factory;
+
+    public DroolsPolicyAdminResource(ResourceServer resourceServer, DroolsPolicyProviderFactory factory) {
+        this.resourceServer = resourceServer;
+        this.factory = factory;
+    }
+
+    @Override
+    public void onCreate(Policy policy) {
+        this.factory.update(policy);
+    }
+
+    @Override
+    public void onUpdate(Policy policy) {
+        this.factory.update(policy);
+    }
+
+    @Override
+    public void onRemove(Policy policy) {
+        this.factory.remove(policy);
+    }
+
+    @Path("/resolveModules")
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response resolveModules(PolicyRepresentation policy) {
+        return Response.ok(getContainer(policy).getKieBaseNames()).build();
+    }
+
+    @Path("/resolveSessions")
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response resolveSessions(PolicyRepresentation policy) {
+        return Response.ok(getContainer(policy).getKieSessionNamesInKieBase(policy.getConfig().get("moduleName"))).build();
+    }
+
+    private KieContainer getContainer(PolicyRepresentation policy) {
+        String groupId = policy.getConfig().get("mavenArtifactGroupId");
+        String artifactId = policy.getConfig().get("mavenArtifactId");
+        String version = policy.getConfig().get("mavenArtifactVersion");
+        return this.factory.getKieContainer(groupId, artifactId, version);
+    }
+}
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java
new file mode 100644
index 0000000..c53e361
--- /dev/null
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProvider.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.policy.provider.drools;
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DroolsPolicyProvider implements PolicyProvider {
+
+    private final DroolsPolicy policy;
+
+    public DroolsPolicyProvider(DroolsPolicy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public void evaluate(Evaluation evaluationt) {
+        this.policy.evaluate(evaluationt);
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
new file mode 100644
index 0000000..0e1e208
--- /dev/null
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
@@ -0,0 +1,109 @@
+package org.keycloak.authorization.policy.provider.drools;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.utils.PostMigrationEvent;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderEventListener;
+import org.keycloak.provider.ProviderFactory;
+import org.kie.api.KieServices;
+import org.kie.api.KieServices.Factory;
+import org.kie.api.runtime.KieContainer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
+
+    private KieServices ks;
+    private final Map<String, DroolsPolicy> containers = new HashMap<>();
+
+    @Override
+    public String getName() {
+        return "Drools";
+    }
+
+    @Override
+    public String getGroup() {
+        return "Rule Based";
+    }
+
+    @Override
+    public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
+        if (!this.containers.containsKey(policy.getId())) {
+            update(policy);
+        }
+
+        return new DroolsPolicyProvider(this.containers.get(policy.getId()));
+    }
+
+    @Override
+    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+        return new DroolsPolicyAdminResource(resourceServer, this);
+    }
+
+    @Override
+    public PolicyProvider create(KeycloakSession session) {
+        return null;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+        this.ks = Factory.get();
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+        factory.register(new ProviderEventListener() {
+
+            @Override
+            public void onEvent(ProviderEvent event) {
+                // Ensure the initialization is done after DB upgrade is finished
+                if (event instanceof PostMigrationEvent) {
+                    ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
+                    AuthorizationProvider authorization = providerFactory.create(factory.create());
+                    authorization.getStoreFactory().getPolicyStore().findByType(getId()).forEach(DroolsPolicyProviderFactory.this::update);
+                }
+            }
+
+        });
+    }
+
+    @Override
+    public void close() {
+        this.containers.values().forEach(DroolsPolicy::dispose);
+        this.containers.clear();
+    }
+
+    @Override
+    public String getId() {
+        return "drools";
+    }
+
+    void update(Policy policy) {
+        remove(policy);
+        this.containers.put(policy.getId(), new DroolsPolicy(this.ks, policy));
+    }
+
+    void remove(Policy policy) {
+        DroolsPolicy holder = this.containers.remove(policy.getId());
+
+        if (holder != null) {
+            holder.dispose();
+        }
+    }
+
+    KieContainer getKieContainer(String groupId, String artifactId, String version) {
+        return this.ks.newKieContainer(this.ks.newReleaseId(groupId, artifactId, version));
+    }
+}
diff --git a/authz/policy/drools/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory b/authz/policy/drools/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory
new file mode 100644
index 0000000..512e334
--- /dev/null
+++ b/authz/policy/drools/src/main/resources/META-INF/services/org.keycloak.authorization.policy.provider.PolicyProviderFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.authorization.policy.provider.drools.DroolsPolicyProviderFactory
\ No newline at end of file
diff --git a/authz/policy/pom.xml b/authz/policy/pom.xml
new file mode 100644
index 0000000..a867c64
--- /dev/null
+++ b/authz/policy/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-provider-parent</artifactId>
+    <packaging>pom</packaging>
+
+    <name>KeyCloak AuthZ: Provider Parent</name>
+    <description>KeyCloak AuthZ: Provider Parent</description>
+
+    <modules>
+        <module>common</module>
+        <module>drools</module>
+    </modules>
+
+</project>
\ No newline at end of file

authz/pom.xml 30(+30 -0)

diff --git a/authz/pom.xml b/authz/pom.xml
new file mode 100644
index 0000000..6616d04
--- /dev/null
+++ b/authz/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-parent</artifactId>
+    <packaging>pom</packaging>
+
+    <name>KeyCloak Authz: Parent</name>
+    <description>KeyCloak AuthZ: Parent</description>
+
+    <modules>
+        <module>policy</module>
+        <module>client</module>
+    </modules>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
+</project>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/AuthorizationContext.java b/core/src/main/java/org/keycloak/AuthorizationContext.java
new file mode 100644
index 0000000..4aa5503
--- /dev/null
+++ b/core/src/main/java/org/keycloak/AuthorizationContext.java
@@ -0,0 +1,93 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak;
+
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+import org.keycloak.representations.authorization.Permission;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationContext {
+
+    private final AccessToken authzToken;
+    private final List<PathConfig> paths;
+    private boolean granted;
+
+    public AuthorizationContext(AccessToken authzToken, List<PathConfig> paths) {
+        this.authzToken = authzToken;
+        this.paths = paths;
+        this.granted = true;
+    }
+
+    public AuthorizationContext() {
+        this(null, null);
+        this.granted = false;
+    }
+
+    public boolean hasPermission(String resourceName, String scopeName) {
+        for (Permission permission : authzToken.getAuthorization().getPermissions()) {
+            for (PathConfig pathHolder : this.paths) {
+                if (pathHolder.getName().equals(resourceName)) {
+                    if (pathHolder.getId().equals(permission.getResourceSetId())) {
+                        if (permission.getScopes().contains(scopeName)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public boolean hasResourcePermission(String resourceName) {
+        for (Permission permission : authzToken.getAuthorization().getPermissions()) {
+            for (PathConfig pathHolder : this.paths) {
+                if (pathHolder.getName().equals(resourceName)) {
+                    if (pathHolder.getId().equals(permission.getResourceSetId())) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public boolean hasScopePermission(String scopeName) {
+        for (Permission permission : authzToken.getAuthorization().getPermissions()) {
+            if (permission.getScopes().contains(scopeName)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public List<Permission> getPermissions() {
+        return this.authzToken.getAuthorization().getPermissions();
+    }
+
+    public boolean isGranted() {
+        return granted;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/KeycloakSecurityContext.java b/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
index 118fd1b..68ac957 100755
--- a/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
+++ b/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
@@ -41,6 +41,7 @@ public class KeycloakSecurityContext implements Serializable {
     // Don't store parsed tokens into HTTP session
     protected transient AccessToken token;
     protected transient IDToken idToken;
+    protected transient AuthorizationContext authorizationContext;
 
     public KeycloakSecurityContext() {
     }
@@ -60,6 +61,10 @@ public class KeycloakSecurityContext implements Serializable {
         return tokenString;
     }
 
+    public AuthorizationContext getAuthorizationContext() {
+        return authorizationContext;
+    }
+
     public IDToken getIdToken() {
         return idToken;
     }
diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index ac800cd..7d7fdea 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -19,10 +19,12 @@ package org.keycloak.representations;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.representations.authorization.Permission;
 
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -81,6 +83,20 @@ public class AccessToken extends IDToken {
         }
     }
 
+    public static class Authorization implements Serializable {
+
+        @JsonProperty("permissions")
+        private List<Permission> permissions;
+
+        public List<Permission> getPermissions() {
+            return permissions;
+        }
+
+        public void setPermissions(List<Permission> permissions) {
+            this.permissions = permissions;
+        }
+    }
+
     @JsonProperty("client_session")
     protected String clientSession;
 
@@ -96,6 +112,9 @@ public class AccessToken extends IDToken {
     @JsonProperty("resource_access")
     protected Map<String, Access> resourceAccess = new HashMap<String, Access>();
 
+    @JsonProperty("authorization")
+    protected Authorization authorization;
+
     public Map<String, Access> getResourceAccess() {
         return resourceAccess;
     }
@@ -219,5 +238,11 @@ public class AccessToken extends IDToken {
         return (AccessToken)super.issuedFor(issuedFor);
     }
 
+    public Authorization getAuthorization() {
+        return authorization;
+    }
 
+    public void setAuthorization(Authorization authorization) {
+        this.authorization = authorization;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index 87b3ab2..91fb5f0 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -36,7 +36,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
         "client-keystore", "client-keystore-password", "client-key-password",
         "always-refresh-token",
         "register-node-at-startup", "register-node-period", "token-store", "principal-attribute",
-        "proxy-url"
+        "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live",
+        "policy-enforcer"
 })
 public class AdapterConfig extends BaseAdapterConfig {
 
@@ -68,6 +69,10 @@ public class AdapterConfig extends BaseAdapterConfig {
     protected String principalAttribute;
     @JsonProperty("turn-off-change-session-id-on-login")
     protected Boolean turnOffChangeSessionIdOnLogin;
+    @JsonProperty("token-minimum-time-to-live")
+    protected int tokenMinimumTimeToLive = 0;
+    @JsonProperty("policy-enforcer")
+    protected PolicyEnforcerConfig policyEnforcerConfig;
 
     /**
      * The Proxy url to use for requests to the auth-server, configurable via the adapter config property {@code proxy-url}.
@@ -187,6 +192,14 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
     }
 
+    public PolicyEnforcerConfig getPolicyEnforcerConfig() {
+        return policyEnforcerConfig;
+    }
+
+    public void setPolicyEnforcerConfig(PolicyEnforcerConfig policyEnforcerConfig) {
+        this.policyEnforcerConfig = policyEnforcerConfig;
+    }
+
     public String getProxyUrl() {
         return proxyUrl;
     }
@@ -194,4 +207,13 @@ public class AdapterConfig extends BaseAdapterConfig {
     public void setProxyUrl(String proxyUrl) {
         this.proxyUrl = proxyUrl;
     }
+
+    public int getTokenMinimumTimeToLive() {
+        return tokenMinimumTimeToLive;
+    }
+
+    public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
+        this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
+    }
+
 }
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
new file mode 100644
index 0000000..5145ec7
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -0,0 +1,221 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.representations.adapters.config;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEnforcerConfig {
+
+    @JsonProperty("create-resources")
+    private Boolean createResources;
+
+    @JsonProperty("enforcement-mode")
+    private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
+
+    @JsonProperty("user-managed-access")
+    private UmaProtocolConfig umaProtocolConfig;
+
+    @JsonProperty("entitlement")
+    private EntitlementProtocolConfig entitlementProtocolConfig;
+
+    @JsonProperty("paths")
+    private List<PathConfig> paths = new ArrayList<>();
+
+    @JsonProperty("online-introspection")
+    private Boolean onlineIntrospection;
+
+    @JsonProperty("on-deny-redirect-to")
+    private String accessDeniedPath;
+
+    public Boolean isCreateResources() {
+        return this.createResources;
+    }
+
+    public List<PathConfig> getPaths() {
+        if (this.paths == null) {
+            return null;
+        }
+
+        return Collections.unmodifiableList(this.paths);
+    }
+
+    public EnforcementMode getEnforcementMode() {
+        return this.enforcementMode;
+    }
+
+    public void setEnforcementMode(EnforcementMode enforcementMode) {
+        this.enforcementMode = enforcementMode;
+    }
+
+    public UmaProtocolConfig getUmaProtocolConfig() {
+        return this.umaProtocolConfig;
+    }
+
+    public EntitlementProtocolConfig getEntitlementProtocolConfig() {
+        return this.entitlementProtocolConfig;
+    }
+
+    public Boolean isOnlineIntrospection() {
+        return onlineIntrospection;
+    }
+
+    public void setPaths(List<PathConfig> paths) {
+        this.paths = paths;
+    }
+
+    public String getAccessDeniedPath() {
+        return accessDeniedPath;
+    }
+
+    public static class PathConfig {
+
+        private String name;
+        private String type;
+        private String path;
+        private List<MethodConfig> methods = new ArrayList<>();
+        private List<String> scopes = Collections.emptyList();
+        private String id;
+        private boolean instance;
+
+        @JsonIgnore
+        private PathConfig parentConfig;
+
+        public String getPath() {
+            return this.path;
+        }
+
+        public void setPath(String path) {
+            this.path = path;
+        }
+
+        public List<String> getScopes() {
+            return this.scopes;
+        }
+
+        public void setScopes(List<String> scopes) {
+            this.scopes = scopes;
+        }
+
+        public List<MethodConfig> getMethods() {
+            return methods;
+        }
+
+        public void setMethods(List<MethodConfig> methods) {
+            this.methods = methods;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void setType(String type) {
+            this.type = type;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        @Override
+        public String toString() {
+            return "PathConfig{" +
+                    "name='" + name + '\'' +
+                    ", type='" + type + '\'' +
+                    ", path='" + path + '\'' +
+                    ", scopes=" + scopes +
+                    ", id='" + id + '\'' +
+                    '}';
+        }
+
+        public boolean hasPattern() {
+            return getPath().indexOf("{") != -1;
+        }
+
+        public boolean isInstance() {
+            return instance;
+        }
+
+        public void setInstance(boolean instance) {
+            this.instance = instance;
+        }
+
+        public void setParentConfig(PathConfig parentConfig) {
+            this.parentConfig = parentConfig;
+        }
+
+        public PathConfig getParentConfig() {
+            return parentConfig;
+        }
+    }
+
+    public static class MethodConfig {
+
+        private String method;
+        private List<String> scopes = Collections.emptyList();
+
+        public String getMethod() {
+            return method;
+        }
+
+        public void setMethod(String method) {
+            this.method = method;
+        }
+
+        public List<String> getScopes() {
+            return scopes;
+        }
+
+        public void setScopes(List<String> scopes) {
+            this.scopes = scopes;
+        }
+    }
+
+    public enum EnforcementMode {
+        PERMISSIVE,
+        ENFORCING,
+        DISABLED
+    }
+
+    public static class UmaProtocolConfig {
+
+    }
+
+    public static class EntitlementProtocolConfig {
+
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/authorization/Permission.java b/core/src/main/java/org/keycloak/representations/authorization/Permission.java
new file mode 100644
index 0000000..1daba20
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/authorization/Permission.java
@@ -0,0 +1,72 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.representations.authorization;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Permission {
+
+    @JsonProperty("resource_set_id")
+    private String resourceSetId;
+
+    @JsonProperty("resource_set_name")
+    private final String resourceSetName;
+
+    private Set<String> scopes;
+
+    public Permission() {
+        this(null, null, null);
+    }
+
+    public Permission(final String resourceSetId, String resourceSetName, final Set<String> scopes) {
+        this.resourceSetId = resourceSetId;
+        this.resourceSetName = resourceSetName;
+        this.scopes = scopes;
+    }
+
+    public String getResourceSetId() {
+        return this.resourceSetId;
+    }
+
+    public String getResourceSetName() {
+        return this.resourceSetName;
+    }
+
+    public Set<String> getScopes() {
+        return this.scopes;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("Permission {").append("id=").append(resourceSetId).append(", name=").append(resourceSetName)
+                .append(", scopes=").append(scopes).append("}");
+
+        return builder.toString();
+    }
+
+    public void setScopes(Set<String> scopes) {
+        this.scopes = scopes;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index a5b3802..f18f4eb 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -47,6 +47,7 @@ public class ClientRepresentation {
     protected Boolean implicitFlowEnabled;
     protected Boolean directAccessGrantsEnabled;
     protected Boolean serviceAccountsEnabled;
+    protected Boolean authorizationServicesEnabled;
     @Deprecated
     protected Boolean directGrantsOnly;
     protected Boolean publicClient;
@@ -239,6 +240,14 @@ public class ClientRepresentation {
         this.serviceAccountsEnabled = serviceAccountsEnabled;
     }
 
+    public Boolean getAuthorizationServicesEnabled() {
+        return authorizationServicesEnabled;
+    }
+
+    public void setAuthorizationServicesEnabled(Boolean authorizationServicesEnabled) {
+        this.authorizationServicesEnabled = authorizationServicesEnabled;
+    }
+
     @Deprecated
     public Boolean isDirectGrantsOnly() {
         return directGrantsOnly;
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 75bda3e..45887d1 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -31,6 +31,30 @@
     <name>Keycloak Dependencies Server All</name>
 	<description />
 
+    <properties>
+        <!-- Drools dependencies versions -->
+        <version.org.eclipse.sisu>0.3.0.M1</version.org.eclipse.sisu>
+        <version.org.eclipse.aether>1.0.0.v20140518</version.org.eclipse.aether>
+        <version.org.apache.ant>1.8.3</version.org.apache.ant>
+        <version.org.antlr>3.5</version.org.antlr>
+        <version.aopalliance>1.0</version.aopalliance>
+        <version.org.apache.maven>3.2.5</version.org.apache.maven>
+        <version.org.mvel>2.2.8.Final</version.org.mvel>
+        <version.org.sonatype.plexus.plexus-cipher>1.7</version.org.sonatype.plexus.plexus-cipher>
+        <version.org.codehaus.plexus.plexus-classworlds>2.5.2</version.org.codehaus.plexus.plexus-classworlds>
+        <version.org.codehaus.plexus.plexus-component-annotations>1.5.5</version.org.codehaus.plexus.plexus-component-annotations>
+        <version.org.codehaus.plexus.plexus-interpolation>1.21</version.org.codehaus.plexus.plexus-interpolation>
+        <version.org.codehaus.plexus.plexus-sec-dispatcher>1.3</version.org.codehaus.plexus.plexus-sec-dispatcher>
+        <version.org.codehaus.plexus.plexus-utils>3.0.20</version.org.codehaus.plexus.plexus-utils>
+        <version.org.apache.maven.wagon>2.6</version.org.apache.maven.wagon>
+        <version.com.thoughtworks.xstream>1.4.7</version.com.thoughtworks.xstream>
+        <version.com.google.guava>13.0.1</version.com.google.guava>
+        <version.org.eclipse.jdt.core.compiler>4.4.2</version.org.eclipse.jdt.core.compiler>
+        <version.com.lowagie>2.1.2</version.com.lowagie>
+        <version.org.sonatype.sisu>3.2.3</version.org.sonatype.sisu>
+        <version.com.google.inject.extensions.guice-servlet>3.0</version.com.google.inject.extensions.guice-servlet>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.keycloak</groupId>
@@ -85,6 +109,282 @@
                 </exclusion>
             </exclusions>
         </dependency>
+
+        <!-- Built-in Authorization Policy Providers  -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-policy-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- Built-in Authorization Drools Policy Provider-->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-policy-drools</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <!-- Drools -->
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-ci</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.sonatype.sisu</groupId>
+                    <artifactId>sisu-guice</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-internal</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-compiler</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-api</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-connector-basic</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-spi</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-impl</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-file</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-http</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-wagon</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-util</artifactId>
+            <version>${version.org.eclipse.aether}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>${version.org.apache.ant}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant-launcher</artifactId>
+            <version>${version.org.apache.ant}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr-runtime</artifactId>
+            <version>${version.org.antlr}</version>
+        </dependency>
+        <dependency>
+            <groupId>aopalliance</groupId>
+            <artifactId>aopalliance</artifactId>
+            <version>${version.aopalliance}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-aether-provider</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-artifact</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model-builder</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-repository-metadata</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-settings</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-settings-builder</artifactId>
+            <version>${version.org.apache.maven}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mvel</groupId>
+            <artifactId>mvel2</artifactId>
+            <version>${version.org.mvel}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.sisu</groupId>
+            <artifactId>org.eclipse.sisu.inject</artifactId>
+            <version>${version.org.eclipse.sisu}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.inject</groupId>
+                    <artifactId>guice</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject.extensions</groupId>
+            <artifactId>guice-servlet</artifactId>
+            <version>${version.com.google.inject.extensions.guice-servlet}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.sisu</groupId>
+            <artifactId>org.eclipse.sisu.plexus</artifactId>
+            <version>${version.org.eclipse.sisu}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.sonatype.sisu</groupId>
+                    <artifactId>sisu-guice</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-cipher</artifactId>
+            <version>${version.org.sonatype.plexus.plexus-cipher}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-classworlds</artifactId>
+            <version>${version.org.codehaus.plexus.plexus-classworlds}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-component-annotations</artifactId>
+            <version>${version.org.codehaus.plexus.plexus-component-annotations}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-interpolation</artifactId>
+            <version>${version.org.codehaus.plexus.plexus-interpolation}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-sec-dispatcher</artifactId>
+            <version>${version.org.codehaus.plexus.plexus-sec-dispatcher}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+            <version>${version.org.codehaus.plexus.plexus-utils}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.wagon</groupId>
+            <artifactId>wagon-http</artifactId>
+            <version>${version.org.apache.maven.wagon}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.wagon</groupId>
+            <artifactId>wagon-http-shared</artifactId>
+            <version>${version.org.apache.maven.wagon}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.wagon</groupId>
+            <artifactId>wagon-provider-api</artifactId>
+            <version>${version.org.apache.maven.wagon}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <version>${version.com.thoughtworks.xstream}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${version.com.google.guava}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jdt.core.compiler</groupId>
+            <artifactId>ecj</artifactId>
+            <version>${version.org.eclipse.jdt.core.compiler}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.lowagie</groupId>
+            <artifactId>itext</artifactId>
+            <version>${version.com.lowagie}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>bouncycastle</groupId>
+                    <artifactId>bcmail-jdk14</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>bouncycastle</groupId>
+                    <artifactId>bcprov-jdk14</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.sonatype.sisu</groupId>
+                    <artifactId>sisu-guice</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.sisu</groupId>
+            <artifactId>sisu-guice</artifactId>
+            <version>${version.org.sonatype.sisu}</version>
+            <classifier>no_aop</classifier>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/distribution/adapters/osgi/features/src/main/resources/features.xml b/distribution/adapters/osgi/features/src/main/resources/features.xml
index f2e825a..f3dc3e9 100755
--- a/distribution/adapters/osgi/features/src/main/resources/features.xml
+++ b/distribution/adapters/osgi/features/src/main/resources/features.xml
@@ -32,6 +32,7 @@
         <bundle dependency="true">mvn:org.jboss.logging/jboss-logging/${jboss.logging.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-common/${project.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-core/${project.version}</bundle>
+        <bundle>mvn:org.keycloak/keycloak-authz-client/${project.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-adapter-spi/${project.version}</bundle>
         <bundle>mvn:org.keycloak/keycloak-adapter-core/${project.version}</bundle>
     </feature>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml b/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
index 8da890a..ece320b 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
@@ -38,6 +38,9 @@
                 <include>org/keycloak/keycloak-wildfly-subsystem/**</include>
                 <include>org/keycloak/keycloak-adapter-subsystem/**</include>
                 <include>org/keycloak/keycloak-servlet-oauth-client/**</include>
+
+                <!-- Authorization -->
+                <include>org/keycloak/keycloak-authz-client/**</include>
             </includes>
             <excludes>
                 <exclude>**/*.war</exclude>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
index 4597205..a534b4f 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
@@ -73,6 +73,10 @@
             <maven-resource group="org.keycloak" artifact="keycloak-servlet-oauth-client"/>
         </module-def>
 
+        <!-- Authorization -->
+        <module-def name="org.keycloak.keycloak-authz-client">
+            <maven-resource group="org.keycloak" artifact="keycloak-authz-client"/>
+        </module-def>
     </target>
 
     <target name="clean-target">
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
index 4fb605e..18783ec 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
@@ -82,6 +82,12 @@
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpcore</artifactId>
         </dependency>
+
+        <!-- Authorization -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
index 8672bf4..84a08f0 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -34,6 +34,7 @@
         <module name="org.keycloak.keycloak-adapter-spi"/>
         <module name="org.keycloak.keycloak-common"/>
         <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-authz-client"/>
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-authz-client/main/module.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-authz-client/main/module.xml
new file mode 100755
index 0000000..3cd1abd
--- /dev/null
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-authz-client/main/module.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-authz-client">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="org.bouncycastle" />
+        <module name="javax.api"/>
+        <module name="javax.activation.api"/>
+        <module name="sun.jdk" optional="true" />
+        <module name="javax.ws.rs.api"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-common"/>
+        <module name="org.apache.httpcomponents"/>
+        <module name="com.fasterxml.jackson.core.jackson-core"/>
+        <module name="com.fasterxml.jackson.core.jackson-annotations"/>
+        <module name="com.fasterxml.jackson.core.jackson-databind"/>
+        <module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/demo-dist/assembly.xml b/distribution/demo-dist/assembly.xml
index 56e5818..4c58b7d 100755
--- a/distribution/demo-dist/assembly.xml
+++ b/distribution/demo-dist/assembly.xml
@@ -61,10 +61,6 @@
             </excludes>
         </fileSet>
         <fileSet>
-            <directory>${project.build.directory}/unpacked/keycloak-docs-${project.version}</directory>
-            <outputDirectory>docs</outputDirectory>
-        </fileSet>
-        <fileSet>
             <directory>${project.build.directory}/unpacked/keycloak-examples-${project.version}</directory>
             <outputDirectory>examples</outputDirectory>
         </fileSet>
diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml
index 76ec682..72e7fa4 100755
--- a/distribution/demo-dist/pom.xml
+++ b/distribution/demo-dist/pom.xml
@@ -50,11 +50,6 @@
             <artifactId>keycloak-examples-dist</artifactId>
             <type>zip</type>
         </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-docs-dist</artifactId>
-            <type>zip</type>
-        </dependency>
     </dependencies>
 
     <build>
@@ -140,23 +135,6 @@
                         </configuration>
                     </execution>
                     <execution>
-                        <id>unpack-docs</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>unpack</goal>
-                        </goals>
-                        <configuration>
-                            <artifactItems>
-                                <artifactItem>
-                                    <groupId>org.keycloak</groupId>
-                                    <artifactId>keycloak-docs-dist</artifactId>
-                                    <type>zip</type>
-                                    <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
-                                </artifactItem>
-                            </artifactItems>
-                        </configuration>
-                    </execution>
-                    <execution>
                         <id>unpack-examples</id>
                         <phase>prepare-package</phase>
                         <goals>
diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml
index 3e1e7f1..d45ad89 100755
--- a/distribution/downloads/pom.xml
+++ b/distribution/downloads/pom.xml
@@ -102,9 +102,9 @@
 
                                 <artifactItem>
                                     <groupId>org.keycloak</groupId>
-                                    <artifactId>keycloak-docs-dist</artifactId>
+                                    <artifactId>keycloak-api-docs-dist</artifactId>
                                     <type>zip</type>
-                                    <destFileName>keycloak-docs-${project.version}.zip</destFileName>
+                                    <destFileName>keycloak-api-docs-${project.version}.zip</destFileName>
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>org.keycloak</groupId>
diff --git a/distribution/examples-dist/build.xml b/distribution/examples-dist/build.xml
index 7714802..6e8b9bc 100755
--- a/distribution/examples-dist/build.xml
+++ b/distribution/examples-dist/build.xml
@@ -136,6 +136,14 @@
                 <exclude name="**/*.iml"/>
             </fileset>
         </copy>
+        <copy todir="target/examples/authz" overwrite="true">
+            <fileset dir="../../examples/authz">
+                <exclude name="**/target/**"/>
+                <exclude name="**/*.iml"/>
+                <exclude name="**/*.unconfigured"/>
+                <exclude name="**/subsystem-config.xml"/>
+            </fileset>
+        </copy>
         <copy file="../../examples/pom.xml" tofile="target/examples/pom.xml"/>
         <copy file="../../examples/README.md" tofile="target/examples/README.md"/>
         <move file="target/examples/unconfigured-demo/README.md.unconfigured" tofile="target/examples/unconfigured-demo/README.md"/>
diff --git a/distribution/feature-packs/server-feature-pack/pom.xml b/distribution/feature-packs/server-feature-pack/pom.xml
index 82f2f74..4906417 100644
--- a/distribution/feature-packs/server-feature-pack/pom.xml
+++ b/distribution/feature-packs/server-feature-pack/pom.xml
@@ -51,6 +51,13 @@
             <groupId>org.wildfly</groupId>
             <artifactId>wildfly-feature-pack</artifactId>
             <type>zip</type>
+            <!-- Need to exlcude that in order to use the right guava version for drools -->
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
 
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
index 77a3f68..89263bf 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
@@ -32,6 +32,10 @@
         "provider": "jpa"
     },
 
+    "authorizationPersister": {
+        "provider": "jpa"
+    },
+
     "timer": {
         "provider": "basic"
     },
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml
new file mode 100644
index 0000000..ac74233
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<module xmlns="urn:jboss:module:1.3" name="org.drools">
+    <resources>
+        <artifact name="${org.kie:kie-api}"/>
+        <artifact name="${org.kie:kie-ci}"/>
+        <artifact name="${org.kie:kie-internal}"/>
+        <artifact name="${org.drools:drools-core}"/>
+        <artifact name="${org.drools:drools-compiler}"/>
+        <artifact name="${org.eclipse.aether:aether-api}"/>
+        <artifact name="${org.eclipse.aether:aether-connector-basic}"/>
+        <artifact name="${org.eclipse.aether:aether-spi}"/>
+        <artifact name="${org.eclipse.aether:aether-impl}"/>
+        <artifact name="${org.eclipse.aether:aether-transport-file}"/>
+        <artifact name="${org.eclipse.aether:aether-transport-http}"/>
+        <artifact name="${org.eclipse.aether:aether-transport-wagon}"/>
+        <artifact name="${org.eclipse.aether:aether-util}"/>
+        <artifact name="${org.apache.ant:ant}"/>
+        <artifact name="${org.apache.ant:ant-launcher}"/>
+        <artifact name="${org.antlr:antlr-runtime}"/>
+        <artifact name="${aopalliance:aopalliance}"/>
+        <artifact name="${org.apache.maven:maven-aether-provider}"/>
+        <artifact name="${org.apache.maven:maven-artifact}"/>
+        <artifact name="${org.apache.maven:maven-compat}"/>
+        <artifact name="${org.apache.maven:maven-core}"/>
+        <artifact name="${org.apache.maven:maven-model}"/>
+        <artifact name="${org.apache.maven:maven-model-builder}"/>
+        <artifact name="${org.apache.maven:maven-plugin-api}"/>
+        <artifact name="${org.apache.maven:maven-repository-metadata}"/>
+        <artifact name="${org.apache.maven:maven-settings}"/>
+        <artifact name="${org.apache.maven:maven-settings-builder}"/>
+        <artifact name="${org.mvel:mvel2}"/>
+        <artifact name="${org.eclipse.sisu:org.eclipse.sisu.inject}"/>
+        <artifact name="${org.eclipse.sisu:org.eclipse.sisu.plexus}"/>
+        <artifact name="${org.sonatype.plexus:plexus-cipher}"/>
+        <artifact name="${org.codehaus.plexus:plexus-classworlds}"/>
+        <artifact name="${org.codehaus.plexus:plexus-component-annotations}"/>
+        <artifact name="${org.codehaus.plexus:plexus-interpolation}"/>
+        <artifact name="${org.sonatype.plexus:plexus-sec-dispatcher}"/>
+        <artifact name="${org.codehaus.plexus:plexus-utils}"/>
+        <artifact name="${org.apache.maven.wagon:wagon-http}"/>
+        <artifact name="${org.apache.maven.wagon:wagon-http-shared}"/>
+        <artifact name="${org.apache.maven.wagon:wagon-provider-api}"/>
+        <artifact name="${com.thoughtworks.xstream:xstream}"/>
+        <artifact name="${com.google.guava:guava}"/>
+        <artifact name="${org.eclipse.jdt.core.compiler:ecj}"/>
+        <artifact name="${org.apache.httpcomponents:httpclient}"/>
+        <artifact name="${org.apache.httpcomponents:httpcore}"/>
+        <artifact name="${com.lowagie:itext}"/>
+        <artifact name="${org.sonatype.sisu:sisu-guice::no_aop}"/>
+        <artifact name="${com.google.inject.extensions:guice-servlet}"/>
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="javax.inject.api"/>
+        <module name="javax.enterprise.api"/>
+        <module name="org.slf4j"/>
+        <module name="org.apache.commons.logging"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-common"/>
+        <module name="org.keycloak.keycloak-server-spi"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml
new file mode 100644
index 0000000..ced09b5
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-authz-policy-common">
+    <resources>
+        <artifact name="${org.keycloak:keycloak-authz-policy-common}"/>
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="javax.ws.rs.api"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-common"/>
+        <module name="org.keycloak.keycloak-server-spi"/>
+        <module name="org.keycloak.keycloak-services"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml
new file mode 100644
index 0000000..d821674
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-authz-policy-drools">
+    <resources>
+        <artifact name="${org.keycloak:keycloak-authz-policy-drools}"/>
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="javax.ws.rs.api"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-common"/>
+        <module name="org.keycloak.keycloak-server-spi"/>
+        <module name="org.keycloak.keycloak-services"/>
+        <module name="org.drools"/>
+    </dependencies>
+</module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml
index 6f19080..6ad93e9 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/web.xml
@@ -45,6 +45,7 @@
     <filter>
         <filter-name>Keycloak Session Management</filter-name>
         <filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class>
+        <async-supported>true</async-supported>
     </filter>
 
     <filter-mapping>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
index ca24001..42042e6 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
@@ -44,6 +44,10 @@
         <module name="org.keycloak.keycloak-services" export="true" services="import"/>
         <module name="org.keycloak.keycloak-wildfly-extensions" services="import"/>
 
+        <!-- Authorization -->
+        <module name="org.keycloak.keycloak-authz-policy-common" services="import"/>
+        <module name="org.keycloak.keycloak-authz-policy-drools" services="import"/>
+
         <module name="org.freemarker"/>
         <module name="javax.ws.rs.api"/>
         <module name="javax.mail.api"/>
diff --git a/distribution/pom.xml b/distribution/pom.xml
index 707733c..1a071bc 100755
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -35,7 +35,7 @@
         <module>adapters</module>
         <module>saml-adapters</module>
         <module>demo-dist</module>
-        <module>docs-dist</module>
+        <module>api-docs-dist</module>
         <module>examples-dist</module>
         <module>proxy-dist</module>
         <module>server-dist</module>
diff --git a/distribution/server-overlay/assembly.xml b/distribution/server-overlay/assembly.xml
index 532308c..325cadf 100755
--- a/distribution/server-overlay/assembly.xml
+++ b/distribution/server-overlay/assembly.xml
@@ -37,9 +37,18 @@
                 <include>org/liquibase/**</include>
                 <include>org/mongodb/**</include>
                 <include>org/twitter4j/**</include>
+                <include>org/drools/**</include>
                 <include>sun/jdk/jgss/**</include>
             </includes>
         </fileSet>
+        <!-- Authorization -->
+        <fileSet>
+            <directory>${project.build.directory}/unpacked/keycloak-${project.version}/modules/system/layers/keycloak-authz</directory>
+            <outputDirectory>modules/system/add-ons/keycloak-authz</outputDirectory>
+            <includes>
+                <include>**/**</include>
+            </includes>
+        </fileSet>
         <fileSet>
             <directory>${project.build.directory}/unpacked/keycloak-${project.version}/content</directory>
             <outputDirectory></outputDirectory>
diff --git a/examples/authz/hello-world/hello-world-authz-realm.json b/examples/authz/hello-world/hello-world-authz-realm.json
new file mode 100644
index 0000000..3ab917c
--- /dev/null
+++ b/examples/authz/hello-world/hello-world-authz-realm.json
@@ -0,0 +1,49 @@
+{
+  "realm" : "hello-world-authz",
+  "enabled" : true,
+  "privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
+  "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
+  "certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
+  "requiredCredentials" : [ "password" ],
+  "users" :
+  [
+    {
+      "username" : "alice",
+      "enabled" : true,
+      "credentials" : [ {
+        "type" : "password",
+        "value" : "alice"
+      } ],
+      "realmRoles" : ["uma_authorization"]
+    },
+    {
+      "username" : "jdoe",
+      "enabled" : true,
+      "credentials" : [ {
+        "type" : "password",
+        "value" : "jdoe"
+      } ],
+      "realmRoles" : ["uma_authorization"]
+    },
+    {
+      "username" : "service-account-hello-world-authz-service",
+      "enabled" : true,
+      "serviceAccountClientId" : "hello-world-authz-service",
+      "clientRoles": {
+        "hello-world-authz-service" : ["uma_protection"]
+      }
+    }
+  ],
+  "clients" : [
+    {
+      "clientId" : "hello-world-authz-service",
+      "secret" : "secret",
+      "authorizationServicesEnabled" : true,
+      "enabled" : true,
+      "redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ],
+      "baseUrl": "http://localhost:8080/hello-world-authz-service",
+      "adminUrl": "http://localhost:8080/hello-world-authz-service",
+      "directAccessGrantsEnabled" : true
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world/hello-world-authz-service.json b/examples/authz/hello-world/hello-world-authz-service.json
new file mode 100644
index 0000000..ea56e62
--- /dev/null
+++ b/examples/authz/hello-world/hello-world-authz-service.json
@@ -0,0 +1,30 @@
+{
+  "resources": [
+    {
+      "name": "Default Resource",
+      "uri": "/*",
+      "type": "urn:hello-world-authz-service:resources:default"
+    }
+  ],
+  "policies": [
+    {
+      "name": "Only From Realm Policy",
+      "description": "A policy that grants access only for users within this realm",
+      "type": "js",
+      "config": {
+        "applyPolicies": "[]",
+        "code": "var context = $evaluation.getContext();\n\n// using attributes from the evaluation context to obtain the realm\nvar contextAttributes = context.getAttributes();\nvar realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n\n// using attributes from the identity to obtain the issuer\nvar identity = context.getIdentity();\nvar identityAttributes = identity.getAttributes();\nvar issuer = identityAttributes.getValue('iss').asString(0);\n\n// only users from the realm have access granted \nif (issuer.endsWith(realmName)) {\n    $evaluation.grant();\n}"
+      }
+    },
+    {
+      "name": "Default Permission",
+      "description": "A permission that applies to the default resource type",
+      "type": "resource",
+      "config": {
+        "defaultResourceType": "urn:hello-world-authz-service:resources:default",
+        "default": "true",
+        "applyPolicies": "[\"Only From Realm Policy\"]"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world/pom.xml b/examples/authz/hello-world/pom.xml
new file mode 100755
index 0000000..e30d381
--- /dev/null
+++ b/examples/authz/hello-world/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-example-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-hello-world</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Keycloak Authz: Hello World Example</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java
new file mode 100644
index 0000000..2ab8788
--- /dev/null
+++ b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java
@@ -0,0 +1,162 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authz.helloworld;
+
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.EntitlementRequest;
+import org.keycloak.authorization.client.representation.EntitlementResponse;
+import org.keycloak.authorization.client.representation.PermissionRequest;
+import org.keycloak.authorization.client.representation.RegistrationResponse;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
+import org.keycloak.authorization.client.resource.ProtectedResource;
+import org.keycloak.representations.authorization.Permission;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationClientExample {
+
+    public static void main(String[] args) {
+        obtainEntitlementsForResource();
+        obtainAllEntitlements();
+        createResource();
+        introspectRequestingPartyToken();
+    }
+
+    private static void introspectRequestingPartyToken() {
+        // create a new instance based on the configuration define at keycloak-authz.json
+        AuthzClient authzClient = AuthzClient.create();
+
+        // query the server for a resource with a given name
+        Set<String> resourceId = authzClient.protection()
+                .resource()
+                .findByFilter("name=Default Resource");
+
+        // obtian a Entitlement API Token in order to get access to the Entitlement API.
+        // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
+        String eat = getEntitlementAPIToken(authzClient);
+
+        // create an entitlement request
+        EntitlementRequest request = new EntitlementRequest();
+        PermissionRequest permission = new PermissionRequest();
+
+        permission.setResourceSetId(resourceId.iterator().next());
+
+        request.addPermission(permission);
+
+        // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
+        EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
+        String rpt = response.getRpt();
+
+        TokenIntrospectionResponse requestingPartyToken = authzClient.protection().introspectRequestingPartyToken(rpt);
+
+        System.out.println("Token status is: " + requestingPartyToken.getActive());
+        System.out.println("Permissions granted by the server: ");
+
+        for (Permission granted : requestingPartyToken.getPermissions()) {
+            System.out.println(granted);
+        }
+
+    }
+
+    private static void createResource() {
+        // create a new instance based on the configuration define at keycloak-authz.json
+        AuthzClient authzClient = AuthzClient.create();
+
+        // create a new resource representation with the information we want
+        ResourceRepresentation newResource = new ResourceRepresentation();
+
+        newResource.setName("New Resource");
+        newResource.setType("urn:hello-world-authz:resources:example");
+
+        newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view"));
+
+        ProtectedResource resourceClient = authzClient.protection().resource();
+        Set<String> existingResource = resourceClient.findByFilter("name=" + newResource.getName());
+
+        if (!existingResource.isEmpty()) {
+            resourceClient.delete(existingResource.iterator().next());
+        }
+
+        // create the resource on the server
+        RegistrationResponse response = resourceClient.create(newResource);
+        String resourceId = response.getId();
+
+        // query the resource using its newly generated id
+        ResourceRepresentation resource = resourceClient.findById(resourceId).getResourceDescription();
+
+        System.out.println(resource);
+    }
+
+    private static void obtainEntitlementsForResource() {
+        // create a new instance based on the configuration define at keycloak-authz.json
+        AuthzClient authzClient = AuthzClient.create();
+
+        // obtian a Entitlement API Token in order to get access to the Entitlement API.
+        // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
+        String eat = getEntitlementAPIToken(authzClient);
+
+        // create an entitlement request
+        EntitlementRequest request = new EntitlementRequest();
+        PermissionRequest permission = new PermissionRequest();
+
+        permission.setResourceSetName("Default Resource");
+
+        request.addPermission(permission);
+
+        // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
+        EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
+        String rpt = response.getRpt();
+
+        System.out.println("You got a RPT: " + rpt);
+
+        // now you can use the RPT to access protected resources on the resource server
+    }
+
+    private static void obtainAllEntitlements() {
+        // create a new instance based on the configuration define at keycloak-authz.json
+        AuthzClient authzClient = AuthzClient.create();
+
+        // obtian a Entitlement API Token in order to get access to the Entitlement API.
+        // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
+        String eat = getEntitlementAPIToken(authzClient);
+
+        // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
+        EntitlementResponse response = authzClient.entitlement(eat).getAll("hello-world-authz-service");
+        String rpt = response.getRpt();
+
+        System.out.println("You got a RPT: " + rpt);
+
+        // now you can use the RPT to access protected resources on the resource server
+    }
+
+    /**
+     * Obtain an Entitlement API Token or EAT from the server. Usually, EATs are going to be obtained by clients using a
+     * authorization_code grant type. Here we are using resource owner credentials for demonstration purposes.
+     *
+     * @param authzClient the authorization client instance
+     * @return a string representing a EAT
+     */
+    private static String getEntitlementAPIToken(AuthzClient authzClient) {
+        return authzClient.obtainAccessToken("alice", "alice").getToken();
+    }
+}
diff --git a/examples/authz/hello-world/src/main/resources/keycloak.json b/examples/authz/hello-world/src/main/resources/keycloak.json
new file mode 100644
index 0000000..b337389
--- /dev/null
+++ b/examples/authz/hello-world/src/main/resources/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm": "hello-world-authz",
+  "auth-server-url" : "http://localhost:8080/auth",
+  "resource" : "hello-world-authz-service",
+  "credentials": {
+    "secret": "secret"
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world-authz-service/hello-world-authz-realm.json b/examples/authz/hello-world-authz-service/hello-world-authz-realm.json
new file mode 100644
index 0000000..3ab917c
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/hello-world-authz-realm.json
@@ -0,0 +1,49 @@
+{
+  "realm" : "hello-world-authz",
+  "enabled" : true,
+  "privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
+  "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
+  "certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
+  "requiredCredentials" : [ "password" ],
+  "users" :
+  [
+    {
+      "username" : "alice",
+      "enabled" : true,
+      "credentials" : [ {
+        "type" : "password",
+        "value" : "alice"
+      } ],
+      "realmRoles" : ["uma_authorization"]
+    },
+    {
+      "username" : "jdoe",
+      "enabled" : true,
+      "credentials" : [ {
+        "type" : "password",
+        "value" : "jdoe"
+      } ],
+      "realmRoles" : ["uma_authorization"]
+    },
+    {
+      "username" : "service-account-hello-world-authz-service",
+      "enabled" : true,
+      "serviceAccountClientId" : "hello-world-authz-service",
+      "clientRoles": {
+        "hello-world-authz-service" : ["uma_protection"]
+      }
+    }
+  ],
+  "clients" : [
+    {
+      "clientId" : "hello-world-authz-service",
+      "secret" : "secret",
+      "authorizationServicesEnabled" : true,
+      "enabled" : true,
+      "redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ],
+      "baseUrl": "http://localhost:8080/hello-world-authz-service",
+      "adminUrl": "http://localhost:8080/hello-world-authz-service",
+      "directAccessGrantsEnabled" : true
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world-authz-service/hello-world-authz-service.json b/examples/authz/hello-world-authz-service/hello-world-authz-service.json
new file mode 100644
index 0000000..ea56e62
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/hello-world-authz-service.json
@@ -0,0 +1,30 @@
+{
+  "resources": [
+    {
+      "name": "Default Resource",
+      "uri": "/*",
+      "type": "urn:hello-world-authz-service:resources:default"
+    }
+  ],
+  "policies": [
+    {
+      "name": "Only From Realm Policy",
+      "description": "A policy that grants access only for users within this realm",
+      "type": "js",
+      "config": {
+        "applyPolicies": "[]",
+        "code": "var context = $evaluation.getContext();\n\n// using attributes from the evaluation context to obtain the realm\nvar contextAttributes = context.getAttributes();\nvar realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n\n// using attributes from the identity to obtain the issuer\nvar identity = context.getIdentity();\nvar identityAttributes = identity.getAttributes();\nvar issuer = identityAttributes.getValue('iss').asString(0);\n\n// only users from the realm have access granted \nif (issuer.endsWith(realmName)) {\n    $evaluation.grant();\n}"
+      }
+    },
+    {
+      "name": "Default Permission",
+      "description": "A permission that applies to the default resource type",
+      "type": "resource",
+      "config": {
+        "defaultResourceType": "urn:hello-world-authz-service:resources:default",
+        "default": "true",
+        "applyPolicies": "[\"Only From Realm Policy\"]"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world-authz-service/pom.xml b/examples/authz/hello-world-authz-service/pom.xml
new file mode 100755
index 0000000..5b9b646
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-example-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>hello-world-authz-service</artifactId>
+    <packaging>war</packaging>
+
+    <name>Keycloak Authz: Hello World Example</name>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/authz/hello-world-authz-service/README.md b/examples/authz/hello-world-authz-service/README.md
new file mode 100644
index 0000000..a0cc40f
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/README.md
@@ -0,0 +1,47 @@
+# About the Example Application
+
+This is a simple application to get you started with Keycloak Authorization Services.
+
+It provides a single page application which is protected by a policy enforcer that decides whether an user can access
+that page or not based on the permissions obtained from a Keycloak Server.
+
+## Create the Example Realm and a Resource Server
+
+Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console.
+
+Now, create a new realm based on the following configuration file:
+
+    examples/authz/hello-world-authz-service/hello-world-authz-realm.json
+    
+That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm 
+into Keycloak, check the Keycloak's reference documentation.
+
+After importing that file, you'll have a new realm called ``hello-world-authz``. 
+
+Now, let's import another configuration using the Administration Console in order to configure the client application ``hello-world-authz-service`` as a resource server with all resources, scopes, permissions and policies.
+
+Click on ``Clients`` on the left side menu. Click on the ``hello-world-authz-service`` on the client listing page. This will
+open the ``Client Details`` page. Once there, click on the `Authorization` tab. 
+
+Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at:
+
+    examples/authz/hello-world-authz-service/hello-world-authz-service.json
+    
+Now click ``Upload`` and the resource server will be updated accordingly.
+
+## Deploy and Run the Example Application
+
+To deploy the example application, follow these steps:
+
+    cd examples/authz/hello-world-authz-service
+    mvn clean package wildfly:deploy
+    
+Now, try to access the client application using the following URL:
+
+    http://localhost:8080/hello-world-authz-service
+
+If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials:
+
+* username: jdoe / password: jdoe
+* username: alice / password: alice
+
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/error.jsp b/examples/authz/hello-world-authz-service/src/main/webapp/error.jsp
new file mode 100644
index 0000000..00d25b3
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/error.jsp
@@ -0,0 +1,30 @@
+<%--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  --%>
+
+<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+
+<html>
+<body>
+<h2><a href="<%= KeycloakUriBuilder.fromUri("/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+            .queryParam("redirect_uri", "http://localhost:8080/hello-world-authz-service").build("hello-world-authz").toString()%>">Logout</a></h2>
+
+<h3>Access Denied !</h3>
+</body>
+</html>
+
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp b/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp
new file mode 100644
index 0000000..697f491
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp
@@ -0,0 +1,50 @@
+<%--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  --%>
+<%@page import="org.keycloak.AuthorizationContext" %>
+<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+<%@ page import="org.keycloak.KeycloakSecurityContext" %>
+<%@ page import="org.keycloak.representations.authorization.Permission" %>
+
+<%
+    KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
+    AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext();
+%>
+<html>
+<body>
+<h2>Welcome !</h2>
+<h2><a href="<%= KeycloakUriBuilder.fromUri("/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+            .queryParam("redirect_uri", "http://localhost:8080/hello-world-authz-service").build("hello-world-authz").toString()%>">Logout</a></h2>
+
+<h3>Your permissions are:</h3>
+
+<ul>
+    <%
+        for (Permission permission : authzContext.getPermissions()) {
+    %>
+    <li>
+        <p>Resource: <%= permission.getResourceSetName() %></p>
+        <p>ID: <%= permission.getResourceSetId() %></p>
+    </li>
+    <%
+        }
+    %>
+</ul>
+</body>
+</html>
+
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..f303fe1
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,13 @@
+{
+  "realm": "hello-world-authz",
+  "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
+  "auth-server-url": "http://localhost:8080/auth",
+  "ssl-required": "external",
+  "resource": "hello-world-authz-service",
+  "credentials": {
+    "secret": "secret"
+  },
+  "policy-enforcer": {
+    "on-deny-redirect-to" : "/hello-world-authz-service/error.jsp"
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/web.xml b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1ca06be
--- /dev/null
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+		 version="3.0">
+
+	<module-name>hello-world-authz-service</module-name>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>uma_authorization</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<login-config>
+		<auth-method>KEYCLOAK</auth-method>
+		<realm-name>hello-world-authz</realm-name>
+	</login-config>
+
+	<security-role>
+		<role-name>uma_authorization</role-name>
+	</security-role>
+</web-app>
diff --git a/examples/authz/photoz/photoz-authz-policy/pom.xml b/examples/authz/photoz/photoz-authz-policy/pom.xml
new file mode 100755
index 0000000..f236a15
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-photoz-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>photoz-authz-policy</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Keycloak Authz: Examples - Photoz Authz Rule-based Policy</name>
+
+    <description>
+        Photoz Authz Rule-based Policies using JBoss Drools
+    </description>
+
+</project>
diff --git a/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl
new file mode 100644
index 0000000..deb1c84
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl
@@ -0,0 +1,14 @@
+package com.photoz.authz.policy.admin
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+
+rule "Authorize Admin Resources"
+    dialect "mvel"
+        when
+           $evaluation : Evaluation(
+               $identity : context.identity,
+               $identity.hasRole("admin")
+           )
+        then
+           $evaluation.grant();
+end
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl
new file mode 100644
index 0000000..9378b94
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl
@@ -0,0 +1,15 @@
+package com.photoz.authz.policy.admin
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+
+rule "Authorize Resource Owner"
+    dialect "mvel"
+    when
+       $evaluation : Evaluation(
+           $identity: context.identity,
+           $permission: permission,
+           $permission.resource != null && $permission.resource.owner.equals($identity.id)
+       )
+    then
+        $evaluation.grant();
+end
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl
new file mode 100644
index 0000000..9b1677e
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl
@@ -0,0 +1,14 @@
+package com.photoz.authz.policy.admin
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+
+rule "Authorize View User Album"
+    dialect "mvel"
+    when
+        $evaluation : Evaluation(
+            $identity : context.identity,
+            $identity.hasRole("user")
+       )
+    then
+       $evaluation.grant();
+end
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl
new file mode 100644
index 0000000..8a6a772
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl
@@ -0,0 +1,15 @@
+package com.photoz.authz.policy.admin
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+
+rule "Authorize Using Context Information"
+    dialect "mvel"
+    when
+       $evaluation : Evaluation(
+           $attributes: context.attributes,
+           $attributes.containsValue("kc.identity.authc.method", "otp"),
+           $attributes.containsValue("someAttribute", "you_can_access")
+       )
+    then
+        $evaluation.grant();
+end
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-authz-policy/src/main/resources/META-INF/kmodule.xml b/examples/authz/photoz/photoz-authz-policy/src/main/resources/META-INF/kmodule.xml
new file mode 100644
index 0000000..84bacd5
--- /dev/null
+++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/META-INF/kmodule.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://jboss.org/kie/6.0.0/kmodule">
+
+    <kbase name="PhotozAuthzAdminPolicy" packages="com.photoz.authz.policy.admin">
+        <ksession name="MainAdminSession" default="true"/>
+    </kbase>
+
+    <kbase name="PhotozAuthzUserPolicy" packages="com.photoz.authz.policy.user">
+        <ksession name="MainUserSession"  default="true"/>
+    </kbase>
+
+    <kbase name="PhotozAuthzOwnerPolicy" packages="com.photoz.authz.policy.resource.owner">
+        <ksession name="MainOwnerSession" default="true"/>
+    </kbase>
+
+    <kbase name="PhotozAuthzContextualPolicy" packages="com.photoz.authz.policy.contextual">
+        <ksession name="MainContextualSession" default="true"/>
+    </kbase>
+
+</kmodule>
diff --git a/examples/authz/photoz/photoz-html5-client/pom.xml b/examples/authz/photoz/photoz-html5-client/pom.xml
new file mode 100755
index 0000000..31f6540
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-photoz-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>photoz-html5-client</artifactId>
+    <packaging>war</packaging>
+
+    <name>Keycloak Authz: Photoz HTML5 Client</name>
+    <description>Photoz HTML5 Client</description>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html
new file mode 100755
index 0000000..f9375a2
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8">
+    <title>Photoz HTML5 Client</title>
+
+    <!-- Load AngularJS -->
+    <script src="lib/angular/angular.min.js"></script>
+    <script src="lib/angular/angular-resource.min.js"></script>
+    <script src="lib/angular/angular-route.min.js"></script>
+    <script src="lib/jwt-decode.min.js"></script>
+
+    <script src="http://localhost:8080/auth/js/keycloak.js"></script>
+    <script src="http://localhost:8080/auth/js/keycloak-authz.js"></script>
+    <script src="js/security/keycloak-authorization.js" type="text/javascript"></script>
+    <script src="js/identity.js" type="text/javascript"></script>
+    <script src="js/app.js" type="text/javascript"></script>
+</head>
+
+<body data-ng-controller="TokenCtrl">
+
+<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a>
+
+<div id="content-area" class="col-md-9" role="main">
+    <div id="content" ng-view/>
+</div>
+
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="output"></pre>
+
+</body>
+</html>
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js
new file mode 100755
index 0000000..2990675
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js
@@ -0,0 +1,168 @@
+var module = angular.module('photoz', ['ngRoute', 'ngResource']);
+
+var resourceServerId = 'photoz-restful-api';
+var apiUrl = window.location.origin + '/' + resourceServerId;
+
+angular.element(document).ready(function ($http) {
+    var keycloak = new Keycloak('keycloak.json');
+    keycloak.init({onLoad: 'login-required'}).success(function () {
+        console.log('User is now authenticated.');
+
+        module.factory('Identity', function () {
+            return new Identity(keycloak);
+        });
+
+        angular.bootstrap(document, ["photoz"]);
+    }).error(function () {
+        window.location.reload();
+    });
+});
+
+module.config(function ($httpProvider, $routeProvider) {
+    $httpProvider.interceptors.push('authInterceptor');
+    $routeProvider.when('/', {
+        templateUrl: 'partials/home.html',
+        controller: 'GlobalCtrl'
+    }).when('/album/create', {
+        templateUrl: 'partials/album/create.html',
+        controller: 'AlbumCtrl',
+    }).when('/album/:id', {
+        templateUrl: 'partials/album/detail.html',
+        controller: 'AlbumCtrl',
+    }).when('/admin/album', {
+        templateUrl: 'partials/admin/albums.html',
+        controller: 'AdminAlbumCtrl',
+    }).when('/profile', {
+        templateUrl: 'partials/profile.html',
+        controller: 'ProfileCtrl',
+    });
+});
+
+module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Album, Identity) {
+    Album.query(function (albums) {
+        $scope.albums = albums;
+    });
+
+    $scope.Identity = Identity;
+
+    $scope.deleteAlbum = function (album) {
+        new Album(album).$delete({id: album.id}, function () {
+            $route.reload();
+        });
+    }
+});
+
+module.controller('TokenCtrl', function ($scope, Identity) {
+    $scope.showRpt = function () {
+        document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.authorization.rpt), null, '  ');
+    }
+
+    $scope.showAccessToken = function () {
+        document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.authc.token), null, '  ');
+    }
+
+    $scope.requestEntitlements = function () {
+        Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {});
+    }
+});
+
+module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) {
+    $scope.album = {};
+    if ($routeParams.id) {
+        $scope.album = Album.get({id: $routeParams.id});
+    }
+    $scope.create = function () {
+        var newAlbum = new Album($scope.album);
+        newAlbum.$save({}, function (data) {
+            $location.path('/');
+        });
+    };
+});
+
+module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $location, Profile) {
+    $scope.profile = Profile.get();
+});
+
+module.controller('AdminAlbumCtrl', function ($scope, $http, $route, AdminAlbum, Album) {
+    $scope.albums = {};
+    $http.get(apiUrl + '/admin/album').success(function (data) {
+        $scope.albums = data;
+    });
+    $scope.deleteAlbum = function (album) {
+        var newAlbum = new Album(album);
+        newAlbum.$delete({id: album.id}, function () {
+            $route.reload();
+        });
+    }
+});
+
+module.factory('Album', ['$resource', function ($resource) {
+    return $resource(apiUrl + '/album/:id');
+}]);
+
+module.factory('Profile', ['$resource', function ($resource) {
+    return $resource(apiUrl + '/profile');
+}]);
+
+module.factory('AdminAlbum', ['$resource', function ($resource) {
+    return $resource(apiUrl + '/admin/album/:id');
+}]);
+
+module.factory('authInterceptor', function ($q, $injector, $timeout, Identity) {
+    return {
+        request: function (request) {
+            document.getElementById("output").innerHTML = '';
+            if (Identity.authorization && Identity.authorization.rpt && request.url.indexOf('/authorize') == -1) {
+                retries = 0;
+                request.headers.Authorization = 'Bearer ' + Identity.authorization.rpt;
+            } else {
+                request.headers.Authorization = 'Bearer ' + Identity.authc.token;
+            }
+            return request;
+        },
+        responseError: function (rejection) {
+            var status = rejection.status;
+
+            if (status == 403 || status == 401) {
+                var retry = (!rejection.config.retry ||  rejection.config.retry < 1);
+
+                if (!retry) {
+                    document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.';
+                    return $q.reject(rejection);
+                }
+
+                if (rejection.config.url.indexOf('/authorize') == -1 && retry) {
+                    var deferred = $q.defer();
+
+                    // here is the authorization logic, which tries to obtain an authorization token from the server in case the resource server
+                    // returns a 403 or 401.
+                    Identity.authorization.authorize(rejection.headers('WWW-Authenticate')).then(function (rpt) {
+                        deferred.resolve(rejection);
+                    }, function () {
+                        document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.';
+                    }, function () {
+                        document.getElementById("output").innerHTML = 'Unexpected error from server.';
+                    });
+
+                    var promise = deferred.promise;
+
+                    return promise.then(function (res) {
+                        if (!res.config.retry) {
+                            res.config.retry = 1;
+                        } else {
+                            res.config.retry++;
+                        }
+
+                        var $http = $injector.get("$http");
+
+                        return $http(res.config).then(function (response) {
+                            return response;
+                        });
+                    });
+                }
+            }
+
+            return $q.reject(rejection);
+        }
+    };
+});
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js
new file mode 100644
index 0000000..9a018e4
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+/**
+ * Creates an Identity object holding the information obtained from the access token issued by Keycloak, after a successful authentication,
+ * and a few utility methods to manage it.
+ */
+(function (window, undefined) {
+    var Identity = function (keycloak) {
+        this.loggedIn = true;
+
+        this.claims = {};
+        this.claims.name = keycloak.idTokenParsed.name;
+
+        this.authc = {};
+        this.authc.token = keycloak.token;
+
+        this.logout = function () {
+            keycloak.logout();
+        };
+
+        this.hasRole = function (name) {
+            if (keycloak && keycloak.hasRealmRole(name)) {
+                return true;
+            }
+            return false;
+        };
+
+        this.isAdmin = function () {
+            return this.hasRole("admin");
+        };
+
+        this.authorization = new KeycloakAuthorization(keycloak);
+    }
+
+    if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+        module.exports = Identity;
+    } else {
+        window.Identity = Identity;
+
+        if ( typeof define === "function" && define.amd ) {
+            define( "identity", [], function () { return Identity; } );
+        }
+    }
+})( window );
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
new file mode 100644
index 0000000..4b4d193
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
@@ -0,0 +1,12 @@
+{
+  "realm": "photoz",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8080/auth",
+  "ssl-required" : "external",
+  "resource" : "photoz-html5-client",
+  "public-client" : true,
+  "use-resource-role-mappings": "false",
+  "scope" : {
+    "realm" : [ "user" ]
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js
new file mode 100644
index 0000000..569a9a2
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js
@@ -0,0 +1,214 @@
+/*
+ AngularJS v1.3.0-beta.5
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(O,U,s){'use strict';function v(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.0-beta.5/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function db(b){if(null==b||Da(b))return!1;
+var a=b.length;return 1===b.nodeType&&a?!0:t(b)||M(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(P(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(db(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Tb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function ad(b,
+a,c){for(var d=Tb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Ub(b){return function(a,c){b(c,a)}}function eb(){for(var b=ka.length,a;b;){b--;a=ka[b].charCodeAt(0);if(57==a)return ka[b]="A",ka.join("");if(90==a)ka[b]="0";else return ka[b]=String.fromCharCode(a+1),ka.join("")}ka.unshift("0");return ka.join("")}function Vb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function A(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});Vb(b,a);return b}function Y(b){return parseInt(b,
+10)}function Wb(b,a){return A(new (A(function(){},{prototype:b})),a)}function C(){}function Ea(b){return b}function aa(b){return function(){return b}}function D(b){return"undefined"===typeof b}function B(b){return"undefined"!==typeof b}function X(b){return null!=b&&"object"===typeof b}function t(b){return"string"===typeof b}function Ab(b){return"number"===typeof b}function ra(b){return"[object Date]"===ya.call(b)}function M(b){return"[object Array]"===ya.call(b)}function P(b){return"function"===typeof b}
+function fb(b){return"[object RegExp]"===ya.call(b)}function Da(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function bd(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function cd(b,a,c){var d=[];q(b,function(b,g,f){d.push(a.call(c,b,g,f))});return d}function gb(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Fa(b,a){var c=gb(b,a);0<=c&&b.splice(c,1);return a}function ba(b,a){if(Da(b)||b&&b.$evalAsync&&b.$watch)throw Oa("cpws");
+if(a){if(b===a)throw Oa("cpi");if(M(b))for(var c=a.length=0;c<b.length;c++)a.push(ba(b[c]));else{c=a.$$hashKey;q(a,function(b,c){delete a[c]});for(var d in b)a[d]=ba(b[d]);Vb(a,c)}}else(a=b)&&(M(b)?a=ba(b,[]):ra(b)?a=new Date(b.getTime()):fb(b)?a=RegExp(b.source):X(b)&&(a=ba(b,{})));return a}function Xb(b,a){a=a||{};for(var c in b)!b.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(a[c]=b[c]);return a}function za(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;
+var c=typeof b,d;if(c==typeof a&&"object"==c)if(M(b)){if(!M(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!za(b[d],a[d]))return!1;return!0}}else{if(ra(b))return ra(a)&&b.getTime()==a.getTime();if(fb(b)&&fb(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||Da(b)||Da(a)||M(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!P(b[d])){if(!za(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!P(a[d]))return!1;
+return!0}return!1}function Yb(){return U.securityPolicy&&U.securityPolicy.isActive||U.querySelector&&!(!U.querySelector("[ng-csp]")&&!U.querySelector("[data-ng-csp]"))}function hb(b,a){var c=2<arguments.length?sa.call(arguments,2):[];return!P(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(sa.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function dd(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=
+s:Da(a)?c="$WINDOW":a&&U===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function ta(b,a){return"undefined"===typeof b?s:JSON.stringify(b,dd,a?"  ":null)}function Zb(b){return t(b)?JSON.parse(b):b}function Pa(b){"function"===typeof b?b=!0:b&&0!==b.length?(b=I(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function ha(b){b=y(b).clone();try{b.empty()}catch(a){}var c=y("<div>").append(b).html();try{return 3===b[0].nodeType?I(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,
+function(a,b){return"<"+I(b)})}catch(d){return I(c)}}function $b(b){try{return decodeURIComponent(b)}catch(a){}}function ac(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=$b(c[0]),B(d)&&(b=B(c[1])?$b(c[1]):!0,a[d]?M(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function bc(b){var a=[];q(b,function(b,d){M(b)?q(b,function(b){a.push(Aa(d,!0)+(!0===b?"":"="+Aa(b,!0)))}):a.push(Aa(d,!0)+(!0===b?"":"="+Aa(b,!0)))});return a.length?a.join("&"):""}function Bb(b){return Aa(b,
+!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Aa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function ed(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,f=["ng:app","ng-app","x-ng-app","data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(f,function(a){f[a]=!0;c(U.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+
+a+"\\:"),c),q(b.querySelectorAll("["+a+"]"),c))});q(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&f[b.name]&&(e=a,g=b.value)})}});e&&a(e,g?[g]:[])}function cc(b,a){var c=function(){b=y(b);if(b.injector()){var c=b[0]===U?"document":ha(b);throw Oa("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=dc(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",
+function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(O&&!d.test(O.name))return c();O.name=O.name.replace(d,"");Qa.resumeBootstrap=function(b){q(b,function(b){a.push(b)});c()}}function ib(b,a){a=a||"_";return b.replace(fd,function(b,d){return(d?a:"")+b.toLowerCase()})}function Cb(b,a,c){if(!b)throw Oa("areq",a||"?",c||"required");return b}function Ra(b,a,c){c&&M(b)&&(b=b[b.length-1]);Cb(P(b),a,"not a function, got "+(b&&"object"==typeof b?
+b.constructor.name||"Object":typeof b));return b}function Ba(b,a){if("hasOwnProperty"===b)throw Oa("badname",a);}function ec(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,g=a.length,f=0;f<g;f++)d=a[f],b&&(b=(e=b)[d]);return!c&&P(b)?hb(e,b):b}function Db(b){var a=b[0];b=b[b.length-1];if(a===b)return y(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return y(c)}function gd(b){var a=v("$injector"),c=v("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||v;return b.module||
+(b.module=function(){var b={};return function(e,g,f){if("hasOwnProperty"===e)throw c("badname","module");g&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);return n}}if(!g)throw a("nomod",e);var c=[],d=[],l=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:g,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide",
+"constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:l,run:function(a){d.push(a);return this}};f&&l(f);return n}())}}())}function hd(b){A(b,{bootstrap:cc,copy:ba,extend:A,equals:za,element:y,forEach:q,injector:dc,noop:C,bind:hb,toJson:ta,fromJson:Zb,identity:Ea,isUndefined:D,isDefined:B,isString:t,isFunction:P,isObject:X,isNumber:Ab,isElement:bd,isArray:M,
+version:id,isDate:ra,lowercase:I,uppercase:Ga,callbacks:{counter:0},$$minErr:v,$$csp:Yb});Sa=gd(O);try{Sa("ngLocale")}catch(a){Sa("ngLocale",[]).provider("$locale",jd)}Sa("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:kd});a.provider("$compile",fc).directive({a:ld,input:gc,textarea:gc,form:md,script:nd,select:od,style:pd,option:qd,ngBind:rd,ngBindHtml:sd,ngBindTemplate:td,ngClass:ud,ngClassEven:vd,ngClassOdd:wd,ngCloak:xd,ngController:yd,ngForm:zd,ngHide:Ad,ngIf:Bd,ngInclude:Cd,
+ngInit:Dd,ngNonBindable:Ed,ngPluralize:Fd,ngRepeat:Gd,ngShow:Hd,ngStyle:Id,ngSwitch:Jd,ngSwitchWhen:Kd,ngSwitchDefault:Ld,ngOptions:Md,ngTransclude:Nd,ngModel:Od,ngList:Pd,ngChange:Qd,required:hc,ngRequired:hc,ngValue:Rd}).directive({ngInclude:Sd}).directive(Eb).directive(ic);a.provider({$anchorScroll:Td,$animate:Ud,$browser:Vd,$cacheFactory:Wd,$controller:Xd,$document:Yd,$exceptionHandler:Zd,$filter:jc,$interpolate:$d,$interval:ae,$http:be,$httpBackend:ce,$location:de,$log:ee,$parse:fe,$rootScope:ge,
+$q:he,$sce:ie,$sceDelegate:je,$sniffer:ke,$templateCache:le,$timeout:me,$window:ne,$$rAF:oe,$$asyncCallback:pe})}])}function Ta(b){return b.replace(qe,function(a,b,d,e){return e?d.toUpperCase():d}).replace(re,"Moz$1")}function Fb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:[this],m=a,k,l,n,p,r,u;if(!d||null!=b)for(;e.length;)for(k=e.shift(),l=0,n=k.length;l<n;l++)for(p=y(k[l]),m?p.triggerHandler("$destroy"):m=!m,r=0,p=(u=p.children()).length;r<p;r++)e.push(Ha(u[r]));return g.apply(this,arguments)}
+var g=Ha.fn[b],g=g.$original||g;e.$original=g;Ha.fn[b]=e}function se(b,a){var c,d,e=a.createDocumentFragment(),g=[];if(Gb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(te.exec(b)||["",""])[1].toLowerCase();d=ea[d]||ea._default;c.innerHTML=d[1]+b.replace(ue,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;g=g.concat(sa.call(c.childNodes,void 0));c=e.firstChild;c.textContent=""}else g.push(a.createTextNode(b));e.textContent="";e.innerHTML="";q(g,function(a){e.appendChild(a)});return e}function N(b){if(b instanceof
+N)return b;t(b)&&(b=ca(b));if(!(this instanceof N)){if(t(b)&&"<"!=b.charAt(0))throw Hb("nosel");return new N(b)}if(t(b)){var a;a=U;var c;b=(c=ve.exec(b))?[a.createElement(c[1])]:(c=se(b,a))?c.childNodes:[]}kc(this,b)}function Ib(b){return b.cloneNode(!0)}function Ia(b){lc(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ia(b[a])}function mc(b,a,c,d){if(B(d))throw Hb("offargs");var e=la(b,"events");la(b,"handle")&&(D(a)?q(e,function(a,c){Ua(b,c,a);delete e[c]}):q(a.split(" "),function(a){D(c)?(Ua(b,
+a,e[a]),delete e[a]):Fa(e[a]||[],c)}))}function lc(b,a){var c=b[jb],d=Va[c];d&&(a?delete Va[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),mc(b)),delete Va[c],b[jb]=s))}function la(b,a,c){var d=b[jb],d=Va[d||-1];if(B(c))d||(b[jb]=d=++we,d=Va[d]={}),d[a]=c;else return d&&d[a]}function nc(b,a,c){var d=la(b,"data"),e=B(c),g=!e&&B(a),f=g&&!X(a);d||f||la(b,"data",d={});if(e)d[a]=c;else if(g){if(f)return d&&d[a];A(d,a)}else return d}function Jb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||
+"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function kb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",ca((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+ca(a)+" "," ")))})}function lb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(a.split(" "),function(a){a=ca(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",ca(c))}}function kc(b,a){if(a){a=a.nodeName||!B(a.length)||
+Da(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function oc(b,a){return mb(b,"$"+(a||"ngController")+"Controller")}function mb(b,a,c){b=y(b);9==b[0].nodeType&&(b=b.find("html"));for(a=M(a)?a:[a];b.length;){for(var d=b[0],e=0,g=a.length;e<g;e++)if((c=b.data(a[e]))!==s)return c;b=y(d.parentNode||11===d.nodeType&&d.host)}}function pc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ia(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function qc(b,a){var c=nb[a.toLowerCase()];return c&&rc[b.nodeName]&&
+c}function xe(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||U);if(D(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var f=Xb(a[e||c.type]||[]);q(f,function(a){a.call(b,c)});8>=T?(c.preventDefault=
+null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ja(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=b.$$hashKey():c===s&&(c=b.$$hashKey=eb()):c=b;return a+":"+c}function Wa(b){q(b,this.put,this)}function sc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(ye,""),c=c.match(ze),q(c[1].split(Ae),function(b){b.replace(Be,function(b,
+c,d){a.push(d)})})),b.$inject=a):M(b)?(c=b.length-1,Ra(b[c],"fn"),a=b.slice(0,c)):Ra(b,"fn",!0);return a}function dc(b){function a(a){return function(b,c){if(X(b))q(b,Ub(a));else return a(b,c)}}function c(a,b){Ba(a,"service");if(P(b)||M(b))b=n.instantiate(b);if(!b.$get)throw Xa("pget",a);return l[a+h]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,g,h;q(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(t(a))for(c=Sa(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,
+g=0,h=d.length;g<h;g++){var f=d[g],m=n.get(f[0]);m[f[1]].apply(m,f[2])}else P(a)?b.push(n.invoke(a)):M(a)?b.push(n.invoke(a)):Ra(a,"module")}catch(l){throw M(a)&&(a=a[a.length-1]),l.message&&(l.stack&&-1==l.stack.indexOf(l.message))&&(l=l.message+"\n"+l.stack),Xa("modulerr",a,l.stack||l.message||l);}}});return b}function g(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===f)throw Xa("cdep",m.join(" <- "));return a[d]}try{return m.unshift(d),a[d]=f,a[d]=b(d)}catch(e){throw a[d]===f&&delete a[d],
+e;}finally{m.shift()}}function d(a,b,e){var g=[],h=sc(a),f,m,k;m=0;for(f=h.length;m<f;m++){k=h[m];if("string"!==typeof k)throw Xa("itkn",k);g.push(e&&e.hasOwnProperty(k)?e[k]:c(k))}a.$inject||(a=a[f]);return a.apply(b,g)}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(M(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return X(e)||P(e)?e:c},get:c,annotate:sc,has:function(b){return l.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}var f={},h="Provider",m=[],k=new Wa,l={$provide:{provider:a(c),
+factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,aa(b))}),constant:a(function(a,b){Ba(a,"constant");l[a]=b;p[a]=b}),decorator:function(a,b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=r.invoke(d,c);return r.invoke(b,null,{$delegate:a})}}}},n=l.$injector=g(l,function(){throw Xa("unpr",m.join(" <- "));}),p={},r=p.$injector=g(p,function(a){a=n.get(a+h);return r.invoke(a.$get,a)});q(e(b),function(a){r.invoke(a||
+C)});return r}function Td(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==I(a.nodeName)||(b=a)});return b}function g(){var b=c.hash(),d;b?(d=f.getElementById(b))?d.scrollIntoView():(d=e(f.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var f=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(g)});return g}]}function pe(){this.$get=
+["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function Ce(b,a,c,d){function e(a){try{a.apply(null,sa.call(arguments,1))}finally{if(u--,0===u)for(;z.length;)try{z.pop()()}catch(b){c.error(b)}}}function g(a,b){(function S(){q(K,function(a){a()});w=b(S,a)})()}function f(){x=null;H!=h.url()&&(H=h.url(),q(ma,function(a){a(h.url())}))}var h=this,m=a[0],k=b.location,l=b.history,n=b.setTimeout,p=b.clearTimeout,r={};h.isMock=!1;var u=0,z=[];h.$$completeOutstandingRequest=
+e;h.$$incOutstandingRequestCount=function(){u++};h.notifyWhenNoOutstandingRequests=function(a){q(K,function(a){a()});0===u?a():z.push(a)};var K=[],w;h.addPollFn=function(a){D(w)&&g(100,n);K.push(a);return a};var H=k.href,G=a.find("base"),x=null;h.url=function(a,c){k!==b.location&&(k=b.location);l!==b.history&&(l=b.history);if(a){if(H!=a)return H=a,d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),G.attr("href",G.attr("href"))):(x=a,c?k.replace(a):k.href=a),h}else return x||k.href.replace(/%27/g,
+"'")};var ma=[],L=!1;h.onUrlChange=function(a){if(!L){if(d.history)y(b).on("popstate",f);if(d.hashchange)y(b).on("hashchange",f);else h.addPollFn(f);L=!0}ma.push(a);return a};h.baseHref=function(){var a=G.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var Q={},da="",E=h.baseHref();h.cookies=function(a,b){var d,e,g,h;if(a)b===s?m.cookie=escape(a)+"=;path="+E+";expires=Thu, 01 Jan 1970 00:00:00 GMT":t(b)&&(d=(m.cookie=escape(a)+"="+escape(b)+";path="+E).length+1,4096<d&&c.warn("Cookie '"+
+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==da)for(da=m.cookie,d=da.split("; "),Q={},g=0;g<d.length;g++)e=d[g],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,h)),Q[a]===s&&(Q[a]=unescape(e.substring(h+1))));return Q}};h.defer=function(a,b){var c;u++;c=n(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};h.defer.cancel=function(a){return r[a]?(delete r[a],p(a),e(C),!0):!1}}function Vd(){this.$get=["$window","$log","$sniffer","$document",
+function(b,a,c,d){return new Ce(b,d,a,c)}]}function Wd(){this.$get=function(){function b(b,d){function e(a){a!=n&&(p?p==a&&(p=a.n):p=a,g(a.n,a.p),g(a,n),n=a,n.n=null)}function g(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw v("$cacheFactory")("iid",b);var f=0,h=A({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,l={},n=null,p=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=l[a]||(l[a]={key:a});e(c)}if(!D(b))return a in m||f++,m[a]=b,f>k&&this.remove(p.key),b},get:function(a){if(k<
+Number.MAX_VALUE){var b=l[a];if(!b)return;e(b)}return m[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;b==n&&(n=b.p);b==p&&(p=b.n);g(b.n,b.p);delete l[a]}delete m[a];f--},removeAll:function(){m={};f=0;l={};n=p=null},destroy:function(){l=h=m=null;delete a[b]},info:function(){return A({},h,{size:f})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function le(){this.$get=["$cacheFactory",function(b){return b("templates")}]}
+function fc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,g=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,f=/^(on[a-z]+|formaction)$/;this.directive=function m(a,e){Ba(a,"directive");t(a)?(Cb(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];q(c[a],function(c,g){try{var f=b.invoke(c);P(f)?f={compile:aa(f)}:!f.compile&&f.link&&(f.compile=aa(f.link));f.priority=f.priority||0;f.index=g;f.name=f.name||a;f.require=f.require||
+f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(m){d(m)}});return e}])),c[a].push(e)):q(a,Ub(m));return this};this.aHrefSanitizationWhitelist=function(b){return B(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope",
+"$document","$sce","$animate","$$sanitizeUri",function(a,b,l,n,p,r,u,z,K,w,H,G){function x(a,b,c,d,e){a instanceof y||(a=y(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});var g=L(a,b,a,c,d,e);ma(a,"ng-scope");return function(b,c,d){Cb(b,"scope");var e=c?Ka.clone.call(a):a;q(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var f=e.length;d<f;d++){var m=e[d].nodeType;1!==m&&9!==m||e.eq(d).data("$scope",b)}c&&c(e,b);g&&g(b,e,e);return e}}
+function ma(a,b){try{a.addClass(b)}catch(c){}}function L(a,b,c,d,e,g){function f(a,c,d,e){var g,k,l,n,r,p,u;g=c.length;var J=Array(g);for(r=0;r<g;r++)J[r]=c[r];u=r=0;for(p=m.length;r<p;u++)k=J[u],c=m[r++],g=m[r++],l=y(k),c?(c.scope?(n=a.$new(),l.data("$scope",n)):n=a,(l=c.transclude)||!e&&b?c(g,n,k,d,Q(a,l||b)):c(g,n,k,d,e)):g&&g(a,k.childNodes,s,e)}for(var m=[],k,l,n,r,p=0;p<a.length;p++)k=new Kb,l=da(a[p],[],k,0===p?d:s,e),(g=l.length?ia(l,a[p],k,b,c,null,[],[],g):null)&&g.scope&&ma(y(a[p]),"ng-scope"),
+k=g&&g.terminal||!(n=a[p].childNodes)||!n.length?null:L(n,g?g.transclude:b),m.push(g,k),r=r||g||k,g=null;return r?f:null}function Q(a,b){return function(c,d,e){var g=!1;c||(c=a.$new(),g=c.$$transcluded=!0);d=b(c,d,e);if(g)d.on("$destroy",hb(c,c.$destroy));return d}}function da(a,b,c,d,f){var m=c.$attr,k;switch(a.nodeType){case 1:S(b,na(La(a).toLowerCase()),"E",d,f);var l,n,r;k=a.attributes;for(var p=0,u=k&&k.length;p<u;p++){var z=!1,K=!1;l=k[p];if(!T||8<=T||l.specified){n=l.name;r=na(n);W.test(r)&&
+(n=ib(r.substr(6),"-"));var H=r.replace(/(Start|End)$/,"");r===H+"Start"&&(z=n,K=n.substr(0,n.length-5)+"end",n=n.substr(0,n.length-6));r=na(n.toLowerCase());m[r]=n;c[r]=l=ca(l.value);qc(a,r)&&(c[r]=!0);N(a,b,l,r);S(b,r,"A",d,f,z,K)}}a=a.className;if(t(a)&&""!==a)for(;k=g.exec(a);)r=na(k[2]),S(b,r,"C",d,f)&&(c[r]=ca(k[3])),a=a.substr(k.index+k[0].length);break;case 3:v(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))r=na(k[1]),S(b,r,"M",d,f)&&(c[r]=ca(k[2]))}catch(x){}}b.sort(D);return b}
+function E(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ja("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return y(d)}function R(a,b,c){return function(d,e,g,f,k){e=E(e[0],b,c);return a(d,e,g,f,k)}}function ia(a,c,d,e,g,f,m,n,p){function z(a,b,c,d){if(a){c&&(a=R(a,c,d));a.require=F.require;if(Q===F||F.$$isolateScope)a=uc(a,{isolateScope:!0});m.push(a)}if(b){c&&(b=R(b,c,d));b.require=F.require;
+if(Q===F||F.$$isolateScope)b=uc(b,{isolateScope:!0});n.push(b)}}function K(a,b,c){var d,e="data",g=!1;if(t(a)){for(;"^"==(d=a.charAt(0))||"?"==d;)a=a.substr(1),"^"==d&&(e="inheritedData"),g=g||"?"==d;d=null;c&&"data"===e&&(d=c[a]);d=d||b[e]("$"+a+"Controller");if(!d&&!g)throw ja("ctreq",a,v);}else M(a)&&(d=[],q(a,function(a){d.push(K(a,b,c))}));return d}function H(a,e,g,f,p){function z(a,b){var c;2>arguments.length&&(b=a,a=s);A&&(c=da);return p(a,b,c)}var J,x,w,G,R,E,da={},ob;J=c===g?d:Xb(d,new Kb(y(g),
+d.$attr));x=J.$$element;if(Q){var S=/^\s*([@=&])(\??)\s*(\w*)\s*$/;f=y(g);E=e.$new(!0);ia&&ia===Q.$$originalDirective?f.data("$isolateScope",E):f.data("$isolateScopeNoTemplate",E);ma(f,"ng-isolate-scope");q(Q.scope,function(a,c){var d=a.match(S)||[],g=d[3]||c,f="?"==d[2],d=d[1],m,l,n,p;E.$$isolateBindings[c]=d+g;switch(d){case "@":J.$observe(g,function(a){E[c]=a});J.$$observers[g].$$scope=e;J[g]&&(E[c]=b(J[g])(e));break;case "=":if(f&&!J[g])break;l=r(J[g]);p=l.literal?za:function(a,b){return a===
+b};n=l.assign||function(){m=E[c]=l(e);throw ja("nonassign",J[g],Q.name);};m=E[c]=l(e);E.$watch(function(){var a=l(e);p(a,E[c])||(p(a,m)?n(e,a=E[c]):E[c]=a);return m=a},null,l.literal);break;case "&":l=r(J[g]);E[c]=function(a){return l(e,a)};break;default:throw ja("iscp",Q.name,c,a);}})}ob=p&&z;L&&q(L,function(a){var b={$scope:a===Q||a.$$isolateScope?E:e,$element:x,$attrs:J,$transclude:ob},c;R=a.controller;"@"==R&&(R=J[a.name]);c=u(R,b);da[a.name]=c;A||x.data("$"+a.name+"Controller",c);a.controllerAs&&
+(b.$scope[a.controllerAs]=c)});f=0;for(w=m.length;f<w;f++)try{G=m[f],G(G.isolateScope?E:e,x,J,G.require&&K(G.require,x,da),ob)}catch(F){l(F,ha(x))}f=e;Q&&(Q.template||null===Q.templateUrl)&&(f=E);a&&a(f,g.childNodes,s,p);for(f=n.length-1;0<=f;f--)try{G=n[f],G(G.isolateScope?E:e,x,J,G.require&&K(G.require,x,da),ob)}catch(B){l(B,ha(x))}}p=p||{};for(var w=-Number.MAX_VALUE,G,L=p.controllerDirectives,Q=p.newIsolateScopeDirective,ia=p.templateDirective,S=p.nonTlbTranscludeDirective,D=!1,A=p.hasElementTranscludeDirective,
+Z=d.$$element=y(c),F,v,V,Ya=e,O,N=0,oa=a.length;N<oa;N++){F=a[N];var T=F.$$start,W=F.$$end;T&&(Z=E(c,T,W));V=s;if(w>F.priority)break;if(V=F.scope)G=G||F,F.templateUrl||(I("new/isolated scope",Q,F,Z),X(V)&&(Q=F));v=F.name;!F.templateUrl&&F.controller&&(V=F.controller,L=L||{},I("'"+v+"' controller",L[v],F,Z),L[v]=F);if(V=F.transclude)D=!0,F.$$tlb||(I("transclusion",S,F,Z),S=F),"element"==V?(A=!0,w=F.priority,V=E(c,T,W),Z=d.$$element=y(U.createComment(" "+v+": "+d[v]+" ")),c=Z[0],pb(g,y(sa.call(V,0)),
+c),Ya=x(V,e,w,f&&f.name,{nonTlbTranscludeDirective:S})):(V=y(Ib(c)).contents(),Z.empty(),Ya=x(V,e));if(F.template)if(I("template",ia,F,Z),ia=F,V=P(F.template)?F.template(Z,d):F.template,V=Y(V),F.replace){f=F;V=Gb.test(V)?y(V):[];c=V[0];if(1!=V.length||1!==c.nodeType)throw ja("tplrt",v,"");pb(g,Z,c);oa={$attr:{}};V=da(c,[],oa);var $=a.splice(N+1,a.length-(N+1));Q&&tc(V);a=a.concat(V).concat($);B(d,oa);oa=a.length}else Z.html(V);if(F.templateUrl)I("template",ia,F,Z),ia=F,F.replace&&(f=F),H=C(a.splice(N,
+a.length-N),Z,d,g,Ya,m,n,{controllerDirectives:L,newIsolateScopeDirective:Q,templateDirective:ia,nonTlbTranscludeDirective:S}),oa=a.length;else if(F.compile)try{O=F.compile(Z,d,Ya),P(O)?z(null,O,T,W):O&&z(O.pre,O.post,T,W)}catch(aa){l(aa,ha(Z))}F.terminal&&(H.terminal=!0,w=Math.max(w,F.priority))}H.scope=G&&!0===G.scope;H.transclude=D&&Ya;p.hasElementTranscludeDirective=A;return H}function tc(a){for(var b=0,c=a.length;b<c;b++)a[b]=Wb(a[b],{$$isolateScope:!0})}function S(b,e,g,f,k,n,r){if(e===k)return null;
+k=null;if(c.hasOwnProperty(e)){var p;e=a.get(e+d);for(var u=0,z=e.length;u<z;u++)try{p=e[u],(f===s||f>p.priority)&&-1!=p.restrict.indexOf(g)&&(n&&(p=Wb(p,{$$start:n,$$end:r})),b.push(p),k=p)}catch(K){l(K)}}return k}function B(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,g){"class"==g?(ma(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==g?(e.attr("style",e.attr("style")+";"+b),a.style=
+(a.style?a.style+";":"")+b):"$"==g.charAt(0)||a.hasOwnProperty(g)||(a[g]=b,d[g]=c[g])})}function C(a,b,c,d,e,g,f,k){var m=[],l,r,u=b[0],z=a.shift(),K=A({},z,{templateUrl:null,transclude:null,replace:null,$$originalDirective:z}),x=P(z.templateUrl)?z.templateUrl(b,c):z.templateUrl;b.empty();n.get(w.getTrustedResourceUrl(x),{cache:p}).success(function(n){var p,H;n=Y(n);if(z.replace){n=Gb.test(n)?y(n):[];p=n[0];if(1!=n.length||1!==p.nodeType)throw ja("tplrt",z.name,x);n={$attr:{}};pb(d,b,p);var w=da(p,
+[],n);X(z.scope)&&tc(w);a=w.concat(a);B(c,n)}else p=u,b.html(n);a.unshift(K);l=ia(a,p,c,e,b,z,g,f,k);q(d,function(a,c){a==p&&(d[c]=b[0])});for(r=L(b[0].childNodes,e);m.length;){n=m.shift();H=m.shift();var G=m.shift(),R=m.shift(),w=b[0];if(H!==u){var E=H.className;k.hasElementTranscludeDirective&&z.replace||(w=Ib(p));pb(G,y(H),w);ma(y(w),E)}H=l.transclude?Q(n,l.transclude):R;l(r,n,w,d,H)}m=null}).error(function(a,b,c,d){throw ja("tpload",d.url);});return function(a,b,c,d,e){m?(m.push(b),m.push(c),
+m.push(d),m.push(e)):l(r,b,c,d,e)}}function D(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function I(a,b,c,d){if(b)throw ja("multidir",b.name,c.name,a,ha(d));}function v(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:aa(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);ma(c.data("$binding",e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function O(a,b){if("srcdoc"==b)return w.HTML;var c=La(a);if("xlinkHref"==b||
+"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return w.RESOURCE_URL}function N(a,c,d,e){var g=b(d,!0);if(g){if("multiple"===e&&"SELECT"===La(a))throw ja("selmulti",ha(a));c.push({priority:100,compile:function(){return{pre:function(c,d,m){d=m.$$observers||(m.$$observers={});if(f.test(e))throw ja("nodomevents");if(g=b(m[e],!0,O(a,e)))m[e]=g(c),(d[e]||(d[e]=[])).$$inter=!0,(m.$$observers&&m.$$observers[e].$$scope||c).$watch(g,function(a,b){"class"===e&&a!=b?m.$updateClass(a,b):m.$set(e,a)})}}}})}}
+function pb(a,b,c){var d=b[0],e=b.length,g=d.parentNode,f,m;if(a)for(f=0,m=a.length;f<m;f++)if(a[f]==d){a[f++]=c;m=f+e-1;for(var k=a.length;f<k;f++,m++)m<k?a[f]=a[m]:delete a[f];a.length-=e-1;break}g&&g.replaceChild(c,d);a=U.createDocumentFragment();a.appendChild(d);c[y.expando]=d[y.expando];d=1;for(e=b.length;d<e;d++)g=b[d],y(g).remove(),a.appendChild(g),delete b[d];b[0]=c;b.length=1}function uc(a,b){return A(function(){return a.apply(null,arguments)},a,b)}var Kb=function(a,b){this.$$element=a;this.$attr=
+b||{}};Kb.prototype={$normalize:na,$addClass:function(a){a&&0<a.length&&H.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&H.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=vc(a,b),d=vc(b,a);0===c.length?H.removeClass(this.$$element,d):0===d.length?H.addClass(this.$$element,c):H.setClass(this.$$element,c,d)},$set:function(a,b,c,d){var e=qc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=ib(a,
+"-"));e=La(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=G(b,"src"===a);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){l(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);z.$evalAsync(function(){e.$$inter||b(c[a])});return function(){Fa(e,b)}}};var Z=b.startSymbol(),oa=b.endSymbol(),Y="{{"==Z||"}}"==oa?Ea:function(a){return a.replace(/\{\{/g,
+Z).replace(/}}/g,oa)},W=/^ngAttr[A-Z]/;return x}]}function na(b){return Ta(b.replace(De,""))}function vc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),g=0;a:for(;g<d.length;g++){for(var f=d[g],h=0;h<e.length;h++)if(f==e[h])continue a;c+=(0<c.length?" ":"")+f}return c}function Xd(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){Ba(a,"controller");X(a)?A(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,g){var f,h,m;t(e)&&(f=e.match(a),h=f[1],m=f[3],e=
+b.hasOwnProperty(h)?b[h]:ec(g.$scope,h,!0)||ec(d,h,!0),Ra(e,h,!0));f=c.instantiate(e,g);if(m){if(!g||"object"!=typeof g.$scope)throw v("$controller")("noscp",h||e.name,m);g.$scope[m]=f}return f}}]}function Yd(){this.$get=["$window",function(b){return y(b.document)}]}function Zd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function wc(b){var a={},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=I(ca(b.substr(0,e)));d=ca(b.substr(e+1));c&&(a[c]=
+a[c]?a[c]+(", "+d):d)});return a}function xc(b){var a=X(b)?b:s;return function(c){a||(a=wc(b));return c?a[I(c)]||null:a}}function yc(b,a,c){if(P(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function be(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){t(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=Zb(d)));return d}],transformRequest:[function(a){return X(a)&&"[object File]"!==ya.call(a)&&
+"[object Blob]"!==ya.call(a)?ta(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ba(d),put:ba(d),patch:ba(d)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},g=this.interceptors=[],f=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,p){function r(a){function c(a){var b=A({},a,{data:yc(a.data,a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?b:n.reject(b)}var d={method:"get",
+transformRequest:e.transformRequest,transformResponse:e.transformResponse},g=function(a){function b(a){var c;q(a,function(b,d){P(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=A({},a.headers),g,f,c=A({},c.common,c[I(a.method)]);b(c);b(d);a:for(g in c){a=I(g);for(f in d)if(I(f)===a)continue a;d[g]=c[g]}return d}(a);A(d,a);d.headers=g;d.method=Ga(d.method);(a=Lb(d.url)?b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:s)&&(g[d.xsrfHeaderName||e.xsrfHeaderName]=a);var f=[function(a){g=a.headers;
+var b=yc(a.data,xc(g),a.transformRequest);D(a.data)&&q(g,function(a,b){"content-type"===I(b)&&delete g[b]});D(a.withCredentials)&&!D(e.withCredentials)&&(a.withCredentials=e.withCredentials);return u(a,b,g).then(c,c)},s],h=n.when(d);for(q(w,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),h=h.then(a,k)}h.success=function(a){h.then(function(b){a(b.data,b.status,b.headers,
+d)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,d)});return h};return h}function u(b,c,g){function f(a,b,c,e){w&&(200<=a&&300>a?w.put(s,[a,b,wc(c),e]):w.remove(s));m(b,a,c,e);d.$$phase||d.$apply()}function m(a,c,d,e){c=Math.max(c,0);(200<=c&&300>c?p.resolve:p.reject)({data:a,status:c,headers:xc(d),config:b,statusText:e})}function k(){var a=gb(r.pendingRequests,b);-1!==a&&r.pendingRequests.splice(a,1)}var p=n.defer(),u=p.promise,w,q,s=z(b.url,b.params);r.pendingRequests.push(b);
+u.then(k,k);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(w=X(b.cache)?b.cache:X(e.cache)?e.cache:K);if(w)if(q=w.get(s),B(q)){if(q.then)return q.then(k,k),q;M(q)?m(q[1],q[0],ba(q[2]),q[3]):m(q,200,{},"OK")}else w.put(s,u);D(q)&&a(b.method,s,c,f,g,b.timeout,b.withCredentials,b.responseType);return u}function z(a,b){if(!b)return a;var c=[];ad(b,function(a,b){null===a||D(a)||(M(a)||(a=[a]),q(a,function(a){X(a)&&(a=ta(a));c.push(Aa(b)+"="+Aa(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+
+c.join("&"));return a}var K=c("$http"),w=[];q(g,function(a){w.unshift(t(a)?p.get(a):p.invoke(a))});q(f,function(a,b){var c=t(a)?p.get(a):p.invoke(a);w.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});r.pendingRequests=[];(function(a){q(arguments,function(a){r[a]=function(b,c){return r(A(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){r[a]=function(b,c,d){return r(A(d||{},{method:a,url:b,data:c}))}})})("post",
+"put");r.defaults=e;return r}]}function Ee(b){if(8>=T&&(!b.match(/^(get|post|head|put|delete|options)$/i)||!O.XMLHttpRequest))return new O.ActiveXObject("Microsoft.XMLHTTP");if(O.XMLHttpRequest)return new O.XMLHttpRequest;throw v("$httpBackend")("noxhr");}function ce(){this.$get=["$browser","$window","$document",function(b,a,c){return Fe(b,Ee,b.defer,a.angular.callbacks,c[0])}]}function Fe(b,a,c,d,e){function g(a,b,c){var g=e.createElement("script"),f=null;g.type="text/javascript";g.src=a;g.async=
+!0;f=function(a){Ua(g,"load",f);Ua(g,"error",f);e.body.removeChild(g);g=null;var h=-1,u="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),u=a.type,h="error"===a.type?404:200);c&&c(h,u)};qb(g,"load",f);qb(g,"error",f);e.body.appendChild(g);return f}var f=-1;return function(e,m,k,l,n,p,r,u){function z(){w=f;G&&G();x&&x.abort()}function K(a,d,e,g,f){L&&c.cancel(L);G=x=null;0===d&&(d=e?200:"file"==ua(m).protocol?404:0);a(1223===d?204:d,e,g,f||"");b.$$completeOutstandingRequest(C)}var w;b.$$incOutstandingRequestCount();
+m=m||b.url();if("jsonp"==I(e)){var H="_"+(d.counter++).toString(36);d[H]=function(a){d[H].data=a;d[H].called=!0};var G=g(m.replace("JSON_CALLBACK","angular.callbacks."+H),H,function(a,b){K(l,a,d[H].data,"",b);d[H]=C})}else{var x=a(e);x.open(e,m,!0);q(n,function(a,b){B(a)&&x.setRequestHeader(b,a)});x.onreadystatechange=function(){if(x&&4==x.readyState){var a=null,b=null;w!==f&&(a=x.getAllResponseHeaders(),b="response"in x?x.response:x.responseText);K(l,w||x.status,b,a,x.statusText||"")}};r&&(x.withCredentials=
+!0);if(u)try{x.responseType=u}catch(s){if("json"!==u)throw s;}x.send(k||null)}if(0<p)var L=c(z,p);else p&&p.then&&p.then(z)}}function $d(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function g(g,k,l){for(var n,p,r=0,u=[],z=g.length,K=!1,w=[];r<z;)-1!=(n=g.indexOf(b,r))&&-1!=(p=g.indexOf(a,n+f))?(r!=n&&u.push(g.substring(r,n)),u.push(r=c(K=g.substring(n+f,p))),
+r.exp=K,r=p+h,K=!0):(r!=z&&u.push(g.substring(r)),r=z);(z=u.length)||(u.push(""),z=1);if(l&&1<u.length)throw zc("noconcat",g);if(!k||K)return w.length=z,r=function(a){try{for(var b=0,c=z,f;b<c;b++)"function"==typeof(f=u[b])&&(f=f(a),f=l?e.getTrusted(l,f):e.valueOf(f),null===f||D(f)?f="":"string"!=typeof f&&(f=ta(f))),w[b]=f;return w.join("")}catch(h){a=zc("interr",g,h.toString()),d(a)}},r.exp=g,r.parts=u,r}var f=b.length,h=a.length;g.startSymbol=function(){return b};g.endSymbol=function(){return a};
+return g}]}function ae(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,f,h,m){var k=a.setInterval,l=a.clearInterval,n=c.defer(),p=n.promise,r=0,u=B(m)&&!m;h=B(h)?h:0;p.then(null,null,d);p.$$intervalId=k(function(){n.notify(r++);0<h&&r>=h&&(n.resolve(r),l(p.$$intervalId),delete e[p.$$intervalId]);u||b.$apply()},f);e[p.$$intervalId]=n;return p}var e={};d.cancel=function(a){return a&&a.$$intervalId in e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],
+!0):!1};return d}]}function jd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
+DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Ac(b){b=b.split("/");for(var a=b.length;a--;)b[a]=Bb(b[a]);return b.join("/")}function Bc(b,a,c){b=ua(b,c);a.$$protocol=
+b.protocol;a.$$host=b.hostname;a.$$port=Y(b.port)||Ge[b.protocol]||null}function Cc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=ua(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=ac(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function pa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Za(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Mb(b){return b.substr(0,
+Za(b).lastIndexOf("/")+1)}function Dc(b,a){this.$$html5=!0;a=a||"";var c=Mb(b);Bc(b,this,b);this.$$parse=function(a){var e=pa(c,a);if(!t(e))throw Nb("ipthprfx",a,c);Cc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=bc(this.$$search),b=this.$$hash?"#"+Bb(this.$$hash):"";this.$$url=Ac(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=pa(b,d))!==s)return d=e,(e=pa(a,e))!==s?c+(pa("/",e)||e):b+d;if((e=pa(c,
+d))!==s)return c+e;if(c==d+"/")return c}}function Ob(b,a){var c=Mb(b);Bc(b,this,b);this.$$parse=function(d){var e=pa(b,d)||pa(c,d),e="#"==e.charAt(0)?pa(a,e):this.$$html5?e:"";if(!t(e))throw Nb("ihshprfx",d,a);Cc(e,this,b);d=this.$$path;var g=/^\/?.*?:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));g.exec(e)||(d=(e=g.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=bc(this.$$search),e=this.$$hash?"#"+Bb(this.$$hash):"";this.$$url=Ac(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=
+b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Za(b)==Za(a))return a}}function Ec(b,a){this.$$html5=!0;Ob.apply(this,arguments);var c=Mb(b);this.$$rewrite=function(d){var e;if(b==Za(d))return d;if(e=pa(c,d))return b+a+e;if(c===d+"/")return c}}function rb(b){return function(){return this[b]}}function Fc(b,a){return function(c){if(D(c))return this[b];this[b]=a(c);this.$$compose();return this}}function de(){var b="",a=!1;this.hashPrefix=function(a){return B(a)?(b=a,this):b};this.html5Mode=
+function(b){return B(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function f(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,m=d.baseHref(),k=d.url();a?(m=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(m||"/"),e=e.history?Dc:Ec):(m=Za(k),e=Ob);h=new e(m,"#"+b);h.$$parse(h.$$rewrite(k));g.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=y(a.target);"a"!==I(b[0].nodeName);)if(b[0]===g[0]||!(b=b.parent())[0])return;
+var e=b.prop("href");X(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=ua(e.animVal).href);var f=h.$$rewrite(e);e&&(!b.attr("target")&&f&&!a.isDefaultPrevented())&&(a.preventDefault(),f!=d.url()&&(h.$$parse(f),c.$apply(),O.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=k&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):f(b)}),c.$$phase||
+c.$digest())});var l=0;c.$watch(function(){var a=d.url(),b=h.$$replace;l&&a==h.absUrl()||(l++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),f(a))}));h.$$replace=!1;return l});return h}]}function ee(){var b=!0,a=this;this.debugEnabled=function(a){return B(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:
+a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||C;a=!1;try{a=!!e.apply}catch(m){}return a?function(){var a=[];q(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function fa(b,a){if("constructor"===b)throw Ca("isecfld",a);return b}function $a(b,
+a){if(b){if(b.constructor===b)throw Ca("isecfn",a);if(b.document&&b.location&&b.alert&&b.setInterval)throw Ca("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw Ca("isecdom",a);}return b}function sb(b,a,c,d,e){e=e||{};a=a.split(".");for(var g,f=0;1<a.length;f++){g=fa(a.shift(),d);var h=b[g];h||(h={},b[g]=h);b=h;b.then&&e.unwrapPromises&&(va(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}g=fa(a.shift(),d);return b[g]=c}function Gc(b,
+a,c,d,e,g,f){fa(b,g);fa(a,g);fa(c,g);fa(d,g);fa(e,g);return f.unwrapPromises?function(f,m){var k=m&&m.hasOwnProperty(b)?m:f,l;if(null==k)return k;(k=k[b])&&k.then&&(va(g),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!a)return k;if(null==k)return s;(k=k[a])&&k.then&&(va(g),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!c)return k;if(null==k)return s;(k=k[c])&&k.then&&(va(g),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!d)return k;if(null==
+k)return s;(k=k[d])&&k.then&&(va(g),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!e)return k;if(null==k)return s;(k=k[e])&&k.then&&(va(g),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);return k}:function(g,f){var k=f&&f.hasOwnProperty(b)?f:g;if(null==k)return k;k=k[b];if(!a)return k;if(null==k)return s;k=k[a];if(!c)return k;if(null==k)return s;k=k[c];if(!d)return k;if(null==k)return s;k=k[d];return e?null==k?s:k=k[e]:k}}function He(b,a){fa(b,a);return function(a,
+d){return null==a?s:(d&&d.hasOwnProperty(b)?d:a)[b]}}function Ie(b,a,c){fa(b,c);fa(a,c);return function(c,e){if(null==c)return s;c=(e&&e.hasOwnProperty(b)?e:c)[b];return null==c?s:c[a]}}function Hc(b,a,c){if(Pb.hasOwnProperty(b))return Pb[b];var d=b.split("."),e=d.length,g;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||2!==e)if(a.csp)g=6>e?Gc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,g){var f=0,h;do h=Gc(d[f++],d[f++],d[f++],d[f++],d[f++],c,a)(b,g),g=s,b=h;while(f<e);return h};else{var f="var p;\n";
+q(d,function(b,d){fa(b,c);f+="if(s == null) return undefined;\ns="+(d?"s":'((k&&k.hasOwnProperty("'+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises?'if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});var f=f+"return s;",h=new Function("s","k","pw",f);h.toString=aa(f);g=a.unwrapPromises?function(a,b){return h(a,b,va)}:h}else g=Ie(d[0],d[1],c);else g=He(d[0],c);"hasOwnProperty"!==
+b&&(Pb[b]=g);return g}function fe(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=function(b){return B(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return B(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(c,d,e){a.csp=d.csp;va=function(b){a.logPromiseWarnings&&!Ic.hasOwnProperty(b)&&(Ic[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};
+return function(d){var e;switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];e=new Qb(a);e=(new ab(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return C}}}]}function he(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Je(function(a){b.$evalAsync(a)},a)}]}function Je(b,a){function c(a){return a}function d(a){return f(a)}var e=function(){var f=[],k,l;return l={resolve:function(a){if(f){var c=f;f=s;k=g(a);c.length&&b(function(){for(var a,
+b=0,d=c.length;b<d;b++)a=c[b],k.then(a[0],a[1],a[2])})}},reject:function(a){l.resolve(h(a))},notify:function(a){if(f){var c=f;f.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,g,h){var l=e(),z=function(d){try{l.resolve((P(b)?b:c)(d))}catch(e){l.reject(e),a(e)}},K=function(b){try{l.resolve((P(g)?g:d)(b))}catch(c){l.reject(c),a(c)}},w=function(b){try{l.notify((P(h)?h:c)(b))}catch(d){a(d)}};f?f.push([z,K,w]):k.then(z,K,w);return l.promise},"catch":function(a){return this.then(null,
+a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,g){var f=null;try{f=(a||c)()}catch(h){return b(h,!1)}return f&&P(f.then)?f.then(function(){return b(e,g)},function(a){return b(a,!1)}):b(e,g)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},g=function(a){return a&&P(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},f=function(a){var b=e();b.reject(a);return b.promise},h=function(c){return{then:function(g,
+f){var h=e();b(function(){try{h.resolve((P(f)?f:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:f,when:function(h,k,l,n){var p=e(),r,u=function(b){try{return(P(k)?k:c)(b)}catch(d){return a(d),f(d)}},z=function(b){try{return(P(l)?l:d)(b)}catch(c){return a(c),f(c)}},K=function(b){try{return(P(n)?n:c)(b)}catch(d){a(d)}};b(function(){g(h).then(function(a){r||(r=!0,p.resolve(g(a).then(u,z,K)))},function(a){r||(r=!0,p.resolve(z(a)))},function(a){r||p.notify(K(a))})});return p.promise},
+all:function(a){var b=e(),c=0,d=M(a)?[]:{};q(a,function(a,e){c++;g(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function oe(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,g=e?
+function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};g.supported=e;return g}]}function ge(){var b=10,a=v("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,g,f){function h(){this.$id=eb();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;
+this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings={}}function m(b){if(p.$$phase)throw a("inprog",p.$$phase);p.$$phase=b}function k(a,b){var c=g(a);Ra(c,b);return c}function l(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function n(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=
+this.$$postDigestQueue):(a=function(){},a.prototype=this,a=new a,a.$id=eb());a["this"]=a;a.$$listeners={};a.$$listenerCount={};a.$parent=this;a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,d){var e=k(a,"watch"),g=this.$$watchers,f={fn:b,last:n,get:e,exp:a,eq:!!d};c=null;if(!P(b)){var h=k(b||C,"listener");f.fn=function(a,
+b,c){h(c)}}if("string"==typeof a&&e.constant){var m=f.fn;f.fn=function(a,b,c){m.call(this,a,b,c);Fa(g,f)}}g||(g=this.$$watchers=[]);g.unshift(f);return function(){Fa(g,f);c=null}},$watchCollection:function(a,b){var c=this,d,e,f,h=1<b.length,k=0,m=g(a),l=[],n={},p=!0,q=0;return this.$watch(function(){d=m(c);var a,b;if(X(d))if(db(d))for(e!==l&&(e=l,q=e.length=0,k++),a=d.length,q!==a&&(k++,e.length=q=a),b=0;b<a;b++)e[b]!==e[b]&&d[b]!==d[b]||e[b]===d[b]||(k++,e[b]=d[b]);else{e!==n&&(e=n={},q=0,k++);a=
+0;for(b in d)d.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?e[b]!==d[b]&&(k++,e[b]=d[b]):(q++,e[b]=d[b],k++));if(q>a)for(b in k++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(q--,delete e[b])}else e!==d&&(e=d,k++);return k},function(){p?(p=!1,b(d,d,c)):b(d,f,c);if(h)if(X(d))if(db(d)){f=Array(d.length);for(var a=0;a<d.length;a++)f[a]=d[a]}else for(a in f={},d)Jc.call(d,a)&&(f[a]=d[a]);else f=d})},$digest:function(){var d,g,f,h,k=this.$$asyncQueue,l=this.$$postDigestQueue,q,x,s=b,L,Q=[],y,E,R;m("$digest");
+c=null;do{x=!1;for(L=this;k.length;){try{R=k.shift(),R.scope.$eval(R.expression)}catch(B){p.$$phase=null,e(B)}c=null}a:do{if(h=L.$$watchers)for(q=h.length;q--;)try{if(d=h[q])if((g=d.get(L))!==(f=d.last)&&!(d.eq?za(g,f):"number"==typeof g&&"number"==typeof f&&isNaN(g)&&isNaN(f)))x=!0,c=d,d.last=d.eq?ba(g):g,d.fn(g,f===n?g:f,L),5>s&&(y=4-s,Q[y]||(Q[y]=[]),E=P(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,E+="; newVal: "+ta(g)+"; oldVal: "+ta(f),Q[y].push(E));else if(d===c){x=!1;break a}}catch(t){p.$$phase=
+null,e(t)}if(!(h=L.$$childHead||L!==this&&L.$$nextSibling))for(;L!==this&&!(h=L.$$nextSibling);)L=L.$parent}while(L=h);if((x||k.length)&&!s--)throw p.$$phase=null,a("infdig",b,ta(Q));}while(x||k.length);for(p.$$phase=null;l.length;)try{l.shift()()}catch(S){e(S)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==p&&(q(this.$$listenerCount,hb(null,l,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&
+(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=C,this.$on=this.$watch=function(){return C})}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a){p.$$phase||
+p.$$asyncQueue.length||f.defer(function(){p.$$asyncQueue.length&&p.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);
+var e=this;return function(){c[gb(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,g=this,f=!1,h={name:a,targetScope:g,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=[h].concat(sa.call(arguments,1)),m,l;do{d=g.$$listeners[a]||c;h.currentScope=g;m=0;for(l=d.length;m<l;m++)if(d[m])try{d[m].apply(null,k)}catch(n){e(n)}else d.splice(m,1),m--,l--;if(f)break;g=g.$parent}while(g);return h},$broadcast:function(a,b){for(var c=this,d=this,g={name:a,
+targetScope:this,preventDefault:function(){g.defaultPrevented=!0},defaultPrevented:!1},f=[g].concat(sa.call(arguments,1)),h,k;c=d;){g.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,f)}catch(m){e(m)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}return g}};var p=new h;return p}]}function kd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*(https?|ftp|file|blob):|data:image\//;
+this.aHrefSanitizationWhitelist=function(a){return B(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,g;if(!T||8<=T)if(g=ua(c).href,""!==g&&!g.match(e))return"unsafe:"+g;return c}}}function Ke(b){if("self"===b)return b;if(t(b)){if(-1<b.indexOf("***"))throw wa("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+
+b+"$")}if(fb(b))return RegExp("^"+b.source+"$");throw wa("imatcher");}function Kc(b){var a=[];B(b)&&q(b,function(b){a.push(Ke(b))});return a}function je(){this.SCE_CONTEXTS=ga;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=Kc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Kc(b));return a};this.$get=["$injector",function(c){function d(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=
+function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var e=function(a){throw wa("unsafe");};c.has("$sanitize")&&(e=c.get("$sanitize"));var g=d(),f={};f[ga.HTML]=d(g);f[ga.CSS]=d(g);f[ga.URL]=d(g);f[ga.JS]=d(g);f[ga.RESOURCE_URL]=d(f[ga.URL]);return{trustAs:function(a,b){var c=f.hasOwnProperty(a)?f[a]:null;if(!c)throw wa("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw wa("itype",a);return new c(b)},
+getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var g=f.hasOwnProperty(c)?f[c]:null;if(g&&d instanceof g)return d.$$unwrapTrustedValue();if(c===ga.RESOURCE_URL){var g=ua(d.toString()),l,n,p=!1;l=0;for(n=b.length;l<n;l++)if("self"===b[l]?Lb(g):b[l].exec(g.href)){p=!0;break}if(p)for(l=0,n=a.length;l<n;l++)if("self"===a[l]?Lb(g):a[l].exec(g.href)){p=!1;break}if(p)return d;throw wa("insecurl",d.toString());}if(c===ga.HTML)return e(d);throw wa("unsafe");},valueOf:function(a){return a instanceof
+g?a.$$unwrapTrustedValue():a}}}]}function ie(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw wa("iequirks");var e=ba(ga);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=Ea);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,
+d(a,c))}};var g=e.parseAs,f=e.getTrusted,h=e.trustAs;q(ga,function(a,b){var c=I(b);e[Ta("parse_as_"+c)]=function(b){return g(a,b)};e[Ta("get_trusted_"+c)]=function(b){return f(a,b)};e[Ta("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function ke(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(I((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),g=a[0]||{},f=g.documentMode,h,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=g.body&&g.body.style,
+l=!1,n=!1;if(k){for(var p in k)if(l=m.exec(p)){h=l[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||h+"Transition"in k);n=!!("animation"in k||h+"Animation"in k);!d||l&&n||(l=t(g.body.style.webkitTransition),n=t(g.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!f||7<f),hasEvent:function(a){if("input"==a&&9==T)return!1;if(D(c[a])){var b=g.createElement("div");c[a]="on"+
+a in b}return c[a]},csp:Yb(),vendorPrefix:h,transitions:l,animations:n,android:d,msie:T,msieDocumentMode:f}}]}function me(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,m){var k=c.defer(),l=k.promise,n=B(m)&&!m;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete g[l.$$timeoutId]}n||b.$apply()},h);l.$$timeoutId=h;g[h]=k;return l}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),
+delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function ua(b,a){var c=b;T&&(W.setAttribute("href",c),c=W.href);W.setAttribute("href",c);return{href:W.href,protocol:W.protocol?W.protocol.replace(/:$/,""):"",host:W.host,search:W.search?W.search.replace(/^\?/,""):"",hash:W.hash?W.hash.replace(/^#/,""):"",hostname:W.hostname,port:W.port,pathname:"/"===W.pathname.charAt(0)?W.pathname:"/"+W.pathname}}function Lb(b){b=t(b)?ua(b):b;return b.protocol===Lc.protocol&&b.host===Lc.host}
+function ne(){this.$get=aa(O)}function jc(b){function a(d,e){if(X(d)){var g={};q(d,function(b,c){g[c]=a(c,b)});return g}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Mc);a("date",Nc);a("filter",Le);a("json",Me);a("limitTo",Ne);a("lowercase",Oe);a("number",Oc);a("orderBy",Pc);a("uppercase",Pe)}function Le(){return function(b,a,c){if(!M(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;
+return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Qa.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&Jc.call(a,d)&&c(a[d],b[d]))return!0;return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var g=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!g(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,
+b);default:for(var d in a)if("$"!==d.charAt(0)&&g(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(g(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var f in a)(function(b){"undefined"!=typeof a[b]&&e.push(function(c){return g("$"==b?c:c&&c[b],a[b])})})(f);break;case "function":e.push(a);break;default:return b}d=[];for(f=0;f<b.length;f++){var h=b[f];e.check(h)&&d.push(h)}return d}}function Mc(b){var a=
+b.NUMBER_FORMATS;return function(b,d){D(d)&&(d=a.CURRENCY_SYM);return Qc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Oc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Qc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Qc(b,a,c,d,e){if(null==b||!isFinite(b)||X(b))return"";var g=0>b;b=Math.abs(b);var f=b+"",h="",m=[],k=!1;if(-1!==f.indexOf("e")){var l=f.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&l[3]>e+1?f="0":(h=f,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));
+else{f=(f.split(Rc)[1]||"").length;D(e)&&(e=Math.min(Math.max(a.minFrac,f),a.maxFrac));f=Math.pow(10,e);b=Math.round(b*f)/f;b=(""+b).split(Rc);f=b[0];b=b[1]||"";var l=0,n=a.lgSize,p=a.gSize;if(f.length>=n+p)for(l=f.length-n,k=0;k<l;k++)0===(l-k)%p&&0!==k&&(h+=c),h+=f.charAt(k);for(k=l;k<f.length;k++)0===(f.length-k)%n&&0!==k&&(h+=c),h+=f.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}m.push(g?a.negPre:a.posPre);m.push(h);m.push(g?a.negSuf:a.posSuf);return m.join("")}function tb(b,
+a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function $(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return tb(e,a,d)}}function ub(b,a){return function(c,d){var e=c["get"+b](),g=Ga(a?"SHORT"+b:b);return d[g][e]}}function Sc(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Tc(b){return function(a){var c=Sc(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+
+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return tb(a,b)}}function Nc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var g=0,f=0,h=b[8]?a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(g=Y(b[9]+b[10]),f=Y(b[9]+b[11]));h.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));g=Y(b[4]||0)-g;f=Y(b[5]||0)-f;h=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,g,f,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
+return function(c,e){var g="",f=[],h,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;t(c)&&(c=Qe.test(c)?Y(c):a(c));Ab(c)&&(c=new Date(c));if(!ra(c))return c;for(;e;)(m=Re.exec(e))?(f=f.concat(sa.call(m,1)),e=f.pop()):(f.push(e),e=null);q(f,function(a){h=Se[a];g+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Me(){return function(b){return ta(b,!0)}}function Ne(){return function(b,a){if(!M(b)&&!t(b))return b;a=Y(a);if(t(b))return a?0<=a?b.slice(0,a):b.slice(a,
+b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Pc(b){return function(a,c,d){function e(a,b){return Pa(b)?function(b,c){return a(c,b)}:a}function g(a,b){var c=typeof a,d=typeof b;return c==d?("string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!M(a)||!c)return a;c=M(c)?c:[c];c=cd(c,function(a){var c=!1,d=a||Ea;if(t(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c=
+"-"==a.charAt(0),a=a.substring(1);d=b(a);if(d.constant){var f=d();return e(function(a,b){return g(a[f],b[f])},c)}}return e(function(a,b){return g(d(a),d(b))},c)});for(var f=[],h=0;h<a.length;h++)f.push(a[h]);return f.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function xa(b){P(b)&&(b={link:b});b.restrict=b.restrict||"AC";return aa(b)}function Uc(b,a,c,d){function e(a,c){c=c?"-"+ib(c,"-"):"";d.removeClass(b,(a?vb:wb)+c);d.addClass(b,(a?wb:vb)+c)}
+var g=this,f=b.parent().controller("form")||xb,h=0,m=g.$error={},k=[];g.$name=a.name||a.ngForm;g.$dirty=!1;g.$pristine=!0;g.$valid=!0;g.$invalid=!1;f.$addControl(g);b.addClass(Ma);e(!0);g.$addControl=function(a){Ba(a.$name,"input");k.push(a);a.$name&&(g[a.$name]=a)};g.$removeControl=function(a){a.$name&&g[a.$name]===a&&delete g[a.$name];q(m,function(b,c){g.$setValidity(c,!0,a)});Fa(k,a)};g.$setValidity=function(a,b,c){var d=m[a];if(b)d&&(Fa(d,c),d.length||(h--,h||(e(b),g.$valid=!0,g.$invalid=!1),
+m[a]=!1,e(!0,a),f.$setValidity(a,!0,g)));else{h||e(b);if(d){if(-1!=gb(d,c))return}else m[a]=d=[],h++,e(!1,a),f.$setValidity(a,!1,g);d.push(c);g.$valid=!1;g.$invalid=!0}};g.$setDirty=function(){d.removeClass(b,Ma);d.addClass(b,yb);g.$dirty=!0;g.$pristine=!1;f.$setDirty()};g.$setPristine=function(){d.removeClass(b,yb);d.addClass(b,Ma);g.$dirty=!1;g.$pristine=!0;q(k,function(a){a.$setPristine()})}}function qa(b,a,c,d){b.$setValidity(a,c);return c?d:s}function Te(b,a,c){var d=c.prop("validity");X(d)&&
+b.$parsers.push(function(c){if(b.$error[a]||!(d.badInput||d.customError||d.typeMismatch)||d.valueMissing)return c;b.$setValidity(a,!1)})}function bb(b,a,c,d,e,g){var f=a.prop("validity");if(!e.android){var h=!1;a.on("compositionstart",function(a){h=!0});a.on("compositionend",function(){h=!1;m()})}var m=function(){if(!h){var e=a.val();Pa(c.ngTrim||"T")&&(e=ca(e));if(d.$viewValue!==e||f&&""===e&&!f.valueMissing)b.$$phase?d.$setViewValue(e):b.$apply(function(){d.$setViewValue(e)})}};if(e.hasEvent("input"))a.on("input",
+m);else{var k,l=function(){k||(k=g.defer(function(){m();k=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||l()});if(e.hasEvent("paste"))a.on("paste cut",l)}a.on("change",m);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var n=c.ngPattern;n&&((e=n.match(/^\/(.*)\/([gim]*)$/))?(n=RegExp(e[1],e[2]),e=function(a){return qa(d,"pattern",d.$isEmpty(a)||n.test(a),a)}):e=function(c){var e=b.$eval(n);if(!e||!e.test)throw v("ngPattern")("noregexp",n,
+e,ha(a));return qa(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var p=Y(c.ngMinlength);e=function(a){return qa(d,"minlength",d.$isEmpty(a)||a.length>=p,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var r=Y(c.ngMaxlength);e=function(a){return qa(d,"maxlength",d.$isEmpty(a)||a.length<=r,a)};d.$parsers.push(e);d.$formatters.push(e)}}function zb(b,a){return function(c){var d;return ra(c)?c:t(c)&&(b.lastIndex=0,c=b.exec(c))?(c.shift(),
+d={yyyy:0,MM:1,dd:1,HH:0,mm:0},q(c,function(b,c){c<a.length&&(d[a[c]]=+b)}),new Date(d.yyyy,d.MM-1,d.dd,d.HH,d.mm)):NaN}}function cb(b,a,c,d){return function(e,g,f,h,m,k,l){bb(e,g,f,h,m,k);h.$parsers.push(function(d){if(h.$isEmpty(d))return h.$setValidity(b,!0),null;if(a.test(d))return h.$setValidity(b,!0),c(d);h.$setValidity(b,!1);return s});h.$formatters.push(function(a){return ra(a)?l("date")(a,d):""});f.min&&(e=function(a){var b=h.$isEmpty(a)||c(a)>=c(f.min);h.$setValidity("min",b);return b?a:
+s},h.$parsers.push(e),h.$formatters.push(e));f.max&&(e=function(a){var b=h.$isEmpty(a)||c(a)<=c(f.max);h.$setValidity("max",b);return b?a:s},h.$parsers.push(e),h.$formatters.push(e))}}function Rb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],l=0;l<b.length;l++)if(e==b[l])continue a;c.push(e)}return c}function e(a){if(!M(a)){if(t(a))return a.split(" ");if(X(a)){var b=[];q(a,function(a,c){a&&b.push(c)});return b}}return a}return{restrict:"AC",
+link:function(g,f,h){function m(a,b){var c=f.data("$classCounts")||{},d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});f.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||g.$index%2===a){var k=e(b||[]);if(!l){var r=m(k,1);h.$addClass(r)}else if(!za(b,l)){var q=e(l),r=d(k,q),k=d(q,k),k=m(k,-1),r=m(r,1);0===r.length?c.removeClass(f,k):0===k.length?c.addClass(f,r):c.setClass(f,r,k)}}l=ba(b)}var l;g.$watch(h[b],k,!0);h.$observe("class",function(a){k(g.$eval(h[b]))});
+"ngClass"!==b&&g.$watch("$index",function(c,d){var f=c&1;if(f!==d&1){var k=e(g.$eval(h[b]));f===a?(f=m(k,1),h.$addClass(f)):(f=m(k,-1),h.$removeClass(f))}})}}}]}var I=function(b){return t(b)?b.toLowerCase():b},Jc=Object.prototype.hasOwnProperty,Ga=function(b){return t(b)?b.toUpperCase():b},T,y,Ha,sa=[].slice,Ue=[].push,ya=Object.prototype.toString,Oa=v("ng"),Qa=O.angular||(O.angular={}),Sa,La,ka=["0","0","0"];T=Y((/msie (\d+)/.exec(I(navigator.userAgent))||[])[1]);isNaN(T)&&(T=Y((/trident\/.*; rv:(\d+)/.exec(I(navigator.userAgent))||
+[])[1]));C.$inject=[];Ea.$inject=[];var ca=function(){return String.prototype.trim?function(b){return t(b)?b.trim():b}:function(b){return t(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();La=9>T?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ga(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var fd=/[A-Z]/g,id={full:"1.3.0-beta.5",major:1,minor:3,dot:0,codeName:"chimeric-glitterfication"},Va=N.cache={},jb=N.expando="ng-"+
+(new Date).getTime(),we=1,qb=O.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Ua=O.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};N._data=function(b){return this.cache[b[this.expando]]||{}};var qe=/([\:\-\_]+(.))/g,re=/^moz([A-Z])/,Hb=v("jqLite"),ve=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Gb=/<|&#?\w+;/,te=/<([\w:]+)/,ue=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ea={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ea.optgroup=ea.option;ea.tbody=ea.tfoot=ea.colgroup=ea.caption=ea.thead;ea.th=ea.td;var Ka=N.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===U.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),N(O).on("load",a))},
+toString:function(){var b=[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0,push:Ue,sort:[].sort,splice:[].splice},nb={};q("multiple selected checked disabled readOnly required open".split(" "),function(b){nb[I(b)]=b});var rc={};q("input select option textarea button form details".split(" "),function(b){rc[Ga(b)]=!0});q({data:nc,inheritedData:mb,scope:function(b){return y(b).data("$scope")||mb(b.parentNode||b,["$isolateScope",
+"$scope"])},isolateScope:function(b){return y(b).data("$isolateScope")||y(b).data("$isolateScopeNoTemplate")},controller:oc,injector:function(b){return mb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Jb,css:function(b,a,c){a=Ta(a);if(B(c))b.style[a]=c;else{var d;8>=T&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=T&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=I(a);if(nb[d])if(B(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));
+else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:s;else if(B(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?s:b},prop:function(b,a,c){if(B(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(D(d))return e?b[e]:"";b[e]=d}var a=[];9>T?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(D(a)){if("SELECT"===La(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&
+c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(D(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ia(d[c]);b.innerHTML=a},empty:pc},function(b,a){N.prototype[a]=function(a,d){var e,g;if(b!==pc&&(2==b.length&&b!==Jb&&b!==oc?a:d)===s){if(X(a)){for(e=0;e<this.length;e++)if(b===nc)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}e=b.$dv;g=e===s?Math.min(this.length,1):this.length;for(var f=0;f<g;f++){var h=b(this[f],a,d);e=
+e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:lc,dealoc:Ia,on:function a(c,d,e,g){if(B(g))throw Hb("onargs");var f=la(c,"events"),h=la(c,"handle");f||la(c,"events",f={});h||la(c,"handle",h=xe(c,f));q(d.split(" "),function(d){var g=f[d];if(!g){if("mouseenter"==d||"mouseleave"==d){var l=U.body.contains||U.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):
+a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};f[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||h(a,d)})}else qb(c,d,h),f[d]=[];g=f[d]}g.push(e)})},off:mc,one:function(a,c,d){a=y(a);a.on(c,function g(){a.off(c,d);a.off(c,g)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ia(a);q(new N(c),function(c){d?e.insertBefore(c,d.nextSibling):
+e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){q(new N(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new N(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ia(a);
+var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new N(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:lb,removeClass:kb,toggleClass:function(a,c,d){c&&q(c.split(" "),function(c){var g=d;D(g)&&(g=!Jb(a,c));(g?lb:kb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName?
+a.getElementsByTagName(c):[]},clone:Ib,triggerHandler:function(a,c,d){c=(la(a,"events")||{})[c];d=d||[];var e=[{preventDefault:C,stopPropagation:C}];q(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){N.prototype[c]=function(c,e,g){for(var f,h=0;h<this.length;h++)D(f)?(f=a(this[h],c,e,g),B(f)&&(f=y(f))):kc(f,a(this[h],c,e,g));return B(f)?f:this};N.prototype.bind=N.prototype.on;N.prototype.unbind=N.prototype.off});Wa.prototype={put:function(a,c){this[Ja(a)]=c},get:function(a){return this[Ja(a)]},
+remove:function(a){var c=this[a=Ja(a)];delete this[a];return c}};var ze=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Ae=/,/,Be=/^\s*(_?)(\S+?)\1\s*$/,ye=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Xa=v("$injector"),Ve=v("$animate"),Ud=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Ve("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?
+a:null);return this.$$classNameFilter};this.$get=["$timeout","$$asyncCallback",function(a,d){return{enter:function(a,c,f,h){f?f.after(a):c.prepend(a);h&&d(h)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,h){this.enter(a,c,d,h)},addClass:function(a,c,f){c=t(c)?c:M(c)?c.join(" "):"";q(a,function(a){lb(a,c)});f&&d(f)},removeClass:function(a,c,f){c=t(c)?c:M(c)?c.join(" "):"";q(a,function(a){kb(a,c)});f&&d(f)},setClass:function(a,c,f,h){q(a,function(a){lb(a,c);kb(a,f)});h&&d(h)},enabled:C}}]}],
+ja=v("$compile");fc.$inject=["$provide","$$sanitizeUriProvider"];var De=/^(x[\:\-_]|data[\:\-_])/i,zc=v("$interpolate"),We=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Ge={http:80,https:443,ftp:21},Nb=v("$location");Ec.prototype=Ob.prototype=Dc.prototype={$$html5:!1,$$replace:!1,absUrl:rb("$$absUrl"),url:function(a,c){if(D(a))return this.$$url;var d=We.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:rb("$$protocol"),host:rb("$$host"),
+port:rb("$$port"),path:Fc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(t(a))this.$$search=ac(a);else if(X(a))this.$$search=a;else throw Nb("isrcharg");break;default:D(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Fc("$$hash",Ea),replace:function(){this.$$replace=!0;return this}};var Ca=v("$parse"),Ic={},va,Na={"null":function(){return null},"true":function(){return!0},
+"false":function(){return!1},undefined:C,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return B(d)?B(e)?d+e:d:B(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(B(d)?d:0)-(B(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":C,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,
+c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Xe={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},
+Qb=function(a){this.options=a};Qb.prototype={constructor:Qb,lex:function(a){this.text=a;this.index=0;this.ch=s;this.lastCh=":";this.tokens=[];var c;for(a=[];this.index<this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent(),this.was("{,")&&("{"===a[0]&&(c=this.tokens[this.tokens.length-1]))&&(c.json=-1===c.text.indexOf("."));
+else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch,json:this.was(":[,")&&this.is("{[")||this.is("}]:,")}),this.is("{[")&&a.unshift(this.ch),this.is("}]")&&a.shift(),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{var d=this.ch+this.peek(),e=d+this.peek(2),g=Na[this.ch],f=Na[d],h=Na[e];h?(this.tokens.push({index:this.index,text:e,fn:h}),this.index+=3):f?(this.tokens.push({index:this.index,text:d,fn:f}),this.index+=2):g?(this.tokens.push({index:this.index,
+text:this.ch,fn:g,json:this.was("[,:")&&this.is("+-")}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===
+a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=B(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw Ca("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=I(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=
+d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,json:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,g,f,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;
+this.index++}if(e)for(g=this.index;g<this.text.length;){h=this.text.charAt(g);if("("===h){f=c.substr(e-d+1);c=c.substr(0,e-d);this.index=g;break}if(this.isWhitespace(h))g++;else break}d={index:d,text:c};if(Na.hasOwnProperty(c))d.fn=Na[c],d.json=Na[c];else{var m=Hc(c,this.options,this.text);d.fn=A(function(a,c){return m(a,c)},{assign:function(d,e){return sb(d,c,e,a.text,a.options)}})}this.tokens.push(d);f&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:f,json:!1}))},
+readString:function(a){var c=this.index;this.index++;for(var d="",e=a,g=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),e=e+f;if(g)"u"===f?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d=(g=Xe[f])?d+g:d+f,g=!1;else if("\\"===f)g=!0;else{if(f===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=
+f}this.index++}this.throwError("Unterminated quote",c)}};var ab=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};ab.ZERO=A(function(){return 0},{constant:!0});ab.prototype={constructor:ab,parse:function(a,c){this.text=a;this.json=c;this.tokens=this.lexer.lex(a);c&&(this.assignment=this.logicalOR,this.functionCall=this.fieldAccess=this.objectIndex=this.filterChain=function(){this.throwError("is not valid json",{text:a,index:0})});var d=c?this.primary():this.statements();0!==this.tokens.length&&
+this.throwError("is an unexpected token",this.tokens[0]);d.literal=!!d.literal;d.constant=!!d.constant;return d},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);c.json&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?
+(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw Ca("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw Ca("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var g=this.tokens[0],f=g.text;if(f===a||f===c||f===d||f===e||!(a||c||d||e))return g}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,
+e))?(this.json&&!a.json&&this.throwError("is not valid json",a),this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return A(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return A(function(e,g){return a(e,g)?c(e,g):d(e,g)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return A(function(e,g){return c(e,g,a,d)},{constant:a.constant&&d.constant})},
+statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,g=0;g<a.length;g++){var f=a[g];f&&(e=f(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=
+function(a,e,h){h=[h];for(var m=0;m<d.length;m++)h.push(d[m](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,g){return a.assign(d,c(d,g),g)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();
+if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},
+relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(ab.ZERO,a.fn,
+this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Hc(d,this.options,this.text);return A(function(c,d,h){return e(h||a(c,d))},{assign:function(e,f,h){return sb(a(e,h),d,f,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return A(function(e,g){var f=a(e,g),h=d(e,g),m;if(!f)return s;(f=$a(f[h],c.text))&&(f.then&&c.options.unwrapPromises)&&(m=f,"$$v"in f||(m.$$v=s,m.then(function(a){m.$$v=
+a})),f=f.$$v);return f},{assign:function(e,g,f){var h=d(e,f);return $a(a(e,f),c.text)[h]=g}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(g,f){for(var h=[],m=c?c(g,f):g,k=0;k<d.length;k++)h.push(d[k](g,f));k=a(g,f,m)||C;$a(m,e.text);$a(k,e.text);h=k.apply?k.apply(m,h):k(h[0],h[1],h[2],h[3],h[4]);return $a(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
+var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return A(function(c,d){for(var f=[],h=0;h<a.length;h++)f.push(a[h](c,d));return f},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return A(function(c,d){for(var e={},m=0;m<
+a.length;m++){var k=a[m];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Pb={},wa=v("$sce"),ga={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},W=U.createElement("a"),Lc=ua(O.location.href,!0);jc.$inject=["$provide"];Mc.$inject=["$locale"];Oc.$inject=["$locale"];var Rc=".",Se={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:ub("Month"),MMM:ub("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",
+1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",1),sss:$("Milliseconds",3),EEEE:ub("Day"),EEE:ub("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(tb(Math[0<a?"floor":"ceil"](a/60),2)+tb(Math.abs(a%60),2))},ww:Tc(2),w:Tc(1)},Re=/((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,Qe=/^\-?\d+$/;Nc.$inject=["$locale"];var Oe=
+aa(I),Pe=aa(Ga);Pc.$inject=["$parse"];var ld=aa({restrict:"E",compile:function(a,c){8>=T&&(c.href||c.name||c.$set("href",""),a.append(U.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var g="[object SVGAnimatedString]"===ya.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(g)||a.preventDefault()})}}}),Eb={};q(nb,function(a,c){if("multiple"!=a){var d=na("ng-"+c);Eb[d]=function(){return{priority:100,link:function(a,g,f){a.$watch(f[d],function(a){f.$set(c,
+!!a)})}}}}});q(["src","srcset","href"],function(a){var c=na("ng-"+a);Eb[c]=function(){return{priority:99,link:function(d,e,g){var f=a,h=a;"href"===a&&"[object SVGAnimatedString]"===ya.call(e.prop("href"))&&(h="xlinkHref",g.$attr[h]="xlink:href",f=null);g.$observe(c,function(a){a&&(g.$set(h,a),T&&f&&e.prop(f,g[h]))})}}}});var xb={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C,$setPristine:C};Uc.$inject=["$element","$attrs","$scope","$animate"];var Vc=function(a){return["$timeout",function(c){return{name:"form",
+restrict:a?"EAC":"E",controller:Uc,compile:function(){return{pre:function(a,e,g,f){if(!g.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};qb(e[0],"submit",h);e.on("$destroy",function(){c(function(){Ua(e[0],"submit",h)},0,!1)})}var m=e.parent().controller("form"),k=g.name||g.ngForm;k&&sb(a,k,f,k);if(m)e.on("$destroy",function(){m.$removeControl(f);k&&sb(a,k,s,k);A(f,xb)})}}}}}]},md=Vc(),zd=Vc(!0),Ye=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,
+Ze=/^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i,$e=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Wc=/^(\d{4})-(\d{2})-(\d{2})$/,Xc=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/,Sb=/^(\d{4})-W(\d\d)$/,Yc=/^(\d{4})-(\d\d)$/,Zc=/^(\d\d):(\d\d)$/,$c={text:bb,date:cb("date",Wc,zb(Wc,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":cb("datetimelocal",Xc,zb(Xc,["yyyy","MM","dd","HH","mm"]),"yyyy-MM-ddTHH:mm"),time:cb("time",Zc,zb(Zc,["HH","mm"]),"HH:mm"),week:cb("week",Sb,function(a){if(ra(a))return a;
+if(t(a)){Sb.lastIndex=0;var c=Sb.exec(a);if(c){a=+c[1];var d=+c[2],c=Sc(a),d=7*(d-1);return new Date(a,0,c.getDate()+d)}}return NaN},"yyyy-Www"),month:cb("month",Yc,zb(Yc,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,g,f){bb(a,c,d,e,g,f);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||$e.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});Te(e,"number",c);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=
+parseFloat(d.min);return qa(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return qa(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return qa(e,"number",e.$isEmpty(a)||Ab(a),a)})},url:function(a,c,d,e,g,f){bb(a,c,d,e,g,f);a=function(a){return qa(e,"url",e.$isEmpty(a)||Ye.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,f){bb(a,c,d,e,g,f);
+a=function(a){return qa(e,"email",e.$isEmpty(a)||Ze.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){D(d.name)&&c.attr("name",eb());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,f=d.ngFalseValue;t(g)||(g=!0);t(f)||(f=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});
+e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==g};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:f})},hidden:C,button:C,submit:C,reset:C,file:C},gc=["$browser","$sniffer","$filter",function(a,c,d){return{restrict:"E",require:"?ngModel",link:function(e,g,f,h){h&&($c[I(f.type)]||$c.text)(e,g,f,h,c,a,d)}}}],wb="ng-valid",vb="ng-invalid",Ma="ng-pristine",yb="ng-dirty",af=["$scope","$exceptionHandler","$attrs","$element","$parse",
+"$animate",function(a,c,d,e,g,f){function h(a,c){c=c?"-"+ib(c,"-"):"";f.removeClass(e,(a?vb:wb)+c);f.addClass(e,(a?wb:vb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var m=g(d.ngModel),k=m.assign;if(!k)throw v("ngModel")("nonassign",d.ngModel,ha(e));this.$render=C;this.$isEmpty=function(a){return D(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||
+xb,n=0,p=this.$error={};e.addClass(Ma);h(!0);this.$setValidity=function(a,c){p[a]!==!c&&(c?(p[a]&&n--,n||(h(!0),this.$valid=!0,this.$invalid=!1)):(h(!1),this.$invalid=!0,this.$valid=!1,n++),p[a]=!c,h(c,a),l.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;f.removeClass(e,yb);f.addClass(e,Ma)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,f.removeClass(e,Ma),f.addClass(e,yb),l.$setDirty());q(this.$parsers,function(a){d=
+a(d)});this.$modelValue!==d&&(this.$modelValue=d,k(a,d),q(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var r=this;a.$watch(function(){var c=m(a);if(r.$modelValue!==c){var d=r.$formatters,e=d.length;for(r.$modelValue=c;e--;)c=d[e](c);r.$viewValue!==c&&(r.$viewValue=c,r.$render())}return c})}],Od=function(){return{require:["ngModel","^?form"],controller:af,link:function(a,c,d,e){var g=e[0],f=e[1]||xb;f.$addControl(g);a.$on("$destroy",function(){f.$removeControl(g)})}}},Qd=aa({require:"ngModel",
+link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),hc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},Pd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||
+d.ngList||",";e.$parsers.push(function(a){if(!D(a)){var c=[];a&&q(a.split(g),function(a){a&&c.push(ca(a))});return c}});e.$formatters.push(function(a){return M(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},bf=/^(true|false|\d+)$/,Rd=function(){return{priority:100,compile:function(a,c){return bf.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a)})}}}},rd=xa(function(a,c,d){c.addClass("ng-binding").data("$binding",
+d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),td=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],sd=["$sce","$parse",function(a,c){return function(d,e,g){e.addClass("ng-binding").data("$binding",g.ngBindHtml);var f=c(g.ngBindHtml);d.$watch(function(){return(f(d)||"").toString()},function(c){e.html(a.getTrustedHtml(f(d))||"")})}}],ud=Rb("",!0),wd=
+Rb("Odd",0),vd=Rb("Even",1),xd=xa({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),yd=[function(){return{scope:!0,controller:"@",priority:500}}],ic={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=na("ng-"+a);ic[c]=["$parse",function(d){return{compile:function(e,g){var f=d(g[c]);return function(c,d,e){d.on(I(a),function(a){c.$apply(function(){f(c,{$event:a})})})}}}}]});
+var Bd=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,g,f){var h,m,k;c.$watch(e.ngIf,function(g){Pa(g)?m||(m=c.$new(),f(m,function(c){c[c.length++]=U.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)})):(k&&(k.remove(),k=null),m&&(m.$destroy(),m=null),h&&(k=Db(h.clone),a.leave(k,function(){k=null}),h=null))})}}}],Cd=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,g){return{restrict:"ECA",
+priority:400,terminal:!0,transclude:"element",controller:Qa.noop,compile:function(f,h){var m=h.ngInclude||h.src,k=h.onload||"",l=h.autoscroll;return function(f,h,r,q,z){var s=0,w,y,G,x=function(){y&&(y.remove(),y=null);w&&(w.$destroy(),w=null);G&&(e.leave(G,function(){y=null}),y=G,G=null)};f.$watch(g.parseAsResourceUrl(m),function(g){var m=function(){!B(l)||l&&!f.$eval(l)||d()},r=++s;g?(a.get(g,{cache:c}).success(function(a){if(r===s){var c=f.$new();q.template=a;a=z(c,function(a){x();e.enter(a,null,
+h,m)});w=c;G=a;w.$emit("$includeContentLoaded");f.$eval(k)}}).error(function(){r===s&&x()}),f.$emit("$includeContentRequested")):(x(),q.template=null)})}}}}],Sd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,g){d.html(g.template);a(d.contents())(c)}}}],Dd=xa({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Ed=xa({terminal:!0,priority:1E3}),Fd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",
+link:function(e,g,f){var h=f.count,m=f.$attr.when&&g.attr(f.$attr.when),k=f.offset||0,l=e.$eval(m)||{},n={},p=c.startSymbol(),r=c.endSymbol(),s=/^when(Minus)?(.+)$/;q(f,function(a,c){s.test(c)&&(l[I(c.replace("when","").replace("Minus","-"))]=g.attr(f.$attr[c]))});q(l,function(a,e){n[e]=c(a.replace(d,p+h+"-"+k+r))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-k));return n[c](e,g,!0)},function(a){g.text(a)})}}}],Gd=["$parse","$animate",function(a,
+c){var d=v("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,g,f,h,m){var k=f.ngRepeat,l=k.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,p,r,s,z,B,w={$id:Ja};if(!l)throw d("iexp",k);f=l[1];h=l[2];(l=l[3])?(n=a(l),p=function(a,c,d){B&&(w[B]=a);w[z]=c;w.$index=d;return n(e,w)}):(r=function(a,c){return Ja(c)},s=function(a){return a});l=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",f);z=l[3]||l[1];
+B=l[2];var H={};e.$watchCollection(h,function(a){var f,h,l=g[0],n,w={},E,R,t,C,S,v,D=[];if(db(a))S=a,n=p||r;else{n=p||s;S=[];for(t in a)a.hasOwnProperty(t)&&"$"!=t.charAt(0)&&S.push(t);S.sort()}E=S.length;h=D.length=S.length;for(f=0;f<h;f++)if(t=a===S?f:S[f],C=a[t],C=n(t,C,f),Ba(C,"`track by` id"),H.hasOwnProperty(C))v=H[C],delete H[C],w[C]=v,D[f]=v;else{if(w.hasOwnProperty(C))throw q(D,function(a){a&&a.scope&&(H[a.id]=a)}),d("dupes",k,C);D[f]={id:C};w[C]=!1}for(t in H)H.hasOwnProperty(t)&&(v=H[t],
+f=Db(v.clone),c.leave(f),q(f,function(a){a.$$NG_REMOVED=!0}),v.scope.$destroy());f=0;for(h=S.length;f<h;f++){t=a===S?f:S[f];C=a[t];v=D[f];D[f-1]&&(l=D[f-1].clone[D[f-1].clone.length-1]);if(v.scope){R=v.scope;n=l;do n=n.nextSibling;while(n&&n.$$NG_REMOVED);v.clone[0]!=n&&c.move(Db(v.clone),null,y(l));l=v.clone[v.clone.length-1]}else R=e.$new();R[z]=C;B&&(R[B]=t);R.$index=f;R.$first=0===f;R.$last=f===E-1;R.$middle=!(R.$first||R.$last);R.$odd=!(R.$even=0===(f&1));v.scope||m(R,function(a){a[a.length++]=
+U.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,y(l));l=a;v.scope=R;v.clone=a;w[v.id]=v})}H=w})}}}],Hd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Pa(c)?"removeClass":"addClass"](d,"ng-hide")})}}],Ad=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Pa(c)?"addClass":"removeClass"](d,"ng-hide")})}}],Id=xa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Jd=["$animate",
+function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,g){var f,h,m,k=[];c.$watch(e.ngSwitch||e.on,function(d){var n,p=k.length;if(0<p){if(m){for(n=0;n<p;n++)m[n].remove();m=null}m=[];for(n=0;n<p;n++){var r=h[n];k[n].$destroy();m[n]=r;a.leave(r,function(){m.splice(n,1);0===m.length&&(m=null)})}}h=[];k=[];if(f=g.cases["!"+d]||g.cases["?"])c.$eval(e.change),q(f,function(d){var e=c.$new();k.push(e);d.transclude(e,function(c){var e=d.element;
+h.push(c);a.enter(c,e.parent(),e)})})})}}}],Kd=xa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,g){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:g,element:c})}}),Ld=xa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,g){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:g,element:c})}}),Nd=xa({link:function(a,c,d,e,g){if(!g)throw v("ngTransclude")("orphan",ha(c));g(function(a){c.empty();
+c.append(a)})}}),nd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],cf=v("ngOptions"),Md=aa({terminal:!0}),od=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,e={$setViewValue:C};return{restrict:"E",require:["select","?ngModel"],
+controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},l=e,n;m.databound=d.ngModel;m.init=function(a,c,d){l=a;n=d};m.addOption=function(c){Ba(c,'"option value"');k[c]=!0;l.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};m.removeOption=function(a){this.hasOption(a)&&(delete k[a],l.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Ja(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",!0)};m.hasOption=function(a){return k.hasOwnProperty(a)};
+c.$on("$destroy",function(){m.renderUnknownOption=C})}],link:function(e,f,h,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(G.parent()&&G.remove(),c.val(a),""===a&&v.prop("selected",!0)):D(a)&&v?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){G.parent()&&G.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new Wa(d.$viewValue);q(c.find("option"),function(c){c.selected=B(a.get(c.value))})};a.$watch(function(){za(e,
+d.$viewValue)||(e=ba(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k,s,t,u;t=g.$modelValue;u=y(e)||[];var D=n?Tb(u):u,G,J,A;J={};s=!1;var E,I;if(r)if(v&&M(t))for(s=new Wa([]),A=0;A<t.length;A++)J[l]=t[A],s.put(v(e,J),t[A]);else s=new Wa(t);for(A=0;G=D.length,A<G;A++){k=A;if(n){k=D[A];if("$"===k.charAt(0))continue;J[n]=k}J[l]=
+u[k];d=p(e,J)||"";(k=a[d])||(k=a[d]=[],c.push(d));r?d=B(s.remove(v?v(e,J):q(e,J))):(v?(d={},d[l]=t,d=v(e,d)===v(e,J)):d=t===q(e,J),s=s||d);E=m(e,J);E=B(E)?E:"";k.push({id:v?v(e,J):n?D[A]:A,label:E,selected:d})}r||(z||null===t?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));J=0;for(D=c.length;J<D;J++){d=c[J];k=a[d];x.length<=J?(t={element:C.clone().attr("label",d),label:k.label},u=[t],x.push(u),f.append(t.element)):(u=x[J],t=u[0],t.label!=d&&t.element.attr("label",
+t.label=d));E=null;A=0;for(G=k.length;A<G;A++)s=k[A],(d=u[A+1])?(E=d.element,d.label!==s.label&&E.text(d.label=s.label),d.id!==s.id&&E.val(d.id=s.id),d.selected!==s.selected&&E.prop("selected",d.selected=s.selected)):(""===s.id&&z?I=z:(I=w.clone()).val(s.id).attr("selected",s.selected).text(s.label),u.push({element:I,label:s.label,id:s.id,selected:s.selected}),E?E.after(I):t.element.append(I),E=I);for(A++;u.length>A;)u.pop().element.remove()}for(;x.length>J;)x.pop()[0].element.remove()}var k;if(!(k=
+t.match(d)))throw cf("iexp",t,ha(f));var m=c(k[2]||k[1]),l=k[4]||k[6],n=k[5],p=c(k[3]||""),q=c(k[2]?k[1]:l),y=c(k[7]),v=k[8]?c(k[8]):null,x=[[{element:f,label:""}]];z&&(a(z)(e),z.removeClass("ng-scope"),z.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=y(e)||[],d={},h,k,m,p,t,w,u;if(r)for(k=[],p=0,w=x.length;p<w;p++)for(a=x[p],m=1,t=a.length;m<t;m++){if((h=a[m].element)[0].selected){h=h.val();n&&(d[n]=h);if(v)for(u=0;u<c.length&&(d[l]=c[u],v(e,d)!=h);u++);else d[l]=c[h];k.push(q(e,
+d))}}else{h=f.val();if("?"==h)k=s;else if(""===h)k=null;else if(v)for(u=0;u<c.length;u++){if(d[l]=c[u],v(e,d)==h){k=q(e,d);break}}else d[l]=c[h],n&&(d[n]=h),k=q(e,d);1<x[0].length&&x[0][1].id!==h&&(x[0][1].selected=!1)}g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(m[1]){var p=m[0];m=m[1];var r=h.multiple,t=h.ngOptions,z=!1,v,w=y(U.createElement("option")),C=y(U.createElement("optgroup")),G=w.clone();h=0;for(var x=f.children(),A=x.length;h<A;h++)if(""===x[h].value){v=z=x.eq(h);break}p.init(m,z,
+G);r&&(m.$isEmpty=function(a){return!a||0===a.length});t?n(e,f,m):r?l(e,f,m):k(e,f,m,p)}}}}],qd=["$interpolate",function(a){var c={addOption:C,removeOption:C};return{restrict:"E",priority:100,compile:function(d,e){if(D(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),l=k.data("$selectController")||k.parent().data("$selectController");l&&l.databound?d.prop("selected",!1):l=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&l.removeOption(c);l.addOption(a)}):
+l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],pd=aa({restrict:"E",terminal:!1});O.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):((Ha=O.jQuery)?(y=Ha,A(Ha.fn,{scope:Ka.scope,isolateScope:Ka.isolateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inheritedData}),Fb("remove",!0,!0,!1),Fb("empty",!1,!1,!1),Fb("html",!1,!1,!0)):y=N,Qa.element=y,hd(Qa),y(U).ready(function(){ed(U,cc)}))})(window,document);
+!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
+//# sourceMappingURL=angular.min.js.map
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js
new file mode 100644
index 0000000..3f196c3
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js
@@ -0,0 +1,13 @@
+/*
+ AngularJS v1.3.0-beta.5
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(H,a,A){'use strict';function D(p,g){g=g||{};a.forEach(g,function(a,c){delete g[c]});for(var c in p)!p.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(g[c]=p[c]);return g}var v=a.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;a.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(p,g){function c(a,c){this.template=a;this.defaults=c||{};this.urlParams={}}function t(n,w,l){function r(h,d){var e={};d=x({},w,d);s(d,function(b,d){u(b)&&(b=b());var k;if(b&&
+b.charAt&&"@"==b.charAt(0)){k=h;var a=b.substr(1);if(null==a||""===a||"hasOwnProperty"===a||!C.test("."+a))throw v("badmember",a);for(var a=a.split("."),f=0,c=a.length;f<c&&k!==A;f++){var g=a[f];k=null!==k?k[g]:A}}else k=b;e[d]=k});return e}function e(a){return a.resource}function f(a){D(a||{},this)}var F=new c(n);l=x({},B,l);s(l,function(h,d){var c=/^(POST|PUT|PATCH)$/i.test(h.method);f[d]=function(b,d,k,w){var q={},n,l,y;switch(arguments.length){case 4:y=w,l=k;case 3:case 2:if(u(d)){if(u(b)){l=
+b;y=d;break}l=d;y=k}else{q=b;n=d;l=k;break}case 1:u(b)?l=b:c?n=b:q=b;break;case 0:break;default:throw v("badargs",arguments.length);}var t=this instanceof f,m=t?n:h.isArray?[]:new f(n),z={},B=h.interceptor&&h.interceptor.response||e,C=h.interceptor&&h.interceptor.responseError||A;s(h,function(a,b){"params"!=b&&("isArray"!=b&&"interceptor"!=b)&&(z[b]=G(a))});c&&(z.data=n);F.setUrlParams(z,x({},r(n,h.params||{}),q),h.url);q=p(z).then(function(b){var d=b.data,k=m.$promise;if(d){if(a.isArray(d)!==!!h.isArray)throw v("badcfg",
+h.isArray?"array":"object",a.isArray(d)?"array":"object");h.isArray?(m.length=0,s(d,function(b){m.push(new f(b))})):(D(d,m),m.$promise=k)}m.$resolved=!0;b.resource=m;return b},function(b){m.$resolved=!0;(y||E)(b);return g.reject(b)});q=q.then(function(b){var a=B(b);(l||E)(a,b.headers);return a},C);return t?q:(m.$promise=q,m.$resolved=!1,m)};f.prototype["$"+d]=function(b,a,k){u(b)&&(k=a,a=b,b={});b=f[d].call(this,b,this,a,k);return b.$promise||b}});f.bind=function(a){return t(n,x({},w,a),l)};return f}
+var B={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},E=a.noop,s=a.forEach,x=a.extend,G=a.copy,u=a.isFunction;c.prototype={setUrlParams:function(c,g,l){var r=this,e=l||r.template,f,p,h=r.urlParams={};s(e.split(/\W/),function(a){if("hasOwnProperty"===a)throw v("badname");!/^\d+$/.test(a)&&(a&&RegExp("(^|[^\\\\]):"+a+"(\\W|$)").test(e))&&(h[a]=!0)});e=e.replace(/\\:/g,":");g=g||{};s(r.urlParams,function(d,c){f=g.hasOwnProperty(c)?
+g[c]:r.defaults[c];a.isDefined(f)&&null!==f?(p=encodeURIComponent(f).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+c+"(\\W|$)","g"),function(a,c){return p+c})):e=e.replace(RegExp("(/?):"+c+"(\\W|$)","g"),function(a,c,d){return"/"==d.charAt(0)?d:c+d})});e=e.replace(/\/+$/,"")||"/";e=e.replace(/\/\.(?=\w+($|\?))/,".");c.url=e.replace(/\/\\\./,"/.");s(g,function(a,
+e){r.urlParams[e]||(c.params=c.params||{},c.params[e]=a)})}};return t}])})(window,window.angular);
+//# sourceMappingURL=angular-resource.min.js.map
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js
new file mode 100644
index 0000000..9e161e2
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js
@@ -0,0 +1,14 @@
+/*
+ AngularJS v1.3.0-beta.5
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){k.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=b;h.$emit("$viewContentLoaded");h.$eval(u)}else y()}
+var h,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){},
+{prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;k.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b=
+"/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart",
+d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=h.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=
+b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(k,function(f,k){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h<p;++h){var n=q[h-1],r="string"==typeof g[h]?decodeURIComponent(g[h]):
+g[h];n&&r&&(l[n.name]=r)}q=l}else q=null;else q=null;q=a=q}q&&(b=s(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&s(k[null],{params:{},pathParams:{}})}function t(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var u=!1,r={routes:k,reload:function(){u=!0;a.$evalAsync(l)}};a.$on("$locationChangeSuccess",l);return r}]});n.provider("$routeParams",
+function(){this.$get=function(){return{}}});n.directive("ngView",x);n.directive("ngView",z);x.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
+//# sourceMappingURL=angular-route.min.js.map
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/jwt-decode.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/jwt-decode.min.js
new file mode 100644
index 0000000..f56f967
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/jwt-decode.min.js
@@ -0,0 +1 @@
+!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b){function c(a){return decodeURIComponent(atob(a).replace(/(.)/g,function(a,b){var c=b.charCodeAt(0).toString(16).toUpperCase();return c.length<2&&(c="0"+c),"%"+c}))}var d=a("Base64");b.exports=function(a){var b=a.replace(/-/g,"+").replace(/_/g,"/");switch(b.length%4){case 0:break;case 2:b+="==";break;case 3:b+="=";break;default:throw"Illegal base64url string!"}try{return c(b)}catch(e){return d.atob(b)}}},{Base64:4}],2:[function(a,b){"use strict";var c=a("./base64_url_decode"),d=a("./json_parse");b.exports=function(a){if(!a)throw new Error("Invalid token specified");return d(c(a.split(".")[1]))}},{"./base64_url_decode":1,"./json_parse":3}],3:[function(require,module,exports){module.exports=function(str){var parsed;return parsed="object"==typeof JSON?JSON.parse(str):eval("("+str+")")}},{}],4:[function(a,b,c){!function(){var a="undefined"!=typeof c?c:this,b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",d=function(){try{document.createElement("$")}catch(a){return a}}();a.btoa||(a.btoa=function(a){for(var c,e,f=0,g=b,h="";a.charAt(0|f)||(g="=",f%1);h+=g.charAt(63&c>>8-f%1*8)){if(e=a.charCodeAt(f+=.75),e>255)throw d;c=c<<8|e}return h}),a.atob||(a.atob=function(a){if(a=a.replace(/=+$/,""),a.length%4==1)throw d;for(var c,e,f=0,g=0,h="";e=a.charAt(g++);~e&&(c=f%4?64*c+e:e,f++%4)?h+=String.fromCharCode(255&c>>(-2*f&6)):0)e=b.indexOf(e);return h})}()},{}],5:[function(a){var b="undefined"!=typeof self?self:"undefined"!=typeof window?window:{},c=a("./lib/index");"function"==typeof b.window.define&&b.window.define.amd?b.window.define("jwt_decode",function(){return c}):b.window&&(b.window.jwt_decode=c)},{"./lib/index":2}]},{},[5]);
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html
new file mode 100644
index 0000000..bb381b9
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html
@@ -0,0 +1,19 @@
+<h1>All Albums</h1>
+<table class="table" data-ng-repeat="(key, value) in albums">
+    <thead>
+        <tr>
+            <th>{{key}}</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>
+                <ul>
+                    <li data-ng-repeat="p in value">
+                        <a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" ng-click="deleteAlbum(p)">X</a>]
+                    </li>
+                </ul>
+            </td>
+        </tr>
+    </tbody>
+</table>
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html
new file mode 100644
index 0000000..556693c
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html
@@ -0,0 +1,7 @@
+<h1>Create an Album</h1>
+
+<form>
+    Name: <input type="text" ng-model="album.name"/>
+
+    <button ng-click="create()">Save</button>
+</form>
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html
new file mode 100644
index 0000000..cf32df1
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html
@@ -0,0 +1 @@
+<h1>{{album.name}}</h1>
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html
new file mode 100644
index 0000000..5e164b2
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html
@@ -0,0 +1,22 @@
+<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span> [<a href="" ng-click="Identity.logout()">Sign Out</a>]</h2>
+<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album">All Albums</a>]</div>
+<hr/>
+<br/>
+<div data-ng-show="!Identity.isAdmin()">
+<a href="#/album/create">Create Album</a> | <a href="#/profile">My Profile</a>
+<br/>
+<br/>
+<span data-ng-show="albums.length == 0">You don't have any albums, yet.</span>
+<table class="table" data-ng-show="albums.length > 0">
+    <thead>
+        <tr>
+            <th>Your Albums</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr data-ng-repeat="p in albums">
+            <td><a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" ng-click="deleteAlbum(p)">X</a>]</td>
+        </tr>
+    </tbody>
+</table>
+</div>
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/profile.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/profile.html
new file mode 100644
index 0000000..c6f6750
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/profile.html
@@ -0,0 +1,6 @@
+<h1>My Profile</h1>
+
+<form>
+    <p>Name: {{profile.userName}}</p>
+    <p>Total of albums: {{profile.totalAlbums}}</p>
+</form>
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml b/examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a370557
--- /dev/null
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+	<module-name>photoz-html5-client</module-name>
+
+</web-app>
diff --git a/examples/authz/photoz/photoz-realm.json b/examples/authz/photoz/photoz-realm.json
new file mode 100644
index 0000000..342665e
--- /dev/null
+++ b/examples/authz/photoz/photoz-realm.json
@@ -0,0 +1,108 @@
+{
+  "realm": "photoz",
+  "enabled": true,
+  "sslRequired": "external",
+  "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+  "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "requiredCredentials": [
+    "password"
+  ],
+  "users": [
+    {
+      "username": "alice",
+      "enabled": true,
+      "email": "alice@keycloak.org",
+      "firstName": "Alice",
+      "lastName": "In Chains",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "alice"
+        }
+      ],
+      "realmRoles": [
+        "user", "uma_authorization"
+      ]
+    },
+    {
+      "username": "jdoe",
+      "enabled": true,
+      "email": "jdoe@keycloak.org",
+      "firstName": "John",
+      "lastName": "Doe",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "jdoe"
+        }
+      ],
+      "realmRoles": [
+        "user", "uma_authorization"
+      ]
+    },
+    {
+      "username": "admin",
+      "enabled": true,
+      "email": "admin@admin.com",
+      "firstName": "Admin",
+      "lastName": "Istrator",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "admin"
+        }
+      ],
+      "realmRoles": [
+        "user", "admin", "uma_authorization"
+      ],
+      "clientRoles": {
+        "realm-management": [
+          "realm-admin"
+        ]
+      }
+    },
+    {
+      "username": "service-account-photoz-restful-api",
+      "enabled": true,
+      "email": "service-account-photoz-restful-api@placeholder.org",
+      "serviceAccountClientId": "photoz-restful-api",
+      "clientRoles": {
+        "photoz-restful-api" : ["uma_protection"]
+      }
+    }
+  ],
+  "roles": {
+    "realm": [
+      {
+        "name": "user",
+        "description": "User privileges"
+      },
+      {
+        "name": "admin",
+        "description": "Administrator privileges"
+      }
+    ]
+  },
+  "clients": [
+    {
+      "clientId": "photoz-html5-client",
+      "enabled": true,
+      "adminUrl": "/photoz-html5-client",
+      "baseUrl": "/photoz-html5-client",
+      "publicClient": true,
+      "redirectUris": [
+        "/photoz-html5-client/*"
+      ]
+    },
+    {
+      "clientId": "photoz-restful-api",
+      "enabled": true,
+      "baseUrl": "/photoz-restful-api",
+      "authorizationServicesEnabled" : true,
+      "redirectUris": [
+        "/photoz-restful-api/*"
+      ],
+      "secret": "secret"
+    }
+  ]
+}
diff --git a/examples/authz/photoz/photoz-restful-api/pom.xml b/examples/authz/photoz/photoz-restful-api/pom.xml
new file mode 100755
index 0000000..eea3c17
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-photoz-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>photoz-restful-api</artifactId>
+    <packaging>war</packaging>
+
+    <name>Keycloak Authz: Photoz RESTful API</name>
+    <description>Photoz RESTful API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.ws.rs</groupId>
+            <artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+            <version>1.0.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.ejb</groupId>
+            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
+            <version>1.0.0.Final</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
new file mode 100644
index 0000000..b349e02
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -0,0 +1,62 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.example.photoz.admin;
+
+import org.keycloak.example.photoz.entity.Album;
+
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Path("/admin/album")
+@Stateless
+public class AdminAlbumService {
+
+    public static final String SCOPE_ADMIN_ALBUM_MANAGE = "urn:photoz.com:scopes:album:admin:manage";
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @Context
+    private HttpHeaders headers;
+
+    @GET
+    @Produces("application/json")
+    public Response findAll() {
+        HashMap<String, List<Album>> albums = new HashMap<>();
+        List<Album> result = this.entityManager.createQuery("from Album").getResultList();
+
+        for (Album album : result) {
+            albums.computeIfAbsent(album.getUserId(), key -> new ArrayList<>()).add(album);
+        }
+
+        return Response.ok(albums).build();
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
new file mode 100644
index 0000000..be2f1eb
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -0,0 +1,132 @@
+package org.keycloak.example.photoz.album;
+
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.example.photoz.ErrorResponse;
+import org.keycloak.example.photoz.entity.Album;
+
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Path("/album")
+@Stateless
+public class AlbumService {
+
+    public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
+    public static final String SCOPE_ALBUM_CREATE = "urn:photoz.com:scopes:album:create";
+    public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @POST
+    @Consumes("application/json")
+    public Response create(@Context HttpServletRequest request, Album newAlbum) {
+        Principal userPrincipal = request.getUserPrincipal();
+
+        newAlbum.setUserId(userPrincipal.getName());
+
+        Query queryDuplicatedAlbum = this.entityManager.createQuery("from Album where name = :name and userId = :userId");
+
+        queryDuplicatedAlbum.setParameter("name", newAlbum.getName());
+        queryDuplicatedAlbum.setParameter("userId", userPrincipal.getName());
+
+        if (!queryDuplicatedAlbum.getResultList().isEmpty()) {
+            throw new ErrorResponse("Name [" + newAlbum.getName() + "] already taken. Choose another one.", Status.CONFLICT);
+        }
+
+        this.entityManager.persist(newAlbum);
+
+        createProtectedResource(newAlbum);
+
+        return Response.ok(newAlbum).build();
+    }
+
+    @Path("{id}")
+    @DELETE
+    public Response delete(@PathParam("id") String id) {
+        Album album = this.entityManager.find(Album.class, Long.valueOf(id));
+
+        try {
+            deleteProtectedResource(album);
+            this.entityManager.remove(album);
+        } catch (Exception e) {
+            throw new RuntimeException("Could not delete album.", e);
+        }
+
+        return Response.ok().build();
+    }
+
+    @GET
+    @Produces("application/json")
+    public Response findAll(@Context HttpServletRequest request) {
+        return Response.ok(this.entityManager.createQuery("from Album where userId = '" + request.getUserPrincipal().getName() + "'").getResultList()).build();
+    }
+
+    @GET
+    @Path("{id}")
+    @Produces("application/json")
+    public Response findById(@PathParam("id") String id) {
+        List result = this.entityManager.createQuery("from Album where id = " + id).getResultList();
+
+        if (result.isEmpty()) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        return Response.ok(result.get(0)).build();
+    }
+
+    private void createProtectedResource(Album album) {
+        try {
+            HashSet<ScopeRepresentation> scopes = new HashSet<>();
+
+            scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
+            scopes.add(new ScopeRepresentation(SCOPE_ALBUM_CREATE));
+            scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE));
+
+            ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album");
+
+            albumResource.setOwner(album.getUserId());
+
+            AuthzClient.create().protection().resource().create(albumResource);
+        } catch (Exception e) {
+            throw new RuntimeException("Could not register protected resource.", e);
+        }
+    }
+
+    private void deleteProtectedResource(Album album) {
+        String uri = "/album/" + album.getId();
+
+        try {
+            ProtectionResource protection = AuthzClient.create().protection();
+            Set<String> search = protection.resource().findByFilter("uri=" + uri);
+
+            if (search.isEmpty()) {
+                throw new RuntimeException("Could not find protected resource with URI [" + uri + "]");
+            }
+
+            protection.resource().delete(search.iterator().next());
+        } catch (Exception e) {
+            throw new RuntimeException("Could not search protected resource.", e);
+        }
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
new file mode 100644
index 0000000..be638b6
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
@@ -0,0 +1,70 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.example.photoz.album;
+
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import java.security.Principal;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Path("/profile")
+@Stateless
+public class ProfileService {
+
+    private static final String PROFILE_VIEW = "urn:photoz.com:scopes:profile:view";
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @GET
+    @Produces("application/json")
+    public Response view(@Context HttpServletRequest request) {
+        Principal userPrincipal = request.getUserPrincipal();
+        List albums = this.entityManager.createQuery("from Album where userId = '" + userPrincipal.getName() + "'").getResultList();
+        return Response.ok(new Profile(userPrincipal.getName(), albums.size())).build();
+    }
+
+    public static class Profile {
+        private String userName;
+        private int totalAlbums;
+
+        public Profile(String name, int totalAlbums) {
+            this.userName = name;
+            this.totalAlbums = totalAlbums;
+        }
+
+        public String getUserName() {
+            return userName;
+        }
+
+        public int getTotalAlbums() {
+            return totalAlbums;
+        }
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
new file mode 100644
index 0000000..978bdea
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
@@ -0,0 +1,79 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.example.photoz.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+public class Album {
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Column(nullable = false)
+    private String name;
+
+    @OneToMany(mappedBy = "album", fetch = FetchType.EAGER)
+    private List<Photo> photos = new ArrayList<>();
+
+    @Column(nullable = false)
+    private String userId;
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public List<Photo> getPhotos() {
+        return this.photos;
+    }
+
+    public void setPhotos(final List<Photo> photos) {
+        this.photos = photos;
+    }
+
+    public void setUserId(final String userId) {
+        this.userId = userId;
+    }
+
+    public String getUserId() {
+        return this.userId;
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java
new file mode 100644
index 0000000..08b7495
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java
@@ -0,0 +1,81 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.example.photoz.entity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.ManyToOne;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+public class Photo {
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Column
+    private String name;
+
+    @ManyToOne
+    private Album album;
+
+    @Lob
+    @Column
+    @Basic(fetch = FetchType.LAZY)
+    private byte[] image;
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public Album getAlbum() {
+        return this.album;
+    }
+
+    public void setAlbum(final Album album) {
+        this.album = album;
+    }
+
+    public byte[] getImage() {
+        return this.image;
+    }
+
+    public void setImage(final byte[] image) {
+        this.image = image;
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java
new file mode 100644
index 0000000..51755d8
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java
@@ -0,0 +1,32 @@
+package org.keycloak.example.photoz;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ErrorResponse extends WebApplicationException {
+
+    private final Response.Status status;
+
+    public ErrorResponse(String message) {
+        this(message, Response.Status.INTERNAL_SERVER_ERROR);
+    }
+
+    public ErrorResponse(String message, Response.Status status) {
+        super(message, status);
+        this.status = status;
+    }
+
+    @Override
+    public Response getResponse() {
+        Map<String, String> errorResponse = new HashMap<>();
+
+        errorResponse.put("message", getMessage());
+
+        return Response.status(status).entity(errorResponse).build();
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java
new file mode 100644
index 0000000..5b8377c
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java
@@ -0,0 +1,12 @@
+package org.keycloak.example.photoz;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Basic auth app.
+ */
+@ApplicationPath("/")
+public class PhotozApplication extends Application {
+
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json
new file mode 100644
index 0000000..e728827
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm": "photoz",
+  "auth-server-url": "http://localhost:8080/auth",
+  "resource": "photoz-restful-api",
+  "credentials": {
+    "secret": "secret"
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..957dc8a
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="
+        http://java.sun.com/xml/ns/javaee 
+        http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+        
+</beans>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..9323182
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence version="2.0"
+	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+        http://java.sun.com/xml/ns/persistence
+        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
+	<persistence-unit name="primary">
+		<non-jta-data-source>java:jboss/datasources/PhotozDS</non-jta-data-source>
+
+		<class>org.keycloak.example.photoz.entity.Album</class>
+		<class>org.keycloak.example.photoz.entity.Photo</class>
+
+		<properties>
+			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+			<property name="hibernate.hbm2ddl.auto" value="update" />
+			<property name="hibernate.show_sql" value="false" />
+		</properties>
+	</persistence-unit>
+</persistence>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
new file mode 100644
index 0000000..4b23be6
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
@@ -0,0 +1,26 @@
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<jboss-deployment-structure>
+    <deployment>
+        <dependencies>
+            <module name="org.keycloak.keycloak-authz-client" services="import"/>
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
+
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..95fb58b
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,45 @@
+{
+  "realm": "photoz",
+  "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8080/auth",
+  "ssl-required": "external",
+  "resource": "photoz-restful-api",
+  "bearer-only" : true,
+  "credentials": {
+    "secret": "secret"
+  },
+  "policy-enforcer": {
+    "paths": [
+      {
+        "path" : "/album/*",
+        "methods" : [
+          {
+            "method": "GET",
+            "scopes" : ["urn:photoz.com:scopes:album:view"]
+          },
+          {
+            "method": "POST",
+            "scopes" : ["urn:photoz.com:scopes:album:create"]
+          }
+        ]
+      },
+      {
+        "name" : "Album Resource",
+        "path" : "/album/{id}",
+        "methods" : [
+          {
+            "method": "DELETE",
+            "scopes" : ["urn:photoz.com:scopes:album:delete"]
+          }
+        ]
+      },
+      {
+        "path" : "/profile"
+      },
+      {
+        "name" : "Admin Resources",
+        "path" : "/admin/*"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml
new file mode 100644
index 0000000..247448f
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml
@@ -0,0 +1,12 @@
+<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
+    <datasource jndi-name="java:jboss/datasources/PhotozDS" pool-name="PhotozDS" enabled="true" use-java-context="true">
+        <connection-url>jdbc:h2:${jboss.server.data.dir}/kc-authz-photo;AUTO_SERVER=TRUE</connection-url>
+        <driver>h2</driver>
+        <security>
+            <user-name>sa</user-name>
+            <password>sa</password>
+        </security>
+    </datasource>
+</datasources>
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..34cf6bd
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+	<module-name>photoz-restful-api</module-name>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>user</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>admin</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<login-config>
+		<auth-method>KEYCLOAK</auth-method>
+		<realm-name>photoz</realm-name>
+	</login-config>
+
+	<security-role>
+		<role-name>admin</role-name>
+	</security-role>
+
+	<security-role>
+		<role-name>user</role-name>
+	</security-role>
+</web-app>
diff --git a/examples/authz/photoz/photoz-restful-api-authz-service.json b/examples/authz/photoz/photoz-restful-api-authz-service.json
new file mode 100644
index 0000000..1d0356c
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api-authz-service.json
@@ -0,0 +1,183 @@
+{
+  "allowRemoteResourceManagement": true,
+  "policyEnforcementMode": "ENFORCING",
+  "resources": [
+    {
+      "name": "User Profile Resource",
+      "uri": "/profile",
+      "type": "http://photoz.com/profile",
+      "scopes": [
+        {
+          "name": "urn:photoz.com:scopes:profile:view"
+        }
+      ]
+    },
+    {
+      "name": "Album Resource",
+      "uri": "/album/*",
+      "type": "http://photoz.com/album",
+      "scopes": [
+        {
+          "name": "urn:photoz.com:scopes:album:view"
+        },
+        {
+          "name": "urn:photoz.com:scopes:album:create"
+        },
+        {
+          "name": "urn:photoz.com:scopes:album:delete"
+        }
+      ]
+    },
+    {
+      "name": "Admin Resources",
+      "uri": "/admin/*",
+      "type": "http://photoz.com/admin",
+      "scopes": [
+        {
+          "name": "urn:photoz.com:scopes:album:admin:manage"
+        }
+      ]
+    }
+  ],
+  "policies": [
+    {
+      "name": "Only Owner Policy",
+      "description": "Defines that only the resource owner is allowed to do something",
+      "type": "drools",
+      "config": {
+        "mavenArtifactVersion": "2.0.0.CR1-SNAPSHOT",
+        "mavenArtifactId": "photoz-authz-policy",
+        "sessionName": "MainOwnerSession",
+        "mavenArtifactGroupId": "org.keycloak",
+        "moduleName": "PhotozAuthzOwnerPolicy",
+        "scannerPeriod": "1",
+        "scannerPeriodUnit": "Hours"
+      }
+    },
+    {
+      "name": "Any Admin Policy",
+      "description": "Defines that adminsitrators can do something",
+      "type": "role",
+      "config": {
+        "roles": "[\"admin\"]"
+      }
+    },
+    {
+      "name": "Any User Policy",
+      "description": "Defines that any user can do something",
+      "type": "role",
+      "config": {
+        "roles": "[\"user\"]"
+      }
+    },
+    {
+      "name": "Only From a Specific Client Address",
+      "description": "Defines that only clients from a specific address can do something",
+      "type": "js",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n    $evaluation.grant();\n}"
+      }
+    },
+    {
+      "name": "Administration Policy",
+      "description": "Defines that only administrators from a specific network address can do something.",
+      "type": "aggregate",
+      "config": {
+        "applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
+      }
+    },
+    {
+      "name": "Only Owner and Administrators Policy",
+      "description": "Defines that only the resource owner and administrators can do something",
+      "type": "aggregate",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
+      }
+    },
+    {
+      "name": "Only From @keycloak.org or Admin",
+      "description": "Defines that only users from @keycloak.org",
+      "type": "js",
+      "config": {
+        "applyPolicies": "[]",
+        "code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n    $evaluation.grant();\n}"
+      }
+    },
+    {
+      "name": "Only in the Period",
+      "description": "Access granted only during the morning",
+      "type": "time",
+      "config": {
+        "noa": "2016-01-03 23:59:59",
+        "expirationUnit": "Minutes",
+        "nbf": "2016-01-01 00:00:00",
+        "expirationTime": "1"
+      }
+    },
+    {
+      "name": "Album Resource Permission",
+      "description": "General policies that apply to all album resources.",
+      "type": "resource",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "defaultResourceType": "http://photoz.com/album",
+        "default": "true",
+        "applyPolicies": "[\"Any User Policy\",\"Administration Policy\"]"
+      }
+    },
+    {
+      "name": "Admin Resource Permission",
+      "description": "General policy for any administrative resource.",
+      "type": "resource",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "defaultResourceType": "http://photoz.com/admin",
+        "default": "true",
+        "applyPolicies": "[\"Administration Policy\"]"
+      }
+    },
+    {
+      "name": "View User Permission",
+      "description": "Defines who is allowed to view an user profile",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "applyPolicies": "[\"Only From @keycloak.org or Admin\"]",
+        "scopes": "[\"urn:photoz.com:scopes:profile:view\"]"
+      }
+    },
+    {
+      "name": "Delete Album Policy",
+      "description": "A policy that only allows the owner to delete his albums.",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
+        "scopes": "[\"urn:photoz.com:scopes:album:delete\"]"
+      }
+    }
+  ],
+  "scopes": [
+    {
+      "name": "urn:photoz.com:scopes:profile:view"
+    },
+    {
+      "name": "urn:photoz.com:scopes:album:view"
+    },
+    {
+      "name": "urn:photoz.com:scopes:album:create"
+    },
+    {
+      "name": "urn:photoz.com:scopes:album:delete"
+    },
+    {
+      "name": "urn:photoz.com:scopes:album:admin:manage"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/photoz/pom.xml b/examples/authz/photoz/pom.xml
new file mode 100755
index 0000000..65259d8
--- /dev/null
+++ b/examples/authz/photoz/pom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-example-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-photoz-parent</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Keycloak Authz: PhotoZ  Example Application Parent</name>
+    <description>PhotoZ Example Application</description>
+
+    <modules>
+        <module>photoz-restful-api</module>
+        <module>photoz-html5-client</module>
+        <module>photoz-authz-policy</module>
+    </modules>
+</project>
diff --git a/examples/authz/photoz/README.md b/examples/authz/photoz/README.md
new file mode 100644
index 0000000..c915a1f
--- /dev/null
+++ b/examples/authz/photoz/README.md
@@ -0,0 +1,98 @@
+# About the Example Application
+
+This is a simple application based on HTML5+AngularJS+JAX-RS that will introduce you to some of the main concepts around Keycloak Authorization Services.
+
+Basically, it is a project containing three modules:
+ 
+* **photoz-restful-api**, a simple RESTFul API based on JAX-RS and acting as a resource server.
+* **photoz-html5-client**, a HTML5+AngularJS client that will consume the RESTful API published by a resource resourcer.
+* **photoz-authz-policy**, a simple project with some rule-based policies using JBoss Drools.
+
+For this application, users can be regular users or administrators. Regular users can create/view/delete their albums 
+and administrators can do anything.
+
+In Keycloak, albums are resources that must be protected based on a set of policies that defines who and how can access them.
+
+The resources are also associated with a set of scopes that defines a specific access context. In this case, albums have three main scopes:
+
+* urn:photoz.com:scopes:album:create
+* urn:photoz.com:scopes:album:view
+* urn:photoz.com:scopes:album:delete
+
+The authorization requirements for this example application are based on the following assumptions:
+
+* By default, any regular user can perform any operation on his resources.
+
+    * For instance, Alice can create, view and delete her albums. 
+
+* Only the owner and administrators can delete albums. Here we are considering policies based on the *urn:photoz.com:scopes:album:delete* scope
+
+    * For instance, only Alice can delete her album.
+
+* Only administrators can access the Administration API (which basically provides ways to query albums for all users)
+
+* Administrators are only authorized to access resources if the client's ip address is well known
+
+That said, this application will show you how to use the Keycloak to define policies using:
+
+* Role-based Access Control
+* Attribute-based Access Control
+* Rule-based policies using JBoss Drools
+* Rule-based policies using JavaScript 
+
+Beside that, this example demonstrates how to create resources dynamically and how to protected them using the *Protection API* and the *Authorization Client API*. Here you'll see
+how to create a resource whose owner is the authenticated user.
+
+It also provides some background on how you can actually protect your JAX-RS endpoints using a *policy enforcer*.
+
+## Create the Example Realm and a Resource Server
+
+Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console.
+
+Now, create a new realm based on the following configuration file:
+
+    examples/authz/photoz/photoz-realm.json
+    
+That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm 
+into Keycloak, check the Keycloak's reference documentation.
+
+After importing that file, you'll have a new realm called ``photoz``. 
+
+Back to the command-line, build the example application. This step is necessary given that we're using policies based on
+JBoss Drools, which require ``photoz-authz-policy`` artifact installed into your local maven repository.
+
+    cd examples/authz/photoz
+    mvn clean install 
+
+Now, let's import another configuration using the Administration Console in order to configure the client application ``photoz-restful-api`` as a resource server with all resources, scopes, permissions and policies.
+
+Click on ``Clients`` on the left side menu. Click on the ``photoz-restful-api`` on the client listing page. This will
+open the ``Client Details`` page. Once there, click on the `Authorization` tab. 
+
+Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at:
+
+    examples/authz/photoz/photoz-restful-api/photoz-restful-api-authz-config.json
+    
+Now click ``Upload`` and the resource server will be updated accordingly.
+
+## Deploy and Run the Example Applications
+
+To deploy the example applications, follow these steps:
+
+    cd examples/authz/photoz/photoz-html5-client
+    mvn clean package wildfly:deploy
+    
+And then:
+
+    cd examples/authz/photoz/photoz-restful-api
+    mvn clean package wildfly:deploy
+   
+Now, try to access the client application using the following URL:
+
+    http://localhost:8080/photoz-html5-client
+
+If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials:
+
+* username: jdoe / password: jdoe
+* username: alice / password: alice
+* username: admin / password: admin
\ No newline at end of file
diff --git a/examples/authz/pom.xml b/examples/authz/pom.xml
new file mode 100755
index 0000000..e9eb4eb
--- /dev/null
+++ b/examples/authz/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>keycloak-examples-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-authz-example-parent</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Keycloak Authz: Examples Parent</name>
+    <description/>
+
+    <properties>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+    </properties>
+
+    <modules>
+        <module>photoz</module>
+        <module>servlet-authz</module>
+        <module>hello-world</module>
+        <module>hello-world-authz-service</module>
+    </modules>
+</project>
diff --git a/examples/authz/servlet-authz/pom.xml b/examples/authz/servlet-authz/pom.xml
new file mode 100755
index 0000000..cfe82bc
--- /dev/null
+++ b/examples/authz/servlet-authz/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-authz-example-parent</artifactId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>servlet-authz</artifactId>
+    <packaging>war</packaging>
+
+    <name>Keycloak Authz: Examples - Servlet Authorization</name>
+    <description>Servlet Authorization</description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/authz/servlet-authz/README.md b/examples/authz/servlet-authz/README.md
new file mode 100644
index 0000000..f93acb5
--- /dev/null
+++ b/examples/authz/servlet-authz/README.md
@@ -0,0 +1,54 @@
+# About the Example Application
+
+This is a simple Servlet-based application that will introduce you to some of the main concepts around Keycloak Authorization Services.
+
+For this application, users can be regular users, premium users or administrators, where:
+
+* Regular users have very limited access.
+* Premium users have access to the *premium area*
+* Administrators have access to the *administration area*
+
+In Keycloak, all the paths being protected are resources on the server.
+
+This application will also show you how to create a dynamic menu with the permissions granted to an user.
+
+## Create the Example Realm and a Resource Server
+
+Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console.
+
+Now, create a new realm based on the following configuration file:
+
+    examples/authz/servlet-authz/servlet-authz-realm.json
+    
+That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm 
+into Keycloak, check the Keycloak's reference documentation.
+
+After importing that file, you'll have a new realm called ``servlet-authz``. 
+
+Now, let's import another configuration using the Administration Console in order to configure the client application ``servlet-authz-app`` as a resource server with all resources, scopes, permissions and policies.
+
+Click on ``Clients`` on the left side menu. Click on the ``servlet-authz-app`` on the client listing page. This will
+open the ``Client Details`` page. Once there, click on the `Authorization` tab. 
+
+Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at:
+
+    examples/authz/servlet-authz/servlet-authz-app-config.json
+    
+Now click ``Upload`` and the resource server will be updated accordingly.
+
+## Deploy and Run the Example Applications
+
+To deploy the example application, follow these steps:
+
+    cd examples/authz/servlet-authz
+    mvn clean package wildfly:deploy
+    
+Now, try to access the client application using the following URL:
+
+    http://localhost:8080/servlet-authz-app
+
+If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials:
+
+* username: jdoe / password: jdoe
+* username: alice / password: alice
+* username: admin / password: admin
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/servlet-authz-app-config.json b/examples/authz/servlet-authz/servlet-authz-app-config.json
new file mode 100644
index 0000000..d5fb1cb
--- /dev/null
+++ b/examples/authz/servlet-authz/servlet-authz-app-config.json
@@ -0,0 +1,147 @@
+{
+  "allowRemoteResourceManagement": true,
+  "policyEnforcementMode": "ENFORCING",
+  "resources": [
+    {
+      "name": "Admin Resource",
+      "uri": "/protected/admin/*",
+      "type": "http://servlet-authz/protected/admin",
+      "scopes": [
+        {
+          "name": "urn:servlet-authz:protected:admin:access"
+        }
+      ]
+    },
+    {
+      "name": "Protected Resource",
+      "uri": "/*",
+      "type": "http://servlet-authz/protected/resource",
+      "scopes": [
+        {
+          "name": "urn:servlet-authz:protected:resource:access"
+        }
+      ]
+    },
+    {
+      "name": "Premium Resource",
+      "uri": "/protected/premium/*",
+      "type": "urn:servlet-authz:protected:resource",
+      "scopes": [
+        {
+          "name": "urn:servlet-authz:protected:premium:access"
+        }
+      ]
+    },
+    {
+      "name": "Main Page",
+      "type": "urn:servlet-authz:protected:resource",
+      "scopes": [
+        {
+          "name": "urn:servlet-authz:page:main:actionForAdmin"
+        },
+        {
+          "name": "urn:servlet-authz:page:main:actionForUser"
+        },
+        {
+          "name": "urn:servlet-authz:page:main:actionForPremiumUser"
+        }
+      ]
+    }
+  ],
+  "policies": [
+    {
+      "name": "Any Admin Policy",
+      "description": "Defines that adminsitrators can do something",
+      "type": "role",
+      "config": {
+        "roles": "[\"admin\"]"
+      }
+    },
+    {
+      "name": "Any User Policy",
+      "description": "Defines that any user can do something",
+      "type": "role",
+      "config": {
+        "roles": "[\"user\"]"
+      }
+    },
+    {
+      "name": "Only Premium User Policy",
+      "description": "Defines that only premium users can do something",
+      "type": "role",
+      "logic": "POSITIVE",
+      "config": {
+        "roles": "[\"user_premium\"]"
+      }
+    },
+    {
+      "name": "All Users Policy",
+      "description": "Defines that all users can do something",
+      "type": "aggregate",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
+      }
+    },
+    {
+      "name": "Premium Resource Permission",
+      "description": "A policy that defines access to premium resources",
+      "type": "resource",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Premium Resource\"]",
+        "applyPolicies": "[\"Only Premium User Policy\"]"
+      }
+    },
+    {
+      "name": "Administrative Resource Permission",
+      "description": "A policy that defines access to administrative resources",
+      "type": "resource",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "resources": "[\"Admin Resource\"]",
+        "applyPolicies": "[\"Any Admin Policy\"]"
+      }
+    },
+    {
+      "name": "Protected Resource Permission",
+      "description": "A policy that defines access to any protected resource",
+      "type": "resource",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "resources": "[\"Protected Resource\"]",
+        "applyPolicies": "[\"All Users Policy\"]"
+      }
+    },
+    {
+      "name": "Action 1 on Main Page Resource Permission",
+      "description": "A policy that defines access to action 1 on the main page",
+      "type": "scope",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "scopes": "[\"urn:servlet-authz:page:main:actionForAdmin\"]",
+        "applyPolicies": "[\"Any Admin Policy\"]"
+      }
+    },
+    {
+      "name": "Action 2 on Main Page Resource Permission",
+      "description": "A policy that defines access to action 2 on the main page",
+      "type": "scope",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "scopes": "[\"urn:servlet-authz:page:main:actionForUser\"]",
+        "applyPolicies": "[\"Any User Policy\"]"
+      }
+    },
+    {
+      "name": "Action 3 on Main Page Resource Permission",
+      "description": "A policy that defines access to action 3 on the main page",
+      "type": "scope",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "scopes": "[\"urn:servlet-authz:page:main:actionForPremiumUser\"]",
+        "applyPolicies": "[\"Only Premium User Policy\"]"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/servlet-authz-realm.json b/examples/authz/servlet-authz/servlet-authz-realm.json
new file mode 100644
index 0000000..371e451
--- /dev/null
+++ b/examples/authz/servlet-authz/servlet-authz-realm.json
@@ -0,0 +1,95 @@
+{
+  "realm": "servlet-authz",
+  "enabled": true,
+  "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+  "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "requiredCredentials": [
+    "password"
+  ],
+  "users": [
+    {
+      "username": "alice",
+      "enabled": true,
+      "credentials": [
+        {
+          "type": "password",
+          "value": "alice"
+        }
+      ],
+      "realmRoles": [
+        "user"
+      ]
+    },
+    {
+      "username": "jdoe",
+      "enabled": true,
+      "credentials": [
+        {
+          "type": "password",
+          "value": "jdoe"
+        }
+      ],
+      "realmRoles": [
+        "user",
+        "user_premium"
+      ]
+    },
+    {
+      "username": "admin",
+      "enabled": true,
+      "credentials": [
+        {
+          "type": "password",
+          "value": "admin"
+        }
+      ],
+      "realmRoles": [
+        "user",
+        "admin"
+      ],
+      "clientRoles": {
+        "realm-management": [
+          "realm-admin"
+        ]
+      }
+    },
+    {
+      "username": "service-account-servlet-authz-app",
+      "enabled": true,
+      "serviceAccountClientId": "servlet-authz-app",
+      "clientRoles": {
+        "servlet-authz-app" : ["uma_protection"]
+      }
+    }
+  ],
+  "roles": {
+    "realm": [
+      {
+        "name": "user",
+        "description": "User privileges"
+      },
+      {
+        "name": "admin",
+        "description": "Administrator privileges"
+      },
+      {
+        "name": "user_premium",
+        "description": "User Premium privileges"
+      }
+    ]
+  },
+  "clients": [
+    {
+      "clientId": "servlet-authz-app",
+      "enabled": true,
+      "baseUrl": "/servlet-authz-app",
+      "adminUrl": "/servlet-authz-app",
+      "bearerOnly": false,
+      "authorizationServicesEnabled": true,
+      "redirectUris": [
+        "/servlet-authz-app/*"
+      ],
+      "secret": "secret"
+    }
+  ]
+}
diff --git a/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp b/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp
new file mode 100644
index 0000000..6f25023
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp
@@ -0,0 +1,6 @@
+<html>
+    <body>
+        <h2 style="color: red">You can not access this resource.</h2>
+        <%@include file="logout-include.jsp"%>
+    </body>
+</html>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/index.jsp b/examples/authz/servlet-authz/src/main/webapp/index.jsp
new file mode 100755
index 0000000..78c5444
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/index.jsp
@@ -0,0 +1,35 @@
+<%@page import="org.keycloak.AuthorizationContext" %>
+<%@ page import="org.keycloak.KeycloakSecurityContext" %>
+<%@ page import="org.keycloak.representations.authorization.Permission" %>
+
+<%
+    KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
+    AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext();
+%>
+
+<html>
+<body>
+    <%@include file="logout-include.jsp"%>
+    <h2>This is a public resource. Try to access one of these <i>protected</i> resources:</h2>
+
+    <p><a href="protected/dynamicMenu.jsp">Dynamic Menu</a></p>
+    <p><a href="protected/premium/onlyPremium.jsp">User Premium</a></p>
+    <p><a href="protected/admin/onlyAdmin.jsp">Administration</a></p>
+
+    <h3>Your permissions are:</h3>
+
+    <ul>
+        <%
+            for (Permission permission : authzContext.getPermissions()) {
+        %>
+        <li>
+            <p>Resource: <%= permission.getResourceSetName() %></p>
+            <p>ID: <%= permission.getResourceSetId() %></p>
+            <p>Scopes: <%= permission.getScopes() %></p>
+        </li>
+        <%
+            }
+        %>
+    </ul>
+</body>
+</html>
diff --git a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp
new file mode 100644
index 0000000..95365ea
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp
@@ -0,0 +1,11 @@
+<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+<%
+    String scheme = request.getScheme();
+    String host = request.getServerName();
+    int port = request.getServerPort();
+    String contextPath = request.getContextPath();
+    String redirectUri = scheme + "://" + host + ":" + port + contextPath;
+%>
+<h2>Click <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+            .queryParam("redirect_uri", redirectUri).build("servlet-authz").toString()%>">here</a> to logout.</h2>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml b/examples/authz/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml
new file mode 100644
index 0000000..515ffa5
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml
@@ -0,0 +1,25 @@
+<!--
+  ~  Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~  and other contributors as indicated by the @author tags.
+  ~
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<jboss-deployment-structure>
+    <deployment>
+        <dependencies>
+            <module name="org.keycloak.keycloak-authz-client" services="import"/>
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp
new file mode 100644
index 0000000..5946cd6
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp
@@ -0,0 +1,6 @@
+<html>
+<body>
+    <h2>Only Administrators can access this page.</h2>
+    <%@include file="../../logout-include.jsp"%>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp
new file mode 100644
index 0000000..1473d22
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp
@@ -0,0 +1,48 @@
+<%@page import="org.keycloak.AuthorizationContext" %>
+<%@ page import="org.keycloak.KeycloakSecurityContext" %>
+
+<%
+    KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
+    AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext();
+%>
+
+<html>
+<body>
+<h2>Any authenticated user can access this page.</h2>
+<%@include file="../logout-include.jsp"%>
+
+<p>Here is a dynamic menu built from the permissions returned by the server:</p>
+
+<ul>
+    <%
+        if (authzContext.hasResourcePermission("Protected Resource")) {
+    %>
+    <li>
+        Do user thing
+    </li>
+    <%
+        }
+    %>
+
+    <%
+        if (authzContext.hasResourcePermission("Premium Resource")) {
+    %>
+    <li>
+        Do  user premium thing
+    </li>
+    <%
+        }
+    %>
+
+    <%
+        if (authzContext.hasPermission("Admin Resource", "urn:servlet-authz:protected:admin:access")) {
+    %>
+    <li>
+        Do administration thing
+    </li>
+    <%
+        }
+    %>
+</ul>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp
new file mode 100644
index 0000000..9244f9c
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp
@@ -0,0 +1,6 @@
+<html>
+<body>
+<h2>Only for premium users.</h2>
+<%@include file="../../logout-include.jsp"%>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..eaffea8
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,14 @@
+{
+  "realm": "servlet-authz",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8080/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"
+  }
+}
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/web.xml b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..14d0615
--- /dev/null
+++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+		 version="3.0">
+
+	<module-name>servlet-authz-app</module-name>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>user</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>All Resources</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>admin</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+	<login-config>
+		<auth-method>KEYCLOAK</auth-method>
+		<realm-name>servlet-authz</realm-name>
+	</login-config>
+
+	<security-role>
+		<role-name>admin</role-name>
+	</security-role>
+
+	<security-role>
+		<role-name>user</role-name>
+	</security-role>
+
+	<error-page>
+		<error-code>403</error-code>
+		<location>/accessDenied.jsp</location>
+	</error-page>
+
+</web-app>
diff --git a/examples/fuse/fuse-admin/README.md b/examples/fuse/fuse-admin/README.md
index bae96d9..050d709 100644
--- a/examples/fuse/fuse-admin/README.md
+++ b/examples/fuse/fuse-admin/README.md
@@ -70,15 +70,15 @@ may be still able to access MBeans remotely via HTTP (Hawtio). So make sure to p
 really protect JMX mbeans.
 
 
-SSH and JMX on JBoss Fuse 6.2 and Apache Karaf 3.0.3
-----------------------------------------------------
+SSH and JMX on JBoss Fuse 6.2 
+-----------------------------
 For SSH steps are very similar to above for 6.1. In JBoss Fuse 6.2 you may need to install `ssh` feature as it doesn't seem to be installed here by default.
 
 ```
 features:install ssh
 ```
 
-For JMX, the steps are similar like for Fuse 6.1, however there is more fine grained authorization for JMX access in Fuse 6.2 and Karaf 3.
+For JMX, the steps are similar like for Fuse 6.1, however there is more fine grained authorization for JMX access in Fuse 6.2.
 
 Actually if you login as user `admin`, you have very limited privileges without possibility to do much JMX operations as this user has just `admin` role, which is not allowed to do much in JMX.
 
diff --git a/examples/fuse/README.md b/examples/fuse/README.md
index 10232b9..edfba47 100644
--- a/examples/fuse/README.md
+++ b/examples/fuse/README.md
@@ -5,12 +5,15 @@ Currently Keycloak supports securing your web applications running inside [JBoss
 - Jetty8 adapter for both JBoss Fuse 6.2 and Apache Karaf 3, that include [Jetty8](http://eclipse.org/jetty/) server under the covers and Jetty is used for running various kinds of web applications
 - Jetty9 adapter for both JBoss Fuse 6.3 and Apache Karaf 4, that include [Jetty9](http://eclipse.org/jetty/) server under the covers and Jetty is used for running various kinds of web applications
 
+**WARNING:** Running your applications inside standalone Apache Karaf may work, however we are testing just with JBoss Fuse and not with standalone Karaf server. 
+So if you really want adapter on standalone Karaf server, it's up to you to figure exact steps to have it working.
+
 The Fuse example is slightly modified version of Keycloak base demo applications. The main difference among base demo is that for Fuse demo 
-are applications running on separate Fuse/Karaf server. Keycloak server is supposed to run separately on Wildfly.
+are applications running on separate Fuse server. Keycloak server is supposed to run separately on Wildfly.
 
-What is supported for Fuse/Karaf is:
-* Security for classic WAR applications deployed on Fuse/Karaf with [pax-war extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+War). 
-* Security for servlets deployed on Fuse/Karaf as OSGI services with [pax-whiteboard extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard).
+What is supported for Fuse is:
+* Security for classic WAR applications deployed on Fuse with [pax-war extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+War). 
+* Security for servlets deployed on Fuse as OSGI services with [pax-whiteboard extender](https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard).
 * Security for [Apache Camel](http://camel.apache.org/) Jetty endpoints running with [camel-jetty](http://camel.apache.org/jetty.html) component.
 * Security for [Apache CXF](http://cxf.apache.org/) endpoints running on their own separate [Jetty engine](http://cxf.apache.org/docs/jetty-configuration.html). 
 Supports both securing JAX-RS and JAX-WS endpoints.
@@ -27,7 +30,7 @@ The customer-app-fuse invokes the endpoint to get data
 * **cxf-jaxws** [Apache CXF](http://cxf.apache.org/) JAX-WS endpoint running on separate Jetty engine on [http://localhost:8282/PersonServiceCF](http://localhost:8282/PersonServiceCF). 
 The product-app-fuse invokes the endpoint to get data.
 
-Running of demo consists of 2 steps. First you need to run separate Keycloak server and then Fuse/Karaf server with the applications
+Running of demo consists of 2 steps. First you need to run separate Keycloak server and then Fuse server with the applications
 
 Base steps
 ----------
@@ -74,25 +77,6 @@ features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.9
 features:install keycloak-fuse-6.3-example
 ```
 
-Running demo on Apache Karaf 3.0.3
-----------------------------------
-
-Demo is using Apache camel and Apache CXF, which are not in standalone Karaf by default. So you will need to install feature repositories for both of them.
-Next step is to add feature repository for main set of Keycloak karaf features and for the demo. Once all feature URLs are added, you just need to install `keycloak-fuse-example` feature,
-which automatically installs all other needed stuff.
-
-Once you run Apache Karaf, you need to run these commands from Karaf console (Make sure to replace keycloak versions in the example with actual Keycloak version):
-
-```
-feature:repo-add mvn:org.apache.camel.karaf/apache-camel/2.15.1/xml/features
-feature:repo-add mvn:org.apache.cxf.karaf/apache-cxf/3.0.4/xml/features
-feature:repo-add mvn:org.keycloak/keycloak-osgi-features/1.9.4.Final/xml/features
-feature:repo-add mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.9.4.Final/xml/features
-feature:install keycloak-fuse-example
-```
-
-Now you can test example applications similarly like described for "JBoss Fuse 6.2" section.
-
 
 How to secure your own applications
 -----------------------------------

examples/pom.xml 1(+1 -0)

diff --git a/examples/pom.xml b/examples/pom.xml
index 3d55a42..622699e 100755
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -66,5 +66,6 @@
         <module>themes</module>
         <module>saml</module>
         <module>ldap</module>
+        <module>authz</module>
     </modules>
 </project>
diff --git a/examples/providers/authenticator/README.md b/examples/providers/authenticator/README.md
index 38f6983..1edf0ed 100755
--- a/examples/providers/authenticator/README.md
+++ b/examples/providers/authenticator/README.md
@@ -1,4 +1,4 @@
-Example User Federation Provider
+Example Custom Authenticator
 ===================================================
 
 This is an example of defining a custom Authenticator and Required action.  This example is explained in the user documentation
diff --git a/examples/providers/domain-extension/invoke-authenticated.sh b/examples/providers/domain-extension/invoke-authenticated.sh
new file mode 100755
index 0000000..19b56b1
--- /dev/null
+++ b/examples/providers/domain-extension/invoke-authenticated.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+export DIRECT_GRANT_RESPONSE=$(curl -i --request POST http://localhost:8080/auth/realms/master/protocol/openid-connect/token --header "Accept: application/json" --header "Content-Type: application/x-www-form-urlencoded" --data "grant_type=password&username=admin&password=admin&client_id=admin-cli")
+
+echo -e "\n\nSENT RESOURCE-OWNER-PASSWORD-CREDENTIALS-REQUEST. OUTPUT IS:\n\n";
+echo $DIRECT_GRANT_RESPONSE;
+
+export ACCESS_TOKEN=$(echo $DIRECT_GRANT_RESPONSE | grep "access_token" | sed 's/.*\"access_token\":\"\([^\"]*\)\".*/\1/g');
+echo -e "\n\nACCESS TOKEN IS \"$ACCESS_TOKEN\"";
+
+echo -e "\n\nSENDING UN-AUTHENTICATED REQUEST. THIS SHOULD FAIL WITH 401: ";
+curl -i --request POST http://localhost:8080/auth/realms/master/example/companies-auth --data "{ \"name\": \"auth foo company\" }" --header "Content-type: application/json"
+
+echo -e "\n\nSENDING AUTHENTICATED REQUEST. THIS SHOULD SUCCESSFULY CREATE COMPANY AND SUCCESS WITH 201: ";
+curl -i --request POST http://localhost:8080/auth/realms/master/example/companies-auth --data "{ \"name\": \"auth foo company\" }" --header "Content-type: application/json" --header "Authorization: Bearer $ACCESS_TOKEN";
+
+echo -e "\n\nSEARCH COMPANIES: ";
+curl -i --request GET http://localhost:8080/auth/realms/master/example/companies-auth --header "Accept: application/json" --header "Authorization: Bearer $ACCESS_TOKEN";
+
diff --git a/examples/providers/domain-extension/README.md b/examples/providers/domain-extension/README.md
new file mode 100644
index 0000000..e1aa2cd
--- /dev/null
+++ b/examples/providers/domain-extension/README.md
@@ -0,0 +1,44 @@
+Example Domain Extension 
+========================
+
+To run, deploy as a module by running:
+
+    $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist"
+
+
+Then registering the provider by editing keycloak-server.json and adding the module to the providers field:
+
+    "providers": [
+        ....
+        "module:org.keycloak.examples.domain-extension-example"
+    ],
+
+Then start (or restart) the server.
+
+Testing
+-------
+First you can create some example companies with these CURL requests.
+
+````
+curl -i --request POST http://localhost:8080/auth/realms/master/example/companies --data "{ \"name\": \"foo company\" }" --header "Content-type: application/json"
+curl -i --request POST http://localhost:8080/auth/realms/master/example/companies --data "{ \"name\": \"bar company\" }" --header "Content-type: application/json"
+````
+
+Then you can lookup all companies 
+
+````
+curl -i --request GET http://localhost:8080/auth/realms/master/example/companies --header "Accept: application/json"
+````
+
+If you create realm `foo` in Keycloak admin console and then replace the realm name in the URI (for example like `http://localhost:8080/auth/realms/foo/example/companies` ) you will see
+that companies are scoped per-realm. So you will see different companies for realm `master` and for realm `foo` .
+
+
+Testing with authenticated access
+---------------------------------
+Example contains the endpoint, which is accessible just for authenticated users. REST request must be authenticated with bearer access token
+of authenticated user and the user must be in realm role `admin` in order to access the resource. You can run bash script from the current directory:
+````
+./invoke-authenticated.sh
+````
+The script assumes user `admin` with password `admin` exists in realm `master`. Also it assumes that you have `curl` installed.
\ No newline at end of file
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/CompanyRepresentation.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/CompanyRepresentation.java
new file mode 100644
index 0000000..2b76d57
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/CompanyRepresentation.java
@@ -0,0 +1,33 @@
+package org.keycloak.examples.domainextension;
+
+import org.keycloak.examples.domainextension.jpa.Company;
+
+public class CompanyRepresentation {
+
+    private String id;
+    private String name;
+
+    public CompanyRepresentation() {
+    }
+
+    public CompanyRepresentation(Company company) {
+        id = company.getId();
+        name = company.getName();
+    }
+    
+    public String getId() {
+		return id;
+	}
+    
+    public String getName() {
+		return name;
+	}
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/Company.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/Company.java
new file mode 100644
index 0000000..bca8e1d
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/Company.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.jpa;
+
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "EXAMPLE_COMPANY")
+@NamedQueries({ @NamedQuery(name = "findByRealm", query = "from Company where realmId = :realmId") })
+public class Company {
+
+    @Id
+    @Column(name = "ID")
+    private String id;
+
+    @Column(name = "NAME", nullable = false)
+    private String name;
+
+    @Column(name = "REALM_ID", nullable = false)
+    private String realmId;
+
+    public String getId() {
+		return id;
+	}
+    
+    public String getRealmId() {
+        return realmId;
+    }
+    
+    public String getName() {
+		return name;
+	}
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProvider.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProvider.java
new file mode 100644
index 0000000..b6529fd
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.jpa;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Example JpaEntityProvider.
+ */
+public class ExampleJpaEntityProvider implements JpaEntityProvider {
+
+    @Override
+    public List<Class<?>> getEntities() {
+        return Collections.<Class<?>>singletonList(Company.class);
+    }
+
+    @Override
+    public String getChangelogLocation() {
+    	return "META-INF/example-changelog.xml";
+    }
+    
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getFactoryId() {
+        return ExampleJpaEntityProviderFactory.ID;
+    }
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProviderFactory.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProviderFactory.java
new file mode 100644
index 0000000..2c919f4
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/jpa/ExampleJpaEntityProviderFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.jpa;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Example JpaEntityProviderFactory.
+ */
+public class ExampleJpaEntityProviderFactory implements JpaEntityProviderFactory {
+
+	protected static final String ID = "example-entity-provider";
+	
+    @Override
+    public JpaEntityProvider create(KeycloakSession session) {
+        return new ExampleJpaEntityProvider();
+    }
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public void init(Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/CompanyResource.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/CompanyResource.java
new file mode 100644
index 0000000..ba98978
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/CompanyResource.java
@@ -0,0 +1,52 @@
+package org.keycloak.examples.domainextension.rest;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.examples.domainextension.CompanyRepresentation;
+import org.keycloak.examples.domainextension.spi.ExampleService;
+import org.keycloak.models.KeycloakSession;
+
+public class CompanyResource {
+
+	private final KeycloakSession session;
+	
+	public CompanyResource(KeycloakSession session) {
+		this.session = session;
+	}
+
+    @GET
+    @Path("")
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<CompanyRepresentation> getCompanies() {
+        return session.getProvider(ExampleService.class).listCompanies();
+    }
+
+    @POST
+    @Path("")
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createCompany(CompanyRepresentation rep) {
+        session.getProvider(ExampleService.class).addCompany(rep);
+        return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(rep.getId()).build()).build();
+    }
+
+    @GET
+    @NoCache
+    @Path("{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public CompanyRepresentation getCompany(@PathParam("id") final String id) {
+        return session.getProvider(ExampleService.class).findCompany(id);
+    }
+
+}
\ No newline at end of file
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProvider.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProvider.java
new file mode 100644
index 0000000..0882d22
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.rest;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.resource.RealmResourceProvider;
+
+public class ExampleRealmResourceProvider implements RealmResourceProvider {
+
+    private KeycloakSession session;
+
+    public ExampleRealmResourceProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @Override
+    public Object getResource() {
+        return new ExampleRestResource(session);
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProviderFactory.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProviderFactory.java
new file mode 100644
index 0000000..33c2ca2
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRealmResourceProviderFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.rest;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.services.resource.RealmResourceProvider;
+import org.keycloak.services.resource.RealmResourceProviderFactory;
+
+public class ExampleRealmResourceProviderFactory implements RealmResourceProviderFactory {
+
+    public static final String ID = "example";
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public RealmResourceProvider create(KeycloakSession session) {
+        return new ExampleRealmResourceProvider(session);
+    }
+
+    @Override
+    public void init(Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRestResource.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRestResource.java
new file mode 100644
index 0000000..db774cf
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/rest/ExampleRestResource.java
@@ -0,0 +1,43 @@
+package org.keycloak.examples.domainextension.rest;
+
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.Path;
+
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+
+public class ExampleRestResource {
+
+	private final KeycloakSession session;
+    private final AuthenticationManager.AuthResult auth;
+	
+	public ExampleRestResource(KeycloakSession session) {
+		this.session = session;
+        this.auth = new AppAuthManager().authenticateBearerToken(session, session.getContext().getRealm());
+	}
+	
+    @Path("companies")
+    public CompanyResource getCompanyResource() {
+        return new CompanyResource(session);
+    }
+
+    // Same like "companies" endpoint, but REST endpoint is authenticated with Bearer token and user must be in realm role "admin"
+    // Just for illustration purposes
+    @Path("companies-auth")
+    public CompanyResource getCompanyResourceAuthenticated() {
+        checkRealmAdmin();
+        return new CompanyResource(session);
+    }
+
+    private void checkRealmAdmin() {
+        if (auth == null) {
+            throw new NotAuthorizedException("Bearer");
+        } else if (auth.getToken().getRealmAccess() == null || !auth.getToken().getRealmAccess().isUserInRole("admin")) {
+            throw new ForbiddenException("Does not have realm admin role");
+        }
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleService.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleService.java
new file mode 100644
index 0000000..7f41327
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.spi;
+
+import java.util.List;
+
+import org.keycloak.examples.domainextension.CompanyRepresentation;
+import org.keycloak.provider.Provider;
+
+public interface ExampleService extends Provider {
+
+    List<CompanyRepresentation> listCompanies();
+
+    CompanyRepresentation findCompany(String id);
+
+    CompanyRepresentation addCompany(CompanyRepresentation company);
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleServiceProviderFactory.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleServiceProviderFactory.java
new file mode 100644
index 0000000..2c6a122
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleServiceProviderFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.spi;
+
+import org.keycloak.provider.ProviderFactory;
+
+public interface ExampleServiceProviderFactory extends ProviderFactory<ExampleService> {
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleSpi.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleSpi.java
new file mode 100644
index 0000000..811ec92
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/ExampleSpi.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.spi;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+public class ExampleSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "example";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return ExampleService.class;
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return ExampleServiceProviderFactory.class;
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceImpl.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceImpl.java
new file mode 100644
index 0000000..49cc228
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.spi.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.examples.domainextension.jpa.Company;
+import org.keycloak.examples.domainextension.CompanyRepresentation;
+import org.keycloak.examples.domainextension.spi.ExampleService;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+public class ExampleServiceImpl implements ExampleService {
+
+    private final KeycloakSession session;
+
+    public ExampleServiceImpl(KeycloakSession session) {
+        this.session = session;
+        if (getRealm() == null) {
+            throw new IllegalStateException("The service cannot accept a session without a realm in it's context.");
+        }
+    }
+
+    private EntityManager getEntityManager() {
+        return session.getProvider(JpaConnectionProvider.class).getEntityManager();
+    }
+
+    protected RealmModel getRealm() {
+        return session.getContext().getRealm();
+    }
+    
+    @Override
+    public List<CompanyRepresentation> listCompanies() {
+    	List<Company> companyEntities = getEntityManager().createNamedQuery("findByRealm", Company.class)
+                .setParameter("realmId", getRealm().getId())
+                .getResultList();
+
+        List<CompanyRepresentation> result = new LinkedList<>();
+        for (Company entity : companyEntities) {
+            result.add(new CompanyRepresentation(entity));
+        }
+        return result;
+    }
+    
+    @Override
+    public CompanyRepresentation findCompany(String id) {
+    	Company entity = getEntityManager().find(Company.class, id);
+        return entity==null ? null : new CompanyRepresentation(entity);
+    }
+    
+    @Override
+    public CompanyRepresentation addCompany(CompanyRepresentation company) {
+        Company entity = new Company();
+        String id = company.getId()==null ?  KeycloakModelUtils.generateId() : company.getId();
+        entity.setId(id);
+        entity.setName(company.getName());
+        entity.setRealmId(getRealm().getId());
+        getEntityManager().persist(entity);
+
+        company.setId(id);
+        return company;
+    }
+
+    public void close() {
+        // Nothing to do.
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java
new file mode 100644
index 0000000..e4e2ddf
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/java/org/keycloak/examples/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.examples.domainextension.spi.impl;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.examples.domainextension.spi.ExampleService;
+import org.keycloak.examples.domainextension.spi.ExampleServiceProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+public class ExampleServiceProviderFactoryImpl implements ExampleServiceProviderFactory {
+
+    @Override
+    public ExampleService create(KeycloakSession session) {
+        return new ExampleServiceImpl(session);
+    }
+
+    @Override
+    public void init(Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "exampleServiceImpl";
+    }
+
+}
diff --git a/examples/providers/domain-extension/src/main/resources/META-INF/example-changelog.xml b/examples/providers/domain-extension/src/main/resources/META-INF/example-changelog.xml
new file mode 100644
index 0000000..5edd719
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/resources/META-INF/example-changelog.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+    <changeSet author="erik.mulder@docdatapayments.com" id="example-1.0">
+
+        <createTable tableName="EXAMPLE_COMPANY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey
+            constraintName="PK_COMPANY"
+            tableName="EXAMPLE_COMPANY"
+            columnNames="ID"
+        />
+
+    </changeSet>
+    
+</databaseChangeLog>
diff --git a/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.examples.domainextension.spi.ExampleServiceProviderFactory b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.examples.domainextension.spi.ExampleServiceProviderFactory
new file mode 100644
index 0000000..57f9f89
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.examples.domainextension.spi.ExampleServiceProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.examples.domainextension.spi.impl.ExampleServiceProviderFactoryImpl
diff --git a/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..e013bbd
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.examples.domainextension.spi.ExampleSpi
diff --git a/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
new file mode 100644
index 0000000..ea81617
--- /dev/null
+++ b/examples/providers/domain-extension/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.examples.domainextension.rest.ExampleRealmResourceProviderFactory
\ No newline at end of file
diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml
index 9557807..7153c45 100755
--- a/examples/providers/pom.xml
+++ b/examples/providers/pom.xml
@@ -36,5 +36,6 @@
         <module>federation-provider</module>
         <module>authenticator</module>
         <module>rest</module>
+        <module>domain-extension</module>
     </modules>
 </project>
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AttackDetectionResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AttackDetectionResource.java
index 7888cad..ea77759 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AttackDetectionResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AttackDetectionResource.java
@@ -33,16 +33,16 @@ import java.util.Map;
 public interface AttackDetectionResource {
 
     @GET
-    @Path("brute-force/usernames/{username}")
+    @Path("brute-force/users/{userId}")
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    Map<String, Object> bruteForceUserStatus(@PathParam("username") String username);
+    Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId);
 
-    @Path("brute-force/usernames/{username}")
+    @Path("brute-force/users/{userId}")
     @DELETE
-    void clearBruteForceForUser(@PathParam("username") String username);
+    void clearBruteForceForUser(@PathParam("userId") String userId);
 
-    @Path("brute-force/usernames")
+    @Path("brute-force/users")
     @DELETE
     void clearAllBruteForce();
 
diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
index ded6c3c..c9311bd 100755
--- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
@@ -21,6 +21,7 @@ import org.infinispan.configuration.cache.CacheMode;
 import org.infinispan.configuration.cache.Configuration;
 import org.infinispan.configuration.cache.ConfigurationBuilder;
 import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.eviction.EvictionType;
 import org.infinispan.manager.DefaultCacheManager;
 import org.infinispan.manager.EmbeddedCacheManager;
 import org.infinispan.transaction.LockingMode;
@@ -164,6 +165,9 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
         Configuration counterCacheConfiguration = counterConfigBuilder.build();
 
         cacheManager.defineConfiguration(InfinispanConnectionProvider.VERSION_CACHE_NAME, counterCacheConfiguration);
+
+        cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME,
+                new ConfigurationBuilder().eviction().type(EvictionType.COUNT).size(100).simpleCache(true).build());
     }
 
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java
index 8a21def..0fc2bc0 100755
--- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java
@@ -32,6 +32,7 @@ public interface InfinispanConnectionProvider extends Provider {
     static final String OFFLINE_SESSION_CACHE_NAME = "offlineSessions";
     static final String LOGIN_FAILURE_CACHE_NAME = "loginFailures";
     static final String WORK_CACHE_NAME = "work";
+    String AUTHORIZATION_CACHE_NAME = "authorization";
 
     <K, V> Cache<K, V> getCache(String name);
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
new file mode 100644
index 0000000..f1855d3
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
@@ -0,0 +1,407 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.infinispan.Cache;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
+import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedPolicyStore implements PolicyStore {
+
+    private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
+
+    private final Cache<String, List> cache;
+    private final KeycloakSession session;
+    private final CacheTransaction transaction;
+    private StoreFactory storeFactory;
+    private PolicyStore delegate;
+
+    public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction) {
+        this.session = session;
+        this.transaction = transaction;
+        InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
+        this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+    }
+
+    @Override
+    public Policy create(String name, String type, ResourceServer resourceServer) {
+        Policy policy = getDelegate().create(name, type, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
+
+        return createAdapter(new CachedPolicy(policy));
+    }
+
+    @Override
+    public void delete(String id) {
+        getDelegate().delete(id);
+        this.transaction.whenComplete(() -> cache.remove(id));
+    }
+
+    @Override
+    public Policy findById(String id) {
+        String cacheKeyForPolicy = getCacheKeyForPolicy(id);
+        List<CachedPolicy> cached = this.cache.get(cacheKeyForPolicy);
+
+        if (cached == null) {
+            Policy policy = getDelegate().findById(id);
+
+            if (policy != null) {
+                return createAdapter(updatePolicyCache(policy));
+            }
+
+            return null;
+        }
+
+        return createAdapter(cached.get(0));
+    }
+
+    @Override
+    public Policy findByName(String name, String resourceServerId) {
+        return getDelegate().findByName(name, resourceServerId);
+    }
+
+    @Override
+    public List<Policy> findByResourceServer(String resourceServerId) {
+        return getDelegate().findByResourceServer(resourceServerId);
+    }
+
+    @Override
+    public List<Policy> findByResource(String resourceId) {
+        List<Policy> cache = new ArrayList<>();
+
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
+                List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
+                CachedPolicy policy = value.get(0);
+
+                if (policy.getResourcesIds().contains(resourceId)) {
+                    cache.add(findById(policy.getId()));
+                }
+            }
+        }
+
+        if (cache.isEmpty()) {
+            getDelegate().findByResource(resourceId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
+        }
+
+        return cache;
+    }
+
+    @Override
+    public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
+        List<Policy> cache = new ArrayList<>();
+
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
+                List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
+                CachedPolicy policy = value.get(0);
+
+                if (policy.getResourceServerId().equals(resourceServerId) && policy.getConfig().getOrDefault("defaultResourceType", "").equals(resourceType)) {
+                    cache.add(findById(policy.getId()));
+                }
+            }
+        }
+
+        if (cache.isEmpty()) {
+            getDelegate().findByResourceType(resourceType, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
+        }
+
+        return cache;
+    }
+
+    @Override
+    public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
+        List<Policy> cache = new ArrayList<>();
+
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
+                List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
+                CachedPolicy policy = value.get(0);
+
+                for (String scopeId : policy.getScopesIds()) {
+                    if (scopeIds.contains(scopeId)) {
+                        cache.add(findById(policy.getId()));
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (cache.isEmpty()) {
+            getDelegate().findByScopeIds(scopeIds, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
+        }
+
+        return cache;
+    }
+
+    @Override
+    public List<Policy> findByType(String type) {
+        return getDelegate().findByType(type);
+    }
+
+    @Override
+    public List<Policy> findDependentPolicies(String id) {
+        return getDelegate().findDependentPolicies(id);
+    }
+
+    private String getCacheKeyForPolicy(String policyId) {
+        return POLICY_ID_CACHE_PREFIX + policyId;
+    }
+
+    private StoreFactory getStoreFactory() {
+        if (this.storeFactory == null) {
+            this.storeFactory = this.session.getProvider(StoreFactory.class);
+        }
+
+        return this.storeFactory;
+    }
+
+    private PolicyStore getDelegate() {
+        if (this.delegate == null) {
+            this.delegate = getStoreFactory().getPolicyStore();
+        }
+
+        return this.delegate;
+    }
+
+    private Policy createAdapter(CachedPolicy cached) {
+        return new Policy() {
+
+            private Policy updated;
+
+            @Override
+            public String getId() {
+                return cached.getId();
+            }
+
+            @Override
+            public String getType() {
+                return cached.getType();
+            }
+
+            @Override
+            public DecisionStrategy getDecisionStrategy() {
+                return cached.getDecisionStrategy();
+            }
+
+            @Override
+            public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+                getDelegateForUpdate().setDecisionStrategy(decisionStrategy);
+                cached.setDecisionStrategy(decisionStrategy);
+            }
+
+            @Override
+            public Logic getLogic() {
+                return cached.getLogic();
+            }
+
+            @Override
+            public void setLogic(Logic logic) {
+                getDelegateForUpdate().setLogic(logic);
+                cached.setLogic(logic);
+            }
+
+            @Override
+            public Map<String, String> getConfig() {
+                return cached.getConfig();
+            }
+
+            @Override
+            public void setConfig(Map<String, String> config) {
+                getDelegateForUpdate().setConfig(config);
+                cached.setConfig(config);
+            }
+
+            @Override
+            public String getName() {
+                return cached.getName();
+            }
+
+            @Override
+            public void setName(String name) {
+                getDelegateForUpdate().setName(name);
+                cached.setName(name);
+            }
+
+            @Override
+            public String getDescription() {
+                return cached.getDescription();
+            }
+
+            @Override
+            public void setDescription(String description) {
+                getDelegateForUpdate().setDescription(description);
+                cached.setDescription(description);
+            }
+
+            @Override
+            public ResourceServer getResourceServer() {
+                return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+            }
+
+            @Override
+            public void addScope(Scope scope) {
+                getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId()));
+                cached.addScope(scope);
+            }
+
+            @Override
+            public void removeScope(Scope scope) {
+                getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId()));
+                cached.removeScope(scope);
+            }
+
+            @Override
+            public void addAssociatedPolicy(Policy associatedPolicy) {
+                getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
+                cached.addAssociatedPolicy(associatedPolicy);
+            }
+
+            @Override
+            public void removeAssociatedPolicy(Policy associatedPolicy) {
+                getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
+                cached.removeAssociatedPolicy(associatedPolicy);
+            }
+
+            @Override
+            public void addResource(Resource resource) {
+                getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId()));
+                cached.addResource(resource);
+            }
+
+            @Override
+            public void removeResource(Resource resource) {
+                getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId()));
+                cached.removeResource(resource);
+            }
+
+            @Override
+            public Set<Policy> getAssociatedPolicies() {
+                Set<Policy> associatedPolicies = new HashSet<>();
+
+                for (String id : cached.getAssociatedPoliciesIds()) {
+                    Policy cached = findById(id);
+
+                    if (cached != null) {
+                        associatedPolicies.add(cached);
+                    }
+                }
+
+                return associatedPolicies;
+            }
+
+            @Override
+            public Set<Resource> getResources() {
+                Set<Resource> resources = new HashSet<>();
+
+                for (String id : cached.getResourcesIds()) {
+                    Resource cached = getStoreFactory().getResourceStore().findById(id);
+
+                    if (cached != null) {
+                        resources.add(cached);
+                    }
+                }
+
+                return resources;
+            }
+
+            @Override
+            public Set<Scope> getScopes() {
+                Set<Scope> scopes = new HashSet<>();
+
+                for (String id : cached.getScopesIds()) {
+                    Scope cached = getStoreFactory().getScopeStore().findById(id);
+
+                    if (cached != null) {
+                        scopes.add(cached);
+                    }
+                }
+
+                return scopes;
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (o == this) return true;
+
+                if (getId() == null) return false;
+
+                if (o == null || getClass() != o.getClass()) return false;
+
+                Policy that = (Policy) o;
+
+                if (!getId().equals(that.getId())) return false;
+
+                return true;
+
+            }
+
+            @Override
+            public int hashCode() {
+                return getId()!=null ? getId().hashCode() : super.hashCode();
+            }
+
+            private Policy getDelegateForUpdate() {
+                if (this.updated == null) {
+                    this.updated = getDelegate().findById(getId());
+                    if (this.updated == null) throw new IllegalStateException("Not found in database");
+                    transaction.whenComplete(() -> cache.evict(getCacheKeyForPolicy(getId())));
+                }
+
+                return this.updated;
+            }
+        };
+    }
+
+    private CachedPolicy updatePolicyCache(Policy policy) {
+        CachedPolicy cached = new CachedPolicy(policy);
+        List<Policy> cache = new ArrayList<>();
+
+        cache.add(cached);
+
+        this.cache.put(getCacheKeyForPolicy(policy.getId()), cache);
+
+        return cached;
+    }
+
+}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
new file mode 100644
index 0000000..5779ae1
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
@@ -0,0 +1,187 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.infinispan.Cache;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
+import org.keycloak.models.authorization.infinispan.entities.CachedResourceServer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedResourceServerStore implements ResourceServerStore {
+
+    private static final String RS_ID_CACHE_PREFIX = "rs-id-";
+
+    private final KeycloakSession session;
+    private final CacheTransaction transaction;
+    private StoreFactory storeFactory;
+    private ResourceServerStore delegate;
+    private final Cache<String, List> cache;
+
+    public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction) {
+        this.session = session;
+        this.transaction = transaction;
+        InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
+        this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+    }
+
+    @Override
+    public ResourceServer create(String clientId) {
+        ResourceServer resourceServer = getDelegate().create(clientId);
+
+        return createAdapter(new CachedResourceServer(resourceServer));
+    }
+
+    @Override
+    public void delete(String id) {
+        getDelegate().delete(id);
+        this.transaction.whenComplete(() -> this.cache.remove(getCacheKeyForResourceServer(id)));
+    }
+
+    @Override
+    public ResourceServer findById(String id) {
+        String cacheKeyForResourceServer = getCacheKeyForResourceServer(id);
+        List<ResourceServer> cached = this.cache.get(cacheKeyForResourceServer);
+
+        if (cached == null) {
+            ResourceServer resourceServer = getDelegate().findById(id);
+
+            if (resourceServer != null) {
+                return createAdapter(updateResourceServerCache(resourceServer));
+            }
+
+            return null;
+        }
+
+        return createAdapter(cached.get(0));
+    }
+
+    @Override
+    public ResourceServer findByClient(String id) {
+        for (Map.Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(RS_ID_CACHE_PREFIX)) {
+                List<ResourceServer> cache = (List<ResourceServer>) entry.getValue();
+                ResourceServer resourceServer = cache.get(0);
+
+                if (resourceServer.getClientId().equals(id)) {
+                    return findById(resourceServer.getId());
+                }
+            }
+        }
+
+        ResourceServer resourceServer = getDelegate().findByClient(id);
+
+        if (resourceServer != null) {
+            return findById(updateResourceServerCache(resourceServer).getId());
+        }
+
+        return null;
+    }
+
+    private String getCacheKeyForResourceServer(String id) {
+        return RS_ID_CACHE_PREFIX + id;
+    }
+
+    private ResourceServerStore getDelegate() {
+        if (this.delegate == null) {
+            this.delegate = getStoreFactory().getResourceServerStore();
+        }
+
+        return this.delegate;
+    }
+
+    private StoreFactory getStoreFactory() {
+        if (this.storeFactory == null) {
+            this.storeFactory = session.getProvider(StoreFactory.class);
+        }
+
+        return this.storeFactory;
+    }
+    private ResourceServer createAdapter(ResourceServer cached) {
+        return new ResourceServer() {
+
+            private ResourceServer updated;
+
+            @Override
+            public String getId() {
+                return cached.getId();
+            }
+
+            @Override
+            public String getClientId() {
+                return cached.getClientId();
+            }
+
+            @Override
+            public boolean isAllowRemoteResourceManagement() {
+                return cached.isAllowRemoteResourceManagement();
+            }
+
+            @Override
+            public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+                getDelegateForUpdate().setAllowRemoteResourceManagement(allowRemoteResourceManagement);
+                cached.setAllowRemoteResourceManagement(allowRemoteResourceManagement);
+            }
+
+            @Override
+            public PolicyEnforcementMode getPolicyEnforcementMode() {
+                return cached.getPolicyEnforcementMode();
+            }
+
+            @Override
+            public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
+                getDelegateForUpdate().setPolicyEnforcementMode(enforcementMode);
+                cached.setPolicyEnforcementMode(enforcementMode);
+            }
+
+            private ResourceServer getDelegateForUpdate() {
+                if (this.updated == null) {
+                    this.updated = getDelegate().findById(getId());
+                    if (this.updated == null) throw new IllegalStateException("Not found in database");
+                    transaction.whenComplete(() -> cache.evict(getCacheKeyForResourceServer(getId())));
+                }
+
+                return this.updated;
+            }
+        };
+    }
+
+    private CachedResourceServer updateResourceServerCache(ResourceServer resourceServer) {
+        CachedResourceServer cached = new CachedResourceServer(resourceServer);
+        List<ResourceServer> cache = new ArrayList<>();
+
+        cache.add(cached);
+
+        this.cache.put(getCacheKeyForResourceServer(resourceServer.getId()), cache);
+
+        return cached;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
new file mode 100644
index 0000000..9e1ea14
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
@@ -0,0 +1,299 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.infinispan.Cache;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
+import org.keycloak.models.authorization.infinispan.entities.CachedResource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedResourceStore implements ResourceStore {
+
+    private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
+
+    private final KeycloakSession session;
+    private final CacheTransaction transaction;
+    private StoreFactory storeFactory;
+    private ResourceStore delegate;
+    private final Cache<String, List> cache;
+
+    public CachedResourceStore(KeycloakSession session, CacheTransaction transaction) {
+        this.session = session;
+        InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
+        this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+        this.transaction = transaction;
+    }
+
+    @Override
+    public Resource create(String name, ResourceServer resourceServer, String owner) {
+        Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
+
+        return createAdapter(new CachedResource(resource));
+    }
+
+    @Override
+    public void delete(String id) {
+        this.cache.evict(getCacheKeyForResource(id));
+        getDelegate().delete(id);
+    }
+
+    @Override
+    public Resource findById(String id) {
+        String cacheKeyForResource = getCacheKeyForResource(id);
+        List<CachedResource> cached = this.cache.get(cacheKeyForResource);
+
+        if (cached == null) {
+            Resource resource = getDelegate().findById(id);
+
+            if (resource != null) {
+                return createAdapter(updateResourceCache(resource));
+            }
+
+            return null;
+        }
+
+        return createAdapter(cached.get(0));
+    }
+
+    @Override
+    public List<Resource> findByOwner(String ownerId) {
+        List<Resource> cache = new ArrayList<>();
+
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
+                List<Resource> value = (List<Resource>) entry.getValue();
+                Resource resource = value.get(0);
+
+                if (resource.getOwner().equals(ownerId)) {
+                    cache.add(findById(resource.getId()));
+                }
+            }
+        }
+
+        if (cache.isEmpty()) {
+            getDelegate().findByOwner(ownerId).forEach(resource -> cache.add(findById(updateResourceCache(resource).getId())));
+        }
+
+        return cache;
+    }
+
+    @Override
+    public List<Resource> findByResourceServer(String resourceServerId) {
+        return getDelegate().findByResourceServer(resourceServerId);
+    }
+
+    @Override
+    public List<Resource> findByScope(String... id) {
+        return getDelegate().findByScope(id);
+    }
+
+    @Override
+    public Resource findByName(String name, String resourceServerId) {
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
+                List<CachedResource> value = (List<CachedResource>) entry.getValue();
+                CachedResource resource = value.get(0);
+
+                if (resource.getResourceServerId().equals(resourceServerId) && resource.getName().equals(name)) {
+                    return findById(resource.getId());
+                }
+            }
+        }
+
+        Resource resource = getDelegate().findByName(name, resourceServerId);
+
+        if (resource != null) {
+            return findById(updateResourceCache(resource).getId());
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<Resource> findByType(String type) {
+        List<Resource> cache = new ArrayList<>();
+
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
+                List<Resource> value = (List<Resource>) entry.getValue();
+                Resource resource = value.get(0);
+
+                if (resource.getType().equals(type)) {
+                    cache.add(findById(resource.getId()));
+                }
+            }
+        }
+
+        if (cache.isEmpty()) {
+            getDelegate().findByType(type).forEach(resource -> cache.add(findById(updateResourceCache(resource).getId())));
+        }
+
+        return cache;
+    }
+
+    private String getCacheKeyForResource(String id) {
+        return RESOURCE_ID_CACHE_PREFIX + id;
+    }
+
+    private ResourceStore getDelegate() {
+        if (this.delegate == null) {
+            this.delegate = getStoreFactory().getResourceStore();
+        }
+
+        return this.delegate;
+    }
+
+    private StoreFactory getStoreFactory() {
+        if (this.storeFactory == null) {
+            this.storeFactory = session.getProvider(StoreFactory.class);
+        }
+
+        return this.storeFactory;
+    }
+
+    private Resource createAdapter(CachedResource cached) {
+        return new Resource() {
+
+            private List<Scope> scopes;
+            private Resource updated;
+
+            @Override
+            public String getId() {
+                return cached.getId();
+            }
+
+            @Override
+            public String getName() {
+                return cached.getName();
+            }
+
+            @Override
+            public void setName(String name) {
+                getDelegateForUpdate().setName(name);
+                cached.setName(name);
+            }
+
+            @Override
+            public String getUri() {
+                return cached.getUri();
+            }
+
+            @Override
+            public void setUri(String uri) {
+                getDelegateForUpdate().setUri(uri);
+                cached.setUri(uri);
+            }
+
+            @Override
+            public String getType() {
+                return cached.getType();
+            }
+
+            @Override
+            public void setType(String type) {
+                getDelegateForUpdate().setType(type);
+                cached.setType(type);
+            }
+
+            @Override
+            public List<Scope> getScopes() {
+                List<Scope> scopes = new ArrayList<>();
+
+                for (String id : cached.getScopesIds()) {
+                    Scope cached = getStoreFactory().getScopeStore().findById(id);
+
+                    if (cached != null) {
+                        scopes.add(cached);
+                    }
+                }
+
+                return scopes;
+            }
+
+            @Override
+            public String getIconUri() {
+                return cached.getIconUri();
+            }
+
+            @Override
+            public void setIconUri(String iconUri) {
+                getDelegateForUpdate().setIconUri(iconUri);
+                cached.setIconUri(iconUri);
+            }
+
+            @Override
+            public ResourceServer getResourceServer() {
+                return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+            }
+
+            @Override
+            public String getOwner() {
+                return cached.getOwner();
+            }
+
+            @Override
+            public void updateScopes(Set<Scope> scopes) {
+                getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId())).collect(Collectors.toSet()));
+                cached.updateScopes(scopes);
+            }
+
+            private Resource getDelegateForUpdate() {
+                if (this.updated == null) {
+                    this.updated = getDelegate().findById(getId());
+                    if (this.updated == null) throw new IllegalStateException("Not found in database");
+                    transaction.whenComplete(() -> cache.evict(getCacheKeyForResource(getId())));
+                }
+
+                return this.updated;
+            }
+        };
+    }
+
+    private CachedResource updateResourceCache(Resource resource) {
+        CachedResource cached = new CachedResource(resource);
+        List cache = new ArrayList<>();
+
+        cache.add(cached);
+
+        this.cache.put(getCacheKeyForResource(resource.getId()), cache);
+
+        return cached;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
new file mode 100644
index 0000000..5912645
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
@@ -0,0 +1,195 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.infinispan.Cache;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
+import org.keycloak.models.authorization.infinispan.entities.CachedScope;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedScopeStore implements ScopeStore {
+
+    private static final String SCOPE_ID_CACHE_PREFIX = "scp-id-";
+
+    private final Cache<String, List> cache;
+    private final KeycloakSession session;
+    private final CacheTransaction transaction;
+    private ScopeStore delegate;
+    private StoreFactory storeFactory;
+
+    public CachedScopeStore(KeycloakSession session, CacheTransaction transaction) {
+        this.session = session;
+        this.transaction = transaction;
+        InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
+        this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
+    }
+
+    @Override
+    public Scope create(String name, ResourceServer resourceServer) {
+        Scope scope = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
+
+        return createAdapter(new CachedScope(scope));
+    }
+
+    @Override
+    public void delete(String id) {
+        getDelegate().delete(id);
+        this.transaction.whenComplete(() -> cache.remove(getCacheKeyForScope(id)));
+    }
+
+    @Override
+    public Scope findById(String id) {
+        String cacheKeyForScope = getCacheKeyForScope(id);
+        List<CachedScope> cached = this.cache.get(cacheKeyForScope);
+
+        if (cached == null) {
+            Scope scope = getDelegate().findById(id);
+
+            if (scope != null) {
+                return createAdapter(updateScopeCache(scope));
+            }
+
+            return null;
+        }
+
+        return createAdapter(cached.get(0));
+    }
+
+    @Override
+    public Scope findByName(String name, String resourceServerId) {
+        for (Entry entry : this.cache.entrySet()) {
+            String cacheKey = (String) entry.getKey();
+
+            if (cacheKey.startsWith(SCOPE_ID_CACHE_PREFIX)) {
+                List<CachedScope> cache = (List<CachedScope>) entry.getValue();
+                CachedScope scope = cache.get(0);
+
+                if (scope.getResourceServerId().equals(resourceServerId) && scope.getName().equals(name)) {
+                    return findById(scope.getId());
+                }
+            }
+        }
+
+        Scope scope = getDelegate().findByName(name, resourceServerId);
+
+        if (scope != null) {
+            return findById(updateScopeCache(scope).getId());
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<Scope> findByResourceServer(String id) {
+        return getDelegate().findByResourceServer(id);
+    }
+
+    private String getCacheKeyForScope(String id) {
+        return SCOPE_ID_CACHE_PREFIX + id;
+    }
+
+    private ScopeStore getDelegate() {
+        if (this.delegate == null) {
+            this.delegate = getStoreFactory().getScopeStore();
+        }
+
+        return this.delegate;
+    }
+
+    private StoreFactory getStoreFactory() {
+        if (this.storeFactory == null) {
+            this.storeFactory = session.getProvider(StoreFactory.class);
+        }
+
+        return this.storeFactory;
+    }
+
+    private Scope createAdapter(CachedScope cached) {
+        return new Scope() {
+
+            private Scope updated;
+
+            @Override
+            public String getId() {
+                return cached.getId();
+            }
+
+            @Override
+            public String getName() {
+                return cached.getName();
+            }
+
+            @Override
+            public void setName(String name) {
+                getDelegateForUpdate().setName(name);
+                cached.setName(name);
+            }
+
+            @Override
+            public String getIconUri() {
+                return cached.getIconUri();
+            }
+
+            @Override
+            public void setIconUri(String iconUri) {
+                getDelegateForUpdate().setIconUri(iconUri);
+                cached.setIconUri(iconUri);
+            }
+
+            @Override
+            public ResourceServer getResourceServer() {
+                return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
+            }
+
+            private Scope getDelegateForUpdate() {
+                if (this.updated == null) {
+                    this.updated = getDelegate().findById(getId());
+                    if (this.updated == null) throw new IllegalStateException("Not found in database");
+                    transaction.whenComplete(() -> cache.evict(getCacheKeyForScope(getId())));
+                }
+
+                return this.updated;
+            }
+        };
+    }
+
+    private CachedScope updateScopeCache(Scope scope) {
+        CachedScope cached = new CachedScope(scope);
+
+        List cache = new ArrayList();
+
+        cache.add(cached);
+
+        this.transaction.whenComplete(() -> this.cache.put(getCacheKeyForScope(scope.getId()), cache));
+
+        return cached;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
new file mode 100644
index 0000000..6c6230b
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
@@ -0,0 +1,213 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan.entities;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedPolicy implements Policy {
+
+    private String id;
+    private String type;
+    private DecisionStrategy decisionStrategy;
+    private Logic logic;
+    private Map<String, String> config;
+    private String name;
+    private String description;
+    private String resourceServerId;
+    private Set<String> associatedPoliciesIds;
+    private Set<String> resourcesIds;
+    private Set<String> scopesIds;
+
+    public CachedPolicy(Policy policy) {
+        this.id = policy.getId();
+        this.type = policy.getType();
+        this.decisionStrategy = policy.getDecisionStrategy();
+        this.logic = policy.getLogic();
+        this.config = new HashMap(policy.getConfig());
+        this.name = policy.getName();
+        this.description = policy.getDescription();
+        this.resourceServerId = policy.getResourceServer().getId();
+        this.associatedPoliciesIds = policy.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toSet());
+        this.resourcesIds = policy.getResources().stream().map(Resource::getId).collect(Collectors.toSet());
+        this.scopesIds = policy.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
+    }
+
+    public CachedPolicy(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getType() {
+        return this.type;
+    }
+
+    @Override
+    public DecisionStrategy getDecisionStrategy() {
+        return this.decisionStrategy;
+    }
+
+    @Override
+    public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+        this.decisionStrategy = decisionStrategy;
+    }
+
+    @Override
+    public Logic getLogic() {
+        return this.logic;
+    }
+
+    @Override
+    public void setLogic(Logic logic) {
+        this.logic = logic;
+    }
+
+    @Override
+    public Map<String, String> getConfig() {
+        return this.config;
+    }
+
+    @Override
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getDescription() {
+        return this.description;
+    }
+
+    @Override
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public void addScope(Scope scope) {
+        this.scopesIds.add(scope.getId());
+    }
+
+    @Override
+    public void removeScope(Scope scope) {
+        this.scopesIds.remove(scope.getId());
+    }
+
+    @Override
+    public void addAssociatedPolicy(Policy associatedPolicy) {
+        this.associatedPoliciesIds.add(associatedPolicy.getId());
+    }
+
+    @Override
+    public void removeAssociatedPolicy(Policy associatedPolicy) {
+        this.associatedPoliciesIds.remove(associatedPolicy.getId());
+    }
+
+    @Override
+    public void addResource(Resource resource) {
+        this.resourcesIds.add(resource.getId());
+    }
+
+    @Override
+    public void removeResource(Resource resource) {
+        this.resourcesIds.add(resource.getId());
+    }
+
+    @Override
+    public Set<Policy> getAssociatedPolicies() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public Set<Resource> getResources() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public Set<Scope> getScopes() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    public Set<String> getAssociatedPoliciesIds() {
+        return this.associatedPoliciesIds;
+    }
+
+    public Set<String> getResourcesIds() {
+        return this.resourcesIds;
+    }
+
+    public Set<String> getScopesIds() {
+        return this.scopesIds;
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (this.id == null) return false;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractIdentifiableEntity that = (AbstractIdentifiableEntity) o;
+
+        if (!getId().equals(that.getId())) return false;
+
+        return true;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return id!=null ? id.hashCode() : super.hashCode();
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java
new file mode 100644
index 0000000..8af333b
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan.entities;
+
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedResource implements Resource {
+
+    private final String id;
+    private String resourceServerId;
+    private String iconUri;
+    private String owner;
+    private String type;
+    private String name;
+    private String uri;
+    private Set<String> scopesIds;
+
+    public CachedResource(Resource resource) {
+        this.id = resource.getId();
+        this.name = resource.getName();
+        this.uri = resource.getUri();
+        this.type = resource.getType();
+        this.owner = resource.getOwner();
+        this.iconUri = resource.getIconUri();
+        this.resourceServerId = resource.getResourceServer().getId();
+        this.scopesIds = resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
+    }
+
+    public CachedResource(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getUri() {
+        return this.uri;
+    }
+
+    @Override
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    @Override
+    public String getType() {
+        return this.type;
+    }
+
+    @Override
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @Override
+    public List<Scope> getScopes() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public String getOwner() {
+        return this.owner;
+    }
+
+    @Override
+    public void updateScopes(Set<Scope> scopes) {
+        this.scopesIds.clear();
+        this.scopesIds.addAll(scopes.stream().map(Scope::getId).collect(Collectors.toSet()));
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+
+    public Set<String> getScopesIds() {
+        return this.scopesIds;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java
new file mode 100644
index 0000000..fe59510
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan.entities;
+
+import org.keycloak.authorization.model.ResourceServer;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedResourceServer implements ResourceServer {
+
+    private final String id;
+    private String clientId;
+    private boolean allowRemoteResourceManagement;
+    private PolicyEnforcementMode policyEnforcementMode;
+
+    public CachedResourceServer(ResourceServer resourceServer) {
+        this.id = resourceServer.getId();
+        this.clientId = resourceServer.getClientId();
+        this.allowRemoteResourceManagement = resourceServer.isAllowRemoteResourceManagement();
+        this.policyEnforcementMode = resourceServer.getPolicyEnforcementMode();
+    }
+
+    public CachedResourceServer(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getClientId() {
+        return this.clientId;
+    }
+
+    @Override
+    public boolean isAllowRemoteResourceManagement() {
+        return this.allowRemoteResourceManagement;
+    }
+
+    @Override
+    public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+        this.allowRemoteResourceManagement = allowRemoteResourceManagement;
+    }
+
+    @Override
+    public PolicyEnforcementMode getPolicyEnforcementMode() {
+        return this.policyEnforcementMode;
+    }
+
+    @Override
+    public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
+        this.policyEnforcementMode = enforcementMode;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java
new file mode 100644
index 0000000..1cba6a2
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan.entities;
+
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedScope implements Scope {
+
+    private final String id;
+    private String resourceServerId;
+    private String name;
+    private String iconUri;
+
+    public CachedScope(Scope scope) {
+        this.id = scope.getId();
+        this.name = scope.getName();
+        this.iconUri = scope.getIconUri();
+        this.resourceServerId = scope.getResourceServer().getId();
+    }
+
+    public CachedScope(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        throw new RuntimeException("Not implemented");
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
new file mode 100644
index 0000000..df9f262
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvider {
+
+    private final KeycloakSession session;
+    private final CacheTransaction transaction;
+
+    InfinispanStoreFactoryProvider(KeycloakSession delegate) {
+        this.session = delegate;
+        this.transaction = new CacheTransaction();
+        this.session.getTransaction().enlistAfterCompletion(transaction);
+    }
+
+    @Override
+    public ResourceStore getResourceStore() {
+        return new CachedResourceStore(this.session, this.transaction);
+    }
+
+    @Override
+    public ResourceServerStore getResourceServerStore() {
+        return new CachedResourceServerStore(this.session, this.transaction);
+    }
+
+    @Override
+    public ScopeStore getScopeStore() {
+        return new CachedScopeStore(this.session, this.transaction);
+    }
+
+    @Override
+    public PolicyStore getPolicyStore() {
+        return new CachedPolicyStore(this.session, this.transaction);
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    static class CacheTransaction implements KeycloakTransaction {
+
+        private List<Runnable> completeTasks = new ArrayList<>();
+
+        @Override
+        public void begin() {
+
+        }
+
+        @Override
+        public void commit() {
+            this.completeTasks.forEach(task -> task.run());
+        }
+
+        @Override
+        public void rollback() {
+            this.completeTasks.forEach(task -> task.run());
+        }
+
+        @Override
+        public void setRollbackOnly() {
+
+        }
+
+        @Override
+        public boolean getRollbackOnly() {
+            return false;
+        }
+
+        @Override
+        public boolean isActive() {
+            return false;
+        }
+
+        protected void whenComplete(Runnable task) {
+            this.completeTasks.add(task);
+        }
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java
new file mode 100644
index 0000000..8015fc2
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.authorization.infinispan;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
+import org.keycloak.models.cache.authorization.CachedStoreProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class InfinispanStoreProviderFactory implements CachedStoreProviderFactory {
+    @Override
+    public CachedStoreFactoryProvider create(KeycloakSession session) {
+        return new InfinispanStoreFactoryProvider(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "infinispan-authz-store-factory";
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
index 65db3a8..d25f58b 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
@@ -24,19 +24,19 @@ import java.io.Serializable;
  */
 public class LoginFailureEntity implements Serializable {
 
-    private String username;
+    private String userId;
     private String realm;
     private int failedLoginNotBefore;
     private int numFailures;
     private long lastFailure;
     private String lastIPFailure;
 
-    public String getUsername() {
-        return username;
+    public String getUserId() {
+        return userId;
     }
 
-    public void setUsername(String username) {
-        this.username = username;
+    public void setUserId(String userId) {
+        this.userId = userId;
     }
 
     public String getRealm() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
index 21dc463..c452379 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
@@ -25,11 +25,11 @@ import java.io.Serializable;
 public class LoginFailureKey implements Serializable {
 
     private final String realm;
-    private final String username;
+    private final String userId;
 
-    public LoginFailureKey(String realm, String username) {
+    public LoginFailureKey(String realm, String userId) {
         this.realm = realm;
-        this.username = username;
+        this.userId = userId;
     }
 
     @Override
@@ -40,7 +40,7 @@ public class LoginFailureKey implements Serializable {
         LoginFailureKey key = (LoginFailureKey) o;
 
         if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
-        if (username != null ? !username.equals(key.username) : key.username != null) return false;
+        if (userId != null ? !userId.equals(key.userId) : key.userId != null) return false;
 
         return true;
     }
@@ -48,7 +48,7 @@ public class LoginFailureKey implements Serializable {
     @Override
     public int hashCode() {
         int result = realm != null ? realm.hashCode() : 0;
-        result = 31 * result + (username != null ? username.hashCode() : 0);
+        result = 31 * result + (userId != null ? userId.hashCode() : 0);
         return result;
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 10d6205..960ea25 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -30,7 +30,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.UserSessionProvider;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.models.session.UserSessionPersisterProvider;
 import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
 import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
@@ -377,24 +377,24 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
-        LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
+    public UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId) {
+        LoginFailureKey key = new LoginFailureKey(realm.getId(), userId);
         return wrap(key, loginFailureCache.get(key));
     }
 
     @Override
-    public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
-        LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
+    public UserLoginFailureModel addUserLoginFailure(RealmModel realm, String userId) {
+        LoginFailureKey key = new LoginFailureKey(realm.getId(), userId);
         LoginFailureEntity entity = new LoginFailureEntity();
         entity.setRealm(realm.getId());
-        entity.setUsername(username);
+        entity.setUserId(userId);
         tx.put(loginFailureCache, key, entity);
         return wrap(key, entity);
     }
 
     @Override
-    public void removeUserLoginFailure(RealmModel realm, String username) {
-        tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), username));
+    public void removeUserLoginFailure(RealmModel realm, String userId) {
+        tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), userId));
     }
 
     @Override
@@ -538,8 +538,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
 
-    UsernameLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
-        return entity != null ? new UsernameLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
+    UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
+        return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
     }
 
     List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
diff --git a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory
new file mode 100644
index 0000000..0a46bb3
--- /dev/null
+++ b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.authorization.CachedStoreProviderFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.models.authorization.infinispan.InfinispanStoreProviderFactory
\ No newline at end of file

model/jpa/pom.xml 10(+10 -0)

diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index c1176f3..e1c8742 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -30,6 +30,11 @@
     <name>Keycloak Model JPA</name>
     <description/>
 
+    <properties>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.bouncycastle</groupId>
@@ -97,6 +102,11 @@
             <artifactId>jackson-core</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
new file mode 100644
index 0000000..ddaf637
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
@@ -0,0 +1,251 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.entities;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+@Table(name = "RESOURCE_SERVER_POLICY", uniqueConstraints = {
+        @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"})
+})
+public class PolicyEntity implements Policy {
+
+    @Id
+    @Column(name="ID", length = 36)
+    @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    private String id;
+
+    @Column(name = "NAME")
+    private String name;
+
+    @Column(name = "DESCRIPTION")
+    private String description;
+
+    @Column(name = "TYPE")
+    private String type;
+
+    @Column(name = "DECISION_STRATEGY")
+    private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
+
+    @Column(name = "LOGIC")
+    private Logic logic = Logic.POSITIVE;
+
+    @ElementCollection
+    @MapKeyColumn(name="NAME")
+    @Column(name="VALUE", columnDefinition = "TEXT")
+    @CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") })
+    private Map<String, String> config = new HashMap();
+
+    @ManyToOne(optional = false)
+    @JoinColumn(name = "RESOURCE_SERVER_ID")
+    private ResourceServerEntity resourceServer;
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "ASSOCIATED_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "ASSOCIATED_POLICY_ID"))
+    private Set<PolicyEntity> associatedPolicies = new HashSet<>();
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "RESOURCE_ID"))
+    private Set<ResourceEntity> resources = new HashSet<>();
+
+    @ManyToMany(fetch = FetchType.EAGER, cascade = {})
+    @JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
+    private Set<ScopeEntity> scopes = new HashSet<>();
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getType() {
+        return this.type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @Override
+    public DecisionStrategy getDecisionStrategy() {
+        return this.decisionStrategy;
+    }
+
+    @Override
+    public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+        this.decisionStrategy = decisionStrategy;
+    }
+
+    @Override
+    public Logic getLogic() {
+        return this.logic;
+    }
+
+    @Override
+    public void setLogic(Logic logic) {
+        this.logic = logic;
+    }
+
+    @Override
+    public Map<String, String> getConfig() {
+        return this.config;
+    }
+
+    @Override
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getDescription() {
+        return this.description;
+    }
+
+    @Override
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public ResourceServerEntity getResourceServer() {
+        return this.resourceServer;
+    }
+
+    public void setResourceServer(ResourceServerEntity resourceServer) {
+        this.resourceServer = resourceServer;
+    }
+
+    @Override
+    public <P extends Policy> Set<P> getAssociatedPolicies() {
+        return (Set<P>) this.associatedPolicies;
+    }
+
+    public void setAssociatedPolicies(Set<PolicyEntity> associatedPolicies) {
+        this.associatedPolicies = associatedPolicies;
+    }
+
+    @Override
+    public Set<ResourceEntity> getResources() {
+        return this.resources;
+    }
+
+    public void setResources(Set<ResourceEntity> resources) {
+        this.resources = resources;
+    }
+
+    @Override
+    public Set<ScopeEntity> getScopes() {
+        return this.scopes;
+    }
+
+    public void setScopes(Set<ScopeEntity> scopes) {
+        this.scopes = scopes;
+    }
+
+    @Override
+    public void addScope(Scope scope) {
+        getScopes().add((ScopeEntity) scope);
+    }
+
+    @Override
+    public void removeScope(Scope scope) {
+        getScopes().remove(scope);
+    }
+
+    @Override
+    public void addAssociatedPolicy(Policy associatedPolicy) {
+        getAssociatedPolicies().add(associatedPolicy);
+    }
+
+    @Override
+    public void removeAssociatedPolicy(Policy associatedPolicy) {
+        getAssociatedPolicies().remove(associatedPolicy);
+    }
+
+    @Override
+    public void addResource(Resource resource) {
+        getResources().add((ResourceEntity) resource);
+    }
+
+    @Override
+    public void removeResource(Resource resource) {
+        getResources().remove(resource);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (this.id == null) return false;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractIdentifiableEntity that = (AbstractIdentifiableEntity) o;
+
+        if (!getId().equals(that.getId())) return false;
+
+        return true;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return id!=null ? id.hashCode() : super.hashCode();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
new file mode 100644
index 0000000..7cb1a6f
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
@@ -0,0 +1,190 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.entities;
+
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.Scope;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+@Table(name = "RESOURCE_SERVER_RESOURCE", uniqueConstraints = {
+        @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID", "OWNER"})
+})
+public class ResourceEntity implements Resource {
+
+    @Id
+    @Column(name="ID", length = 36)
+    @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    private String id;
+
+    @Column(name = "NAME")
+    private String name;
+
+    @Column(name = "URI")
+    private String uri;
+
+    @Column(name = "TYPE")
+    private String type;
+
+    @Column(name = "ICON_URI")
+    private String iconUri;
+
+    @Column(name = "OWNER")
+    private String owner;
+
+    @ManyToOne(optional = false)
+    @JoinColumn(name = "RESOURCE_SERVER_ID")
+    private ResourceServerEntity resourceServer;
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "RESOURCE_SCOPE", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
+    private List<ScopeEntity> scopes = new ArrayList<>();
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID"))
+    private List<PolicyEntity> policies = new ArrayList<>();
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getUri() {
+        return uri;
+    }
+
+    @Override
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @Override
+    public List<ScopeEntity> getScopes() {
+        return this.scopes;
+    }
+
+    @Override
+    public String getIconUri() {
+        return iconUri;
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    @Override
+    public ResourceServerEntity getResourceServer() {
+        return resourceServer;
+    }
+
+    public void setResourceServer(ResourceServerEntity resourceServer) {
+        this.resourceServer = resourceServer;
+    }
+
+    public String getOwner() {
+        return this.owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public List<PolicyEntity> getPolicies() {
+        return this.policies;
+    }
+
+    public void updateScopes(Set<Scope> toUpdate) {
+        for (Scope scope : toUpdate) {
+            boolean hasScope = false;
+
+            for (Scope existingScope : this.scopes) {
+                if (existingScope.equals(scope)) {
+                    hasScope = true;
+                }
+            }
+
+            if (!hasScope) {
+                this.scopes.add((ScopeEntity) scope);
+            }
+        }
+
+        for (Scope scopeModel : new HashSet<Scope>(this.scopes)) {
+            boolean hasScope = false;
+
+            for (Scope scope : toUpdate) {
+                if (scopeModel.equals(scope)) {
+                    hasScope = true;
+                }
+            }
+
+            if (!hasScope) {
+                this.scopes.remove(scopeModel);
+            }
+        }
+    }
+
+    public void setPolicies(List<PolicyEntity> policies) {
+        this.policies = policies;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java
new file mode 100644
index 0000000..b74b231
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java
@@ -0,0 +1,113 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.entities;
+
+import org.keycloak.authorization.model.ResourceServer;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+@Table(name = "RESOURCE_SERVER", uniqueConstraints = {@UniqueConstraint(columnNames = "CLIENT_ID")})
+public class ResourceServerEntity implements ResourceServer {
+
+    @Id
+    @Column(name="ID", length = 36)
+    @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    private String id;
+
+    @Column(name = "CLIENT_ID")
+    private String clientId;
+
+    @Column(name = "ALLOW_RS_REMOTE_MGMT")
+    private boolean allowRemoteResourceManagement;
+
+    @Column(name = "POLICY_ENFORCE_MODE")
+    private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
+
+    @OneToMany(mappedBy = "resourceServer")
+    private List<ResourceEntity> resources;
+
+    @OneToMany (mappedBy = "resourceServer")
+    private List<ScopeEntity> scopes;
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getClientId() {
+        return this.clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    @Override
+    public boolean isAllowRemoteResourceManagement() {
+        return this.allowRemoteResourceManagement;
+    }
+
+    @Override
+    public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+        this.allowRemoteResourceManagement = allowRemoteResourceManagement;
+    }
+
+    @Override
+    public PolicyEnforcementMode getPolicyEnforcementMode() {
+        return this.policyEnforcementMode;
+    }
+
+    @Override
+    public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
+        this.policyEnforcementMode = policyEnforcementMode;
+    }
+
+    public List<ResourceEntity> getResources() {
+        return this.resources;
+    }
+
+    public void setResources(final List<ResourceEntity> resources) {
+        this.resources = resources;
+    }
+
+    public List<ScopeEntity> getScopes() {
+        return this.scopes;
+    }
+
+    public void setScopes(final List<ScopeEntity> scopes) {
+        this.scopes = scopes;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java
new file mode 100644
index 0000000..99f8b41
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java
@@ -0,0 +1,126 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.entities;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Scope;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+@Table(name = "RESOURCE_SERVER_SCOPE", uniqueConstraints = {
+        @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"})
+})
+public class ScopeEntity implements Scope {
+
+    @Id
+    @Column(name="ID", length = 36)
+    @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    private String id;
+
+    @Column(name = "NAME")
+    private String name;
+
+    @Column(name = "ICON_URI")
+    private String iconUri;
+
+    @ManyToOne(optional = false)
+    @JoinColumn(name = "RESOURCE_SERVER_ID")
+    private ResourceServerEntity resourceServer;
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "SCOPE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID"))
+    private List<PolicyEntity> policies = new ArrayList<>();
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getIconUri() {
+        return iconUri;
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    @Override
+    public ResourceServerEntity getResourceServer() {
+        return resourceServer;
+    }
+
+    public List<? extends Policy> getPolicies() {
+        return this.policies;
+    }
+
+    public void setPolicies(List<PolicyEntity> policies) {
+        this.policies = policies;
+    }
+
+    public void setResourceServer(final ResourceServerEntity resourceServer) {
+        this.resourceServer = resourceServer;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ScopeEntity that = (ScopeEntity) o;
+        return Objects.equals(id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java
new file mode 100644
index 0000000..8788884
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.store.AuthorizationStoreFactory;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAAuthorizationStoreFactory implements AuthorizationStoreFactory {
+    @Override
+    public StoreFactory  create(KeycloakSession session) {
+        return new JPAStoreFactory(getEntityManager(session));
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "jpa";
+    }
+
+    private EntityManager getEntityManager(KeycloakSession session) {
+        return session.getProvider(JpaConnectionProvider.class).getEntityManager();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
new file mode 100644
index 0000000..8b88ad1
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.authorization.jpa.entities.PolicyEntity;
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAPolicyStore implements PolicyStore {
+
+    private final EntityManager entityManager;
+
+    public JPAPolicyStore(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    @Override
+    public Policy create(String name, String type, ResourceServer resourceServer) {
+        PolicyEntity entity = new PolicyEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setType(type);
+        entity.setResourceServer((ResourceServerEntity) resourceServer);
+
+        this.entityManager.persist(entity);
+
+        return entity;
+    }
+
+    public EntityManager getEntityManager() {
+        return this.entityManager;
+    }
+
+    @Override
+    public void delete(String id) {
+        Policy policy = findById(id);
+
+        if (policy != null) {
+            getEntityManager().remove(policy);
+        }
+    }
+
+
+    @Override
+    public Policy findById(String id) {
+        return getEntityManager().find(PolicyEntity.class, id);
+    }
+
+    @Override
+    public Policy findByName(String name, String resourceServerId) {
+        try {
+            Query query = getEntityManager().createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId");
+
+            query.setParameter("name", name);
+            query.setParameter("serverId", resourceServerId);
+
+            return (Policy) query.getSingleResult();
+        } catch (NoResultException nre) {
+            return null;
+        }
+    }
+
+    @Override
+    public List<Policy> findByResourceServer(final String resourceServerId) {
+        Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
+
+        query.setParameter("serverId", resourceServerId);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Policy> findByResource(final String resourceId) {
+        Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where r.id = :resourceId");
+
+        query.setParameter("resourceId", resourceId);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
+        List<Policy> policies = new ArrayList<>();
+        Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
+
+        query.setParameter("serverId", resourceServerId);
+
+        List<Policy> models = query.getResultList();
+
+        for (Policy policy : models) {
+            String defaultType = policy.getConfig().get("defaultResourceType");
+
+            if (defaultType != null && defaultType.equals(resourceType) && policy.getResources().isEmpty()) {
+                policies.add(policy);
+            }
+        }
+
+        return policies;
+    }
+
+    @Override
+    public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
+        if (scopeIds==null || scopeIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // Use separate subquery to handle DB2 and MSSSQL
+        Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.id IN (select p.id from PolicyEntity p inner join p.scopes s where p.resourceServer.id = :serverId and s.id in (:scopeIds) and p.resources is empty group by p.id) order by pe.name");
+
+        query.setParameter("serverId", resourceServerId);
+        query.setParameter("scopeIds", scopeIds);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Policy> findByType(String type) {
+        Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.type = :type");
+
+        query.setParameter("type", type);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Policy> findDependentPolicies(String policyId) {
+        Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where ap.id in (:policyId)");
+
+        query.setParameter("policyId", Arrays.asList(policyId));
+
+        return query.getResultList();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
new file mode 100644
index 0000000..51d0369
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAResourceServerStore implements ResourceServerStore {
+
+    private final EntityManager entityManager;
+
+    public JPAResourceServerStore(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    @Override
+    public ResourceServer create(String clientId) {
+        ResourceServerEntity entity = new ResourceServerEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setClientId(clientId);
+
+        this.entityManager.persist(entity);
+
+        return entity;
+    }
+
+    @Override
+    public void delete(String id) {
+        this.entityManager.remove(findById(id));
+    }
+
+    @Override
+    public ResourceServer findById(String id) {
+        return entityManager.find(ResourceServerEntity.class, id);
+    }
+
+    @Override
+    public ResourceServer findByClient(final String clientId) {
+        Query query = entityManager.createQuery("from ResourceServerEntity where clientId = :clientId");
+
+        query.setParameter("clientId", clientId);
+        List result = query.getResultList();
+
+        if (result.isEmpty()) {
+            return null;
+        }
+
+        return (ResourceServer) result.get(0);
+    }
+}
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
new file mode 100644
index 0000000..802989c
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -0,0 +1,132 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.authorization.jpa.entities.ResourceEntity;
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAResourceStore implements ResourceStore {
+
+    private final EntityManager entityManager;
+
+    public JPAResourceStore(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    @Override
+    public Resource create(String name, ResourceServer resourceServer, String owner) {
+        if (!(resourceServer instanceof ResourceServerEntity)) {
+            throw new RuntimeException("Unexpected type [" + resourceServer.getClass() + "].");
+        }
+
+        ResourceEntity entity = new ResourceEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setResourceServer((ResourceServerEntity) resourceServer);
+        entity.setOwner(owner);
+
+        this.entityManager.persist(entity);
+
+        return entity;
+    }
+
+    @Override
+    public void delete(String id) {
+        Resource resource = findById(id);
+
+        resource.getScopes().clear();
+
+        if (resource != null) {
+            this.entityManager.remove(resource);
+        }
+    }
+
+    @Override
+    public Resource findById(String id) {
+        if (id == null) {
+            return null;
+        }
+
+        return entityManager.find(ResourceEntity.class, id);
+    }
+
+    @Override
+    public List<Resource> findByOwner(String ownerId) {
+        Query query = entityManager.createQuery("from ResourceEntity where owner = :ownerId");
+
+        query.setParameter("ownerId", ownerId);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List findByResourceServer(String resourceServerId) {
+        Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId");
+
+        query.setParameter("serverId", resourceServerId);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<Resource> findByScope(String... id) {
+        Query query = entityManager.createQuery("from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
+
+        query.setParameter("scopeIds", Arrays.asList(id));
+
+        return query.getResultList();
+    }
+
+    @Override
+    public Resource findByName(String name, String resourceServerId) {
+        Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and name = :name");
+
+        query.setParameter("serverId", resourceServerId);
+        query.setParameter("name", name);
+
+        List<Resource> result = query.getResultList();
+
+        if (!result.isEmpty()) {
+            return result.get(0);
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<Resource> findByType(String type) {
+        Query query = entityManager.createQuery("from ResourceEntity where type = :type");
+
+        query.setParameter("type", type);
+
+        return query.getResultList();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
new file mode 100644
index 0000000..cc9a956
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.jpa.entities.ScopeEntity;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAScopeStore implements ScopeStore {
+
+    private final EntityManager entityManager;
+
+    public JPAScopeStore(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    @Override
+    public Scope create(final String name, final ResourceServer resourceServer) {
+        ScopeEntity entity = new ScopeEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setResourceServer((ResourceServerEntity) resourceServer);
+
+        this.entityManager.persist(entity);
+
+        return entity;
+    }
+
+    @Override
+    public void delete(String id) {
+        this.entityManager.remove(findById(id));
+    }
+
+    @Override
+    public Scope findById(String id) {
+        return entityManager.find(ScopeEntity.class, id);
+    }
+
+    @Override
+    public Scope findByName(String name, String resourceServerId) {
+        try {
+            Query query = entityManager.createQuery("select s from ScopeEntity s inner join s.resourceServer rs where rs.id = :resourceServerId and name = :name");
+
+            query.setParameter("name", name);
+            query.setParameter("resourceServerId", resourceServerId);
+
+            return (Scope) query.getSingleResult();
+        } catch (NoResultException nre) {
+            return null;
+        }
+    }
+
+    @Override
+    public List<Scope> findByResourceServer(final String serverId) {
+        Query query = entityManager.createQuery("from ScopeEntity where resourceServer.id = :serverId");
+
+        query.setParameter("serverId", serverId);
+
+        return query.getResultList();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
new file mode 100644
index 0000000..5dad6af
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.jpa.store;
+
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAStoreFactory implements StoreFactory {
+
+    private final EntityManager entityManager;
+
+    public JPAStoreFactory(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    @Override
+    public PolicyStore getPolicyStore() {
+        return new JPAPolicyStore(this.entityManager);
+    }
+
+    @Override
+    public ResourceServerStore getResourceServerStore() {
+        return new JPAResourceServerStore(this.entityManager);
+    }
+
+    @Override
+    public ResourceStore getResourceStore() {
+        return new JPAResourceStore(this.entityManager);
+    }
+
+    @Override
+    public ScopeStore getScopeStore() {
+        return new JPAScopeStore(this.entityManager);
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index f1e6f04..784438c 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -20,32 +20,26 @@ package org.keycloak.connections.jpa;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.DriverManager;
-import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 
 import javax.naming.InitialContext;
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
-import javax.persistence.Persistence;
-import javax.persistence.spi.PersistenceUnitTransactionType;
 import javax.sql.DataSource;
 
-import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
 import org.hibernate.ejb.AvailableSettings;
-import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
-import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
-import org.hibernate.jpa.boot.spi.Bootstrap;
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
 import org.keycloak.connections.jpa.util.JpaUtils;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.dblock.DBLockProvider;
 import org.keycloak.provider.ServerInfoAwareProviderFactory;
+import org.keycloak.models.dblock.DBLockManager;
 import org.keycloak.timer.TimerProvider;
 
 /**
@@ -164,22 +158,15 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
 	                        if (updater == null) {
 	                            throw new RuntimeException("Can't update database: JPA updater provider not found");
 	                        }
+
+                            // Check if having DBLock before trying to initialize hibernate
+                            DBLockProvider dbLock = new DBLockManager(session).getDBLock();
+                            if (!dbLock.hasLock()) {
+                                throw new IllegalStateException("Trying to update database, but don't have a DB lock acquired");
+                            }
 	
 	                        if (databaseSchema.equals("update")) {
-	                            String currentVersion = null;
-	                            try {
-	                                ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql(schema));
-	                                if (resultSet.next()) {
-	                                    currentVersion = resultSet.getString(1);
-	                                }
-	                            } catch (SQLException e) {
-	                            }
-	
-	                            if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
-	                                updater.update(connection, schema);
-	                            } else {
-	                                logger.debug("Database is up to date");
-	                            }
+                                updater.update(connection, schema);
 	                        } else if (databaseSchema.equals("validate")) {
 	                            updater.validate(connection, schema);
 	                        } else {
@@ -195,7 +182,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                         }
 
 	                    logger.trace("Creating EntityManagerFactory");
-	                    emf = JpaUtils.createEntityManagerFactory(unitName, properties, getClass().getClassLoader());
+	                    emf = JpaUtils.createEntityManagerFactory(session, unitName, properties, getClass().getClassLoader());
 	                    logger.trace("EntityManagerFactory created");
 
                         if (globalStatsInterval != -1) {
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntityProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntityProvider.java
new file mode 100644
index 0000000..567080e
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntityProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.jpa.entityprovider;
+
+import java.util.List;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * A JPA Entity Provider can supply extra JPA entities that the Keycloak system should include in it's entity manager. The
+ * entities should be provided as a list of Class objects.
+ */
+public interface JpaEntityProvider extends Provider {
+
+    /**
+     * Return the entities that should be added to the entity manager.
+     * 
+     * @return list of class objects
+     */
+	List<Class<?>> getEntities();
+	
+	/**
+	 * Return the location of the Liquibase changelog that facilitates the extra JPA entities.
+	 * This should be a location that can be found on the same classpath as the entity classes.
+	 * 
+	 * @return a changelog location or null if not needed
+	 */
+	String getChangelogLocation();
+
+	/**
+	 * Return the ID of provider factory, which created this provider. Might be used to "compute" the table name of liquibase changelog table.
+	 * @return ID of provider factory
+	 */
+	String getFactoryId();
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntitySpi.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntitySpi.java
new file mode 100644
index 0000000..d89389f
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/JpaEntitySpi.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.jpa.entityprovider;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Spi that allows for adding extra JPA entity's to the Keycloak entity manager.
+ */
+public class JpaEntitySpi implements Spi {
+
+	@Override
+	public boolean isInternal() {
+		return false;
+	}
+
+	@Override
+	public String getName() {
+		return "jpa-entity-provider";
+	}
+
+	@Override
+	public Class<? extends Provider> getProviderClass() {
+		return JpaEntityProvider.class;
+	}
+
+	@Override
+	public Class<? extends ProviderFactory> getProviderFactoryClass() {
+		return JpaEntityProviderFactory.class;
+	}
+	
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/ProxyClassLoader.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/ProxyClassLoader.java
new file mode 100644
index 0000000..e9b82a3
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/entityprovider/ProxyClassLoader.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.jpa.entityprovider;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Classloader implementation to facilitate loading classes and resources from a collection of other classloaders.
+ * Effectively it forms a proxy to one or more other classloaders.
+ * 
+ * The way it works:
+ * - Get all (unique) classloaders from all provided classes
+ * - For each class or resource that is 'requested':
+ *   - First try all provided classloaders and if we have a match, return that
+ *   - If no match was found: proceed with 'normal' classloading in 'current classpath' scope
+ * 
+ * In this particular context: only loadClass and getResource overrides are needed, since those
+ * are the methods that a classloading and resource loading process will need.
+ */
+public class ProxyClassLoader extends ClassLoader {
+
+    private Set<ClassLoader> classloaders;
+
+    public ProxyClassLoader(Collection<Class<?>> classes, ClassLoader parentClassLoader) {
+    	super(parentClassLoader);
+    	init(classes);
+    }
+    
+    public ProxyClassLoader(Collection<Class<?>> classes) {
+    	init(classes);
+    }
+
+    private void init(Collection<Class<?>> classes) {
+        classloaders = new HashSet<>();
+        for (Class<?> clazz : classes) {
+            classloaders.add(clazz.getClassLoader());
+        }
+    }
+    
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        for (ClassLoader classloader : classloaders) {
+            try {
+                return classloader.loadClass(name);
+            } catch (ClassNotFoundException e) {
+                // This particular class loader did not find the class. It's expected behavior that
+                // this can happen, so we'll just ignore the exception and let the next one try.
+            }
+        }
+        // We did not find the class in the proxy class loaders, so proceed with 'normal' behavior.
+        return super.loadClass(name);
+    }
+
+    @Override
+    public URL getResource(String name) {
+        for (ClassLoader classloader : classloaders) {
+            URL resource = classloader.getResource(name);
+            if (resource != null) {
+                return resource;
+            }
+            // Resource == null means not found, so let the next one try.
+        }
+        // We could not get the resource from the proxy class loaders, so proceed with 'normal' behavior.
+        return super.getResource(name);
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
index f253684..b087535 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -28,10 +28,6 @@ public interface JpaUpdaterProvider extends Provider {
 
     public String FIRST_VERSION = "1.0.0.Final";
 
-    public String LAST_VERSION = "1.9.2";
-
-    public String getCurrentVersionSql(String defaultSchema);
-
     public void update(Connection connection, String defaultSchema);
 
     public void validate(Connection connection, String defaultSchema);
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
index b9018f8..5a58702 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
@@ -19,6 +19,16 @@ package org.keycloak.connections.jpa.updater.liquibase.conn;
 
 import java.sql.Connection;
 
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider;
+import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase;
+import org.keycloak.connections.jpa.updater.liquibase.lock.CustomInsertLockRecordGenerator;
+import org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockDatabaseChangeLogGenerator;
+import org.keycloak.connections.jpa.updater.liquibase.lock.DummyLockService;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
 import liquibase.Liquibase;
 import liquibase.changelog.ChangeSet;
 import liquibase.changelog.DatabaseChangeLog;
@@ -27,23 +37,12 @@ import liquibase.database.DatabaseFactory;
 import liquibase.database.core.DB2Database;
 import liquibase.database.jvm.JdbcConnection;
 import liquibase.exception.LiquibaseException;
-import liquibase.lockservice.LockService;
-import liquibase.lockservice.LockServiceFactory;
 import liquibase.logging.LogFactory;
 import liquibase.logging.LogLevel;
 import liquibase.resource.ClassLoaderResourceAccessor;
+import liquibase.resource.ResourceAccessor;
 import liquibase.servicelocator.ServiceLocator;
 import liquibase.sqlgenerator.SqlGeneratorFactory;
-import org.jboss.logging.Logger;
-import org.keycloak.Config;
-import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider;
-import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase;
-import org.keycloak.connections.jpa.updater.liquibase.lock.CustomInsertLockRecordGenerator;
-import org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockDatabaseChangeLogGenerator;
-import org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService;
-import org.keycloak.connections.jpa.updater.liquibase.lock.DummyLockService;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -53,7 +52,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
     private static final Logger logger = Logger.getLogger(DefaultLiquibaseConnectionProvider.class);
 
     private volatile boolean initialized = false;
-
+    
     @Override
     public LiquibaseConnectionProvider create(KeycloakSession session) {
         if (!initialized) {
@@ -132,9 +131,26 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
         }
 
         String changelog = (database instanceof DB2Database) ? LiquibaseJpaUpdaterProvider.DB2_CHANGELOG :  LiquibaseJpaUpdaterProvider.CHANGELOG;
-        logger.debugf("Using changelog file: %s", changelog);
+        ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(getClass().getClassLoader());
+
+        logger.debugf("Using changelog file %s and changelogTableName %s", changelog, database.getDatabaseChangeLogTableName());
+        
+        return new Liquibase(changelog, resourceAccessor, database);
+    }
+
+    @Override
+    public Liquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
+        Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
+        if (defaultSchema != null) {
+            database.setDefaultSchemaName(defaultSchema);
+        }
+
+        ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(classloader);
+        database.setDatabaseChangeLogTableName(changelogTableName);
+
+        logger.debugf("Using changelog file %s and changelogTableName %s", changelogLocation, database.getDatabaseChangeLogTableName());
 
-        return new Liquibase(changelog, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
+        return new Liquibase(changelogLocation, resourceAccessor, database);
     }
 
     private static class LogWrapper extends LogFactory {
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/LiquibaseConnectionProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/LiquibaseConnectionProvider.java
index 5aa81cc..215bd1d 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/LiquibaseConnectionProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/LiquibaseConnectionProvider.java
@@ -30,4 +30,6 @@ public interface LiquibaseConnectionProvider extends Provider {
 
     Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException;
 
+    Liquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException;
+
 }
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
index 4a505a3..2610174 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
@@ -23,14 +23,17 @@ import liquibase.changelog.ChangeSet;
 import liquibase.changelog.RanChangeSet;
 import liquibase.exception.LiquibaseException;
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.reflections.Reflections;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
 import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
 import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
+import org.keycloak.connections.jpa.util.JpaUtils;
 import org.keycloak.models.KeycloakSession;
 
+import java.lang.reflect.Method;
 import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -49,11 +52,6 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
     }
 
     @Override
-    public String getCurrentVersionSql(String defaultSchema) {
-        return "SELECT ID from " + getTable("DATABASECHANGELOG", defaultSchema) + " ORDER BY DATEEXECUTED DESC LIMIT 1";
-    }
-
-    @Override
     public void update(Connection connection, String defaultSchema) {
         logger.debug("Starting database update");
 
@@ -61,38 +59,51 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
         ThreadLocalSessionContext.setCurrentSession(session);
 
         try {
-            Liquibase liquibase = getLiquibase(connection, defaultSchema);
-
-            List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
-            if (!changeSets.isEmpty()) {
-                if (changeSets.get(0).getId().equals(FIRST_VERSION)) {
-                    Statement statement = connection.createStatement();
-                    try {
-                        statement.executeQuery("SELECT id FROM " + getTable("REALM", defaultSchema));
-
-                        logger.infov("Updating database from {0} to {1}", FIRST_VERSION, changeSets.get(changeSets.size() - 1).getId());
-                        liquibase.markNextChangeSetRan(null);
-                    } catch (SQLException e) {
-                        logger.info("Initializing database schema");
-                    }
-                } else {
-                    if (logger.isDebugEnabled()) {
-                        List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
-                        logger.debugv("Updating database from {0} to {1}", ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId());
-                    } else {
-                        logger.infov("Updating database");
-                    }
+            // Run update with keycloak master changelog first
+            Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
+            updateChangeSet(liquibase, liquibase.getChangeLogFile());
+
+            // Run update for each custom JpaEntityProvider
+            Set<JpaEntityProvider> jpaProviders = session.getAllProviders(JpaEntityProvider.class);
+            for (JpaEntityProvider jpaProvider : jpaProviders) {
+                String customChangelog = jpaProvider.getChangelogLocation();
+                if (customChangelog != null) {
+                    String factoryId = jpaProvider.getFactoryId();
+                    String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
+                    liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
+                    updateChangeSet(liquibase, liquibase.getChangeLogFile());
                 }
-
-                liquibase.update((Contexts) null);
             }
         } catch (Exception e) {
             throw new RuntimeException("Failed to update database", e);
         } finally {
             ThreadLocalSessionContext.removeCurrentSession();
         }
+    }
+
+    protected void updateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
+        List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
+        if (!changeSets.isEmpty()) {
+            List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
+            if (ranChangeSets.isEmpty()) {
+                logger.infov("Initializing database schema. Using changelog {0}", changelog);
+            } else {
+                if (logger.isDebugEnabled()) {
+                    logger.debugv("Updating database from {0} to {1}. Using changelog {2}", ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId(), changelog);
+                } else {
+                    logger.infov("Updating database. Using changelog {0}", changelog);
+                }
+            }
 
-        logger.debug("Completed database update");
+            liquibase.update((Contexts) null);
+            logger.debugv("Completed database update for changelog {0}", changelog);
+        } else {
+            logger.debugv("Database is up to date for changelog {0}", changelog);
+
+            // Needs to restart liquibase services to clear changeLogHistory.
+            Method resetServices = Reflections.findDeclaredMethod(Liquibase.class, "resetServices");
+            Reflections.invokeMethod(true, resetServices, liquibase);
+        }
     }
 
     @Override
@@ -100,16 +111,20 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
         logger.debug("Validating if database is updated");
 
         try {
-            Liquibase liquibase = getLiquibase(connection, defaultSchema);
-
-            List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
-            if (!changeSets.isEmpty()) {
-                List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
-                String errorMessage = String.format("Failed to validate database schema. Schema needs updating database from %s to %s. Please change databaseSchema to 'update' or use other database",
-                        ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId());
-                throw new RuntimeException(errorMessage);
-            } else {
-                logger.debug("Validation passed. Database is up-to-date");
+            // Validate with keycloak master changelog first
+            Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
+            validateChangeSet(liquibase, liquibase.getChangeLogFile());
+
+            // Validate each custom JpaEntityProvider
+            Set<JpaEntityProvider> jpaProviders = session.getAllProviders(JpaEntityProvider.class);
+            for (JpaEntityProvider jpaProvider : jpaProviders) {
+                String customChangelog = jpaProvider.getChangelogLocation();
+                if (customChangelog != null) {
+                    String factoryId = jpaProvider.getFactoryId();
+                    String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
+                    liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
+                    validateChangeSet(liquibase, liquibase.getChangeLogFile());
+                }
             }
 
         } catch (LiquibaseException e) {
@@ -117,11 +132,28 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
         }
     }
 
-    private Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
+    protected void validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
+        List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
+        if (!changeSets.isEmpty()) {
+            List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
+            String errorMessage = String.format("Failed to validate database schema. Schema needs updating database from %s to %s. Please change databaseSchema to 'update' or use other database. Used changelog was %s",
+                    ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId(), changelog);
+            throw new RuntimeException(errorMessage);
+        } else {
+            logger.debugf("Validation passed. Database is up-to-date for changelog %s", changelog);
+        }
+    }
+
+    private Liquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException {
         LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
         return liquibaseProvider.getLiquibase(connection, defaultSchema);
     }
 
+    private Liquibase getLiquibaseForCustomProviderUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
+        LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
+        return liquibaseProvider.getLiquibaseForCustomUpdate(connection, defaultSchema, changelogLocation, classloader, changelogTableName);
+    }
+
     @Override
     public void close() {
     }
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
index f44641e..7c48499 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
@@ -48,32 +48,35 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
 
     private CustomLockService lockService;
     private Connection dbConnection;
+    private boolean initialized = false;
 
     private int maxAttempts = DEFAULT_MAX_ATTEMPTS;
 
     public LiquibaseDBLockProvider(LiquibaseDBLockProviderFactory factory, KeycloakSession session) {
         this.factory = factory;
         this.session = session;
-        init();
     }
 
-    private void init() {
-        LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
-        JpaConnectionProviderFactory jpaProviderFactory = (JpaConnectionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class);
+    private void lazyInit() {
+        if (!initialized) {
+            LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
+            JpaConnectionProviderFactory jpaProviderFactory = (JpaConnectionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class);
 
-        this.dbConnection = jpaProviderFactory.getConnection();
-        String defaultSchema = jpaProviderFactory.getSchema();
+            this.dbConnection = jpaProviderFactory.getConnection();
+            String defaultSchema = jpaProviderFactory.getSchema();
 
-        try {
-            Liquibase liquibase = liquibaseProvider.getLiquibase(dbConnection, defaultSchema);
+            try {
+                Liquibase liquibase = liquibaseProvider.getLiquibase(dbConnection, defaultSchema);
 
-            this.lockService = new CustomLockService();
-            lockService.setChangeLogLockWaitTime(factory.getLockWaitTimeoutMillis());
-            lockService.setDatabase(liquibase.getDatabase());
-        } catch (LiquibaseException exception) {
-            safeRollbackConnection();
-            safeCloseConnection();
-            throw new IllegalStateException(exception);
+                this.lockService = new CustomLockService();
+                lockService.setChangeLogLockWaitTime(factory.getLockWaitTimeoutMillis());
+                lockService.setDatabase(liquibase.getDatabase());
+                initialized = true;
+            } catch (LiquibaseException exception) {
+                safeRollbackConnection();
+                safeCloseConnection();
+                throw new IllegalStateException(exception);
+            }
         }
     }
 
@@ -82,15 +85,19 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
         safeCloseConnection();
         this.dbConnection = null;
         this.lockService = null;
-        init();
+        initialized = false;
+        lazyInit();
     }
 
 
     @Override
     public void waitForLock() {
+        lazyInit();
+
         while (maxAttempts > 0) {
             try {
                 lockService.waitForLock();
+                factory.setHasLock(true);
                 this.maxAttempts = DEFAULT_MAX_ATTEMPTS;
                 return;
             } catch (LockRetryException le) {
@@ -109,8 +116,16 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
 
     @Override
     public void releaseLock() {
+        lazyInit();
+
         lockService.releaseLock();
         lockService.reset();
+        factory.setHasLock(false);
+    }
+
+    @Override
+    public boolean hasLock() {
+        return factory.hasLock();
     }
 
     @Override
@@ -121,6 +136,8 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
 
     @Override
     public void destroyLockInfo() {
+        lazyInit();
+
         try {
             this.lockService.destroy();
             dbConnection.commit();
@@ -147,7 +164,7 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
     }
 
     private void safeCloseConnection() {
-        // Close after creating EntityManagerFactory to prevent in-mem databases from closing
+        // Close to prevent in-mem databases from closing
         if (dbConnection != null) {
             try {
                 dbConnection.close();
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
index 3026f7d..ef1fae1 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.connections.jpa.updater.liquibase.lock;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.common.util.Time;
@@ -33,6 +35,9 @@ public class LiquibaseDBLockProviderFactory implements DBLockProviderFactory {
 
     private long lockWaitTimeoutMillis;
 
+    // True if this node has a lock acquired
+    private AtomicBoolean hasLock = new AtomicBoolean(false);
+
     protected long getLockWaitTimeoutMillis() {
         return lockWaitTimeoutMillis;
     }
@@ -68,4 +73,12 @@ public class LiquibaseDBLockProviderFactory implements DBLockProviderFactory {
     public String getId() {
         return "jpa";
     }
+
+    public boolean hasLock() {
+        return hasLock.get();
+    }
+
+    public void setHasLock(boolean hasLock) {
+        this.hasLock.set(hasLock);
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java
index de07fd5..5ac7d2f 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java
@@ -21,12 +21,18 @@ import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
 import org.hibernate.jpa.boot.spi.Bootstrap;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
+import org.keycloak.connections.jpa.entityprovider.ProxyClassLoader;
+import org.keycloak.models.KeycloakSession;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.spi.PersistenceUnitTransactionType;
+
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -40,14 +46,52 @@ public class JpaUtils {
         return (schema==null) ? tableName : schema + "." + tableName;
     }
 
-    public static EntityManagerFactory createEntityManagerFactory(String unitName, Map<String, Object> properties, ClassLoader classLoader) {
+    public static EntityManagerFactory createEntityManagerFactory(KeycloakSession session, String unitName, Map<String, Object> properties, ClassLoader classLoader) {
         PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(classLoader), PersistenceUnitTransactionType.RESOURCE_LOCAL);
         List<ParsedPersistenceXmlDescriptor> persistenceUnits = parser.doResolve(properties);
         for (ParsedPersistenceXmlDescriptor persistenceUnit : persistenceUnits) {
             if (persistenceUnit.getName().equals(unitName)) {
-                return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties, classLoader).build();
+                List<Class<?>> providedEntities = getProvidedEntities(session);
+                for (Class<?> entityClass : providedEntities) {
+                    // Add all extra entity classes to the persistence unit.
+                    persistenceUnit.addClasses(entityClass.getName());
+                }
+                // Now build the entity manager factory, supplying a proxy classloader, so Hibernate will be able
+                // to find and load the extra provided entities. Set the provided classloader as parent classloader.
+                return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties,
+                        new ProxyClassLoader(providedEntities, classLoader)).build();
             }
         }
         throw new RuntimeException("Persistence unit '" + unitName + "' not found");
     }
+
+    /**
+     * Get a list of all provided entities by looping over all configured entity providers.
+     * 
+     * @param session the keycloak session
+     * @return a list of all provided entities (can be an empty list)
+     */
+    public static List<Class<?>> getProvidedEntities(KeycloakSession session) {
+        List<Class<?>> providedEntityClasses = new ArrayList<>();
+        // Get all configured entity providers.
+        Set<JpaEntityProvider> entityProviders = session.getAllProviders(JpaEntityProvider.class);
+        // For every provider, add all entity classes to the list.
+        for (JpaEntityProvider entityProvider : entityProviders) {
+            providedEntityClasses.addAll(entityProvider.getEntities());
+        }
+        return providedEntityClasses;
+    }
+
+    /**
+     * Get the name of custom table for liquibase updates for give ID of JpaEntityProvider
+     * @param jpaEntityProviderFactoryId
+     * @return table name
+     */
+    public static String getCustomChangelogTableName(String jpaEntityProviderFactoryId) {
+        String upperCased = jpaEntityProviderFactoryId.toUpperCase();
+        upperCased = upperCased.replaceAll("-", "_");
+        upperCased = upperCased.replaceAll("[^A-Z_]", "");
+        return "DATABASECHANGELOG_" + upperCased.substring(0, Math.min(10, upperCased.length()));
+    }
+
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index e504f2b..5ceab0b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -39,11 +39,9 @@ import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -127,7 +125,7 @@ public class JpaRealmProvider implements RealmProvider {
             return false;
         }
         em.refresh(realm);
-        RealmAdapter adapter = new RealmAdapter(session, em, realm);
+        final RealmAdapter adapter = new RealmAdapter(session, em, realm);
         session.users().preRemove(adapter);
 
         realm.getDefaultGroups().clear();
@@ -159,6 +157,19 @@ public class JpaRealmProvider implements RealmProvider {
 
         em.flush();
         em.clear();
+
+        session.getKeycloakSessionFactory().publish(new RealmModel.RealmRemovedEvent() {
+            @Override
+            public RealmModel getRealm() {
+                return adapter;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
+
         return true;
     }
 
@@ -268,6 +279,19 @@ public class JpaRealmProvider implements RealmProvider {
         int val = em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
 
         em.remove(roleEntity);
+
+        session.getKeycloakSessionFactory().publish(new RoleContainerModel.RoleRemovedEvent() {
+            @Override
+            public RoleModel getRole() {
+                return role;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
+
         em.flush();
         return true;
 
@@ -451,7 +475,7 @@ public class JpaRealmProvider implements RealmProvider {
 
     @Override
     public boolean removeClient(String id, RealmModel realm) {
-        ClientModel client = getClientById(id, realm);
+        final ClientModel client = getClientById(id, realm);
         if (client == null) return false;
 
         session.users().preRemove(realm, client);
@@ -460,17 +484,32 @@ public class JpaRealmProvider implements RealmProvider {
             client.removeRole(role);
         }
 
-
         ClientEntity clientEntity = ((ClientAdapter)client).getEntity();
+
         em.createNamedQuery("deleteScopeMappingByClient").setParameter("client", clientEntity).executeUpdate();
         em.flush();
+
+        session.getKeycloakSessionFactory().publish(new RealmModel.ClientRemovedEvent() {
+            @Override
+            public ClientModel getClient() {
+                return client;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
+
         em.remove(clientEntity);  // i have no idea why, but this needs to come before deleteScopeMapping
+
         try {
             em.flush();
         } catch (RuntimeException e) {
             logger.errorv("Unable to delete client entity: {0} from realm {1}", client.getClientId(), realm.getName());
             throw e;
         }
+
         return true;
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 0349a67..7802453 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -27,6 +27,7 @@ import org.keycloak.models.ModelException;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredActionProviderModel;
+import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserConsentModel;
 import org.keycloak.models.UserCredentialModel;
@@ -123,6 +124,17 @@ public class JpaUserProvider implements UserProvider {
         UserEntity userEntity = em.find(UserEntity.class, user.getId());
         if (userEntity == null) return false;
         removeUser(userEntity);
+        session.getKeycloakSessionFactory().publish(new UserModel.UserRemovedEvent() {
+            @Override
+            public UserModel getUser() {
+                return user;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
         return true;
     }
 
@@ -142,6 +154,7 @@ public class JpaUserProvider implements UserProvider {
         if (user != null) {
             em.remove(user);
         }
+
         em.flush();
     }
 
diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
index 2d0117e..6b9ab90 100644
--- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
@@ -32,4 +32,6 @@
     <include file="META-INF/jpa-changelog-1.9.0.xml"/>
     <include file="META-INF/db2-jpa-changelog-1.9.1.xml"/>
     <include file="META-INF/jpa-changelog-1.9.2.xml"/>
+
+    <include file="META-INF/jpa-changelog-authz-master.xml"/>
 </databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-2.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-2.0.0.xml
new file mode 100755
index 0000000..6e7d606
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-2.0.0.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+    <changeSet author="psilva@redhat.com" id="authz-2.0.0">
+        <createTable tableName="RESOURCE_SERVER">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ALLOW_RS_REMOTE_MGMT" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="POLICY_ENFORCE_MODE" type="VARCHAR(15)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FARS" tableName="RESOURCE_SERVER"/>
+        <addUniqueConstraint columnNames="CLIENT_ID" constraintName="UK_AU8TT6T700S9V50BU18WS5HA6" tableName="RESOURCE_SERVER"/>
+
+        <createTable tableName="RESOURCE_SERVER_RESOURCE">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="URI" type="VARCHAR(255)"/>
+            <column name="TYPE" type="VARCHAR(255)"/>
+            <column name="ICON_URI" type="VARCHAR(255)"/>
+            <column name="OWNER" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="RESOURCE_SERVER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FARSR" tableName="RESOURCE_SERVER_RESOURCE"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_SERVER_ID" baseTableName="RESOURCE_SERVER_RESOURCE" constraintName="FK_FRSRHO213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER"/>
+        <addUniqueConstraint columnNames="NAME,OWNER,RESOURCE_SERVER_ID" constraintName="UK_FRSR6T700S9V50BU18WS5HA6" tableName="RESOURCE_SERVER_RESOURCE"/>
+
+        <createTable tableName="RESOURCE_SERVER_SCOPE">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ICON_URI" type="VARCHAR(255)"/>
+            <column name="RESOURCE_SERVER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FARSRS" tableName="RESOURCE_SERVER_SCOPE"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_SERVER_ID" baseTableName="RESOURCE_SERVER_SCOPE" constraintName="FK_FRSRSO213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER"/>
+        <addUniqueConstraint columnNames="NAME,RESOURCE_SERVER_ID" constraintName="UK_FRSRST700S9V50BU18WS5HA6" tableName="RESOURCE_SERVER_SCOPE"/>
+
+        <createTable tableName="RESOURCE_SERVER_POLICY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="DESCRIPTION" type="VARCHAR(255)"/>
+            <column name="TYPE" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="DECISION_STRATEGY" type="VARCHAR(20)"/>
+            <column name="LOGIC" type="VARCHAR(20)"/>
+            <column name="RESOURCE_SERVER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FARSRP" tableName="RESOURCE_SERVER_POLICY"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_SERVER_ID" baseTableName="RESOURCE_SERVER_POLICY" constraintName="FK_FRSRPO213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER"/>
+        <addUniqueConstraint columnNames="NAME,RESOURCE_SERVER_ID" constraintName="UK_FRSRPT700S9V50BU18WS5HA6" tableName="RESOURCE_SERVER_POLICY"/>
+
+        <createTable tableName="POLICY_CONFIG">
+            <column name="POLICY_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="CLOB"/>
+        </createTable>
+
+        <addPrimaryKey columnNames="POLICY_ID, NAME" constraintName="CONSTRAINT_DPC" tableName="POLICY_CONFIG"/>
+        <addForeignKeyConstraint baseColumnNames="POLICY_ID" baseTableName="POLICY_CONFIG" constraintName="FKDC34197CF864C4E43" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_POLICY"/>
+
+        <createTable tableName="RESOURCE_SCOPE">
+            <column name="RESOURCE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="SCOPE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="RESOURCE_ID,SCOPE_ID" constraintName="CONSTRAINT_FARSRSP" tableName="RESOURCE_SCOPE"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_ID" baseTableName="RESOURCE_SCOPE" constraintName="FK_FRSRPOS13XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_RESOURCE"/>
+        <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="RESOURCE_SCOPE" constraintName="FK_FRSRPS213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_SCOPE"/>
+
+        <createTable tableName="RESOURCE_POLICY">
+            <column name="RESOURCE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="POLICY_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="RESOURCE_ID,POLICY_ID" constraintName="CONSTRAINT_FARSRPP" tableName="RESOURCE_POLICY"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_ID" baseTableName="RESOURCE_POLICY" constraintName="FK_FRSRPOS53XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_RESOURCE"/>
+        <addForeignKeyConstraint baseColumnNames="POLICY_ID" baseTableName="RESOURCE_POLICY" constraintName="FK_FRSRPP213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_POLICY"/>
+
+        <createTable tableName="SCOPE_POLICY">
+            <column name="SCOPE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="POLICY_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="SCOPE_ID,POLICY_ID" constraintName="CONSTRAINT_FARSRSPS" tableName="SCOPE_POLICY"/>
+        <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="SCOPE_POLICY" constraintName="FK_FRSRPASS3XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_SCOPE"/>
+        <addForeignKeyConstraint baseColumnNames="POLICY_ID" baseTableName="SCOPE_POLICY" constraintName="FK_FRSRASP13XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_POLICY"/>
+
+        <createTable tableName="ASSOCIATED_POLICY">
+            <column name="POLICY_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ASSOCIATED_POLICY_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="POLICY_ID,ASSOCIATED_POLICY_ID" constraintName="CONSTRAINT_FARSRPAP" tableName="ASSOCIATED_POLICY"/>
+        <addForeignKeyConstraint baseColumnNames="POLICY_ID" baseTableName="ASSOCIATED_POLICY" constraintName="FK_FRSRPAS14XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_POLICY"/>
+        <addForeignKeyConstraint baseColumnNames="ASSOCIATED_POLICY_ID" baseTableName="ASSOCIATED_POLICY" constraintName="FK_FRSR5S213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_POLICY"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index fd1f0f6..c8abe49 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -32,4 +32,6 @@
     <include file="META-INF/jpa-changelog-1.9.0.xml"/>
     <include file="META-INF/jpa-changelog-1.9.1.xml"/>
     <include file="META-INF/jpa-changelog-1.9.2.xml"/>
+
+    <include file="META-INF/jpa-changelog-authz-master.xml"/>
 </databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index 7b6ee37..b90c0fc 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -58,6 +58,12 @@
         <!-- JpaAuditProviders -->
         <class>org.keycloak.events.jpa.EventEntity</class>
         <class>org.keycloak.events.jpa.AdminEventEntity</class>
+
+        <!-- Authorization -->
+        <class>org.keycloak.authorization.jpa.entities.ResourceServerEntity</class>
+        <class>org.keycloak.authorization.jpa.entities.ResourceEntity</class>
+        <class>org.keycloak.authorization.jpa.entities.ScopeEntity</class>
+        <class>org.keycloak.authorization.jpa.entities.PolicyEntity</class>
         
         <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory
new file mode 100644
index 0000000..46463ee
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.authorization.jpa.store.JPAAuthorizationStoreFactory
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 94c6512..5aba7ba 100644
--- a/model/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -17,4 +17,5 @@
 
 org.keycloak.connections.jpa.JpaConnectionSpi
 org.keycloak.connections.jpa.updater.JpaUpdaterSpi
-org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi
\ No newline at end of file
+org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi
+org.keycloak.connections.jpa.entityprovider.JpaEntitySpi
diff --git a/model/jpa/src/test/java/org/keycloak/connections/jpa/util/JpaUtilsTest.java b/model/jpa/src/test/java/org/keycloak/connections/jpa/util/JpaUtilsTest.java
new file mode 100644
index 0000000..838f1c0
--- /dev/null
+++ b/model/jpa/src/test/java/org/keycloak/connections/jpa/util/JpaUtilsTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.jpa.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JpaUtilsTest {
+
+    @Test
+    public void testConvertTableName() {
+        Assert.assertEquals("DATABASECHANGELOG_FOO", JpaUtils.getCustomChangelogTableName("foo"));
+        Assert.assertEquals("DATABASECHANGELOG_FOOBAR", JpaUtils.getCustomChangelogTableName("foo123bar"));
+        Assert.assertEquals("DATABASECHANGELOG_FOO_BAR", JpaUtils.getCustomChangelogTableName("foo_bar568"));
+        Assert.assertEquals("DATABASECHANGELOG_FOO_BAR_C", JpaUtils.getCustomChangelogTableName("foo-bar-c568"));
+        Assert.assertEquals("DATABASECHANGELOG_EXAMPLE_EN", JpaUtils.getCustomChangelogTableName("example-entity-provider"));
+    }
+}
diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml
index db24473..90e2afa 100755
--- a/model/mongo/pom.xml
+++ b/model/mongo/pom.xml
@@ -31,6 +31,11 @@
     <name>Keycloak Model Mongo</name>
     <description/>
 
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.bouncycastle</groupId>
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java
new file mode 100644
index 0000000..38cb87b
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/PolicyAdapter.java
@@ -0,0 +1,162 @@
+package org.keycloak.authorization.mongo.adapter;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.mongo.entities.PolicyEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.adapters.AbstractMongoAdapter;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyAdapter extends AbstractMongoAdapter<PolicyEntity> implements Policy {
+
+    private final PolicyEntity entity;
+    private final AuthorizationProvider authorizationProvider;
+
+    public PolicyAdapter(PolicyEntity entity, MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        super(invocationContext);
+        this.entity = entity;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    protected PolicyEntity getMongoEntity() {
+        return entity;
+    }
+
+    @Override
+    public String getId() {
+        return getMongoEntity().getId();
+    }
+
+    @Override
+    public String getType() {
+        return getMongoEntity().getType();
+    }
+
+    @Override
+    public DecisionStrategy getDecisionStrategy() {
+        return getMongoEntity().getDecisionStrategy();
+    }
+
+    @Override
+    public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+        getMongoEntity().setDecisionStrategy(decisionStrategy);
+        updateMongoEntity();
+    }
+
+    @Override
+    public Logic getLogic() {
+        return getMongoEntity().getLogic();
+    }
+
+    @Override
+    public void setLogic(Logic logic) {
+        getMongoEntity().setLogic(logic);
+        updateMongoEntity();
+    }
+
+    @Override
+    public Map<String, String> getConfig() {
+        return getMongoEntity().getConfig();
+    }
+
+    @Override
+    public void setConfig(Map<String, String> config) {
+        getMongoEntity().setConfig(config);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getName() {
+        return getMongoEntity().getName();
+    }
+
+    @Override
+    public void setName(String name) {
+        getMongoEntity().setName(name);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getDescription() {
+        return getMongoEntity().getDescription();
+    }
+
+    @Override
+    public void setDescription(String description) {
+        getMongoEntity().setDescription(description);
+        updateMongoEntity();
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        return this.authorizationProvider.getStoreFactory().getResourceServerStore().findById(getMongoEntity().getResourceServerId());
+    }
+
+    @Override
+    public Set<Policy> getAssociatedPolicies() {
+        return getMongoEntity().getAssociatedPolicies().stream()
+                .map((Function<String, Policy>) id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Set<Resource> getResources() {
+        return getMongoEntity().getResources().stream()
+                .map((Function<String, Resource>) id -> authorizationProvider.getStoreFactory().getResourceStore().findById(id))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Set<Scope> getScopes() {
+        return getMongoEntity().getScopes().stream()
+                .map((Function<String, Scope>) id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void addScope(Scope scope) {
+        getMongoEntity().addScope(scope.getId());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void removeScope(Scope scope) {
+        getMongoEntity().removeScope(scope.getId());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void addAssociatedPolicy(Policy associatedPolicy) {
+        getMongoEntity().addAssociatedPolicy(associatedPolicy.getId());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void removeAssociatedPolicy(Policy associatedPolicy) {
+        getMongoEntity().removeAssociatedPolicy(associatedPolicy.getId());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void addResource(Resource resource) {
+        getMongoEntity().addResource(resource.getId());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void removeResource(Resource resource) {
+        getMongoEntity().removeResource(resource.getId());
+        updateMongoEntity();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java
new file mode 100644
index 0000000..7c67f6e
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceAdapter.java
@@ -0,0 +1,106 @@
+package org.keycloak.authorization.mongo.adapter;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.mongo.entities.ResourceEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.adapters.AbstractMongoAdapter;
+
+import java.util.List;
+import java.util.Set;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceAdapter extends AbstractMongoAdapter<ResourceEntity> implements Resource {
+
+    private final ResourceEntity entity;
+    private final AuthorizationProvider authorizationProvider;
+
+    public ResourceAdapter(ResourceEntity entity, MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        super(invocationContext);
+        this.entity = entity;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public String getId() {
+        return getMongoEntity().getId();
+    }
+
+    @Override
+    public String getName() {
+        return getMongoEntity().getName();
+    }
+
+    @Override
+    public void setName(String name) {
+        getMongoEntity().setName(name);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getUri() {
+        return getMongoEntity().getUri();
+    }
+
+    @Override
+    public void setUri(String uri) {
+        getMongoEntity().setUri(uri);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getType() {
+        return getMongoEntity().getType();
+    }
+
+    @Override
+    public void setType(String type) {
+        getMongoEntity().setType(type);
+        updateMongoEntity();
+    }
+
+    @Override
+    public List<Scope> getScopes() {
+        return getMongoEntity().getScopes().stream()
+                .map(id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
+                .collect(toList());
+    }
+
+    @Override
+    public String getIconUri() {
+        return getMongoEntity().getIconUri();
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        getMongoEntity().setIconUri(iconUri);
+        updateMongoEntity();
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        return this.authorizationProvider.getStoreFactory().getResourceServerStore().findById(getMongoEntity().getResourceServerId());
+    }
+
+    @Override
+    public String getOwner() {
+        return getMongoEntity().getOwner();
+    }
+
+    @Override
+    public void updateScopes(Set<Scope> scopes) {
+        getMongoEntity().updateScopes(scopes);
+        updateMongoEntity();
+    }
+
+    @Override
+    protected ResourceEntity getMongoEntity() {
+        return this.entity;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceServerAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceServerAdapter.java
new file mode 100644
index 0000000..72feedb
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ResourceServerAdapter.java
@@ -0,0 +1,56 @@
+package org.keycloak.authorization.mongo.adapter;
+
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.mongo.entities.ResourceServerEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.adapters.AbstractMongoAdapter;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceServerAdapter extends AbstractMongoAdapter<ResourceServerEntity> implements ResourceServer{
+
+    private final ResourceServerEntity entity;
+
+    public ResourceServerAdapter(ResourceServerEntity entity, MongoStoreInvocationContext invocationContext) {
+        super(invocationContext);
+        this.entity = entity;
+    }
+
+    @Override
+    public String getId() {
+        return getMongoEntity().getId();
+    }
+
+    @Override
+    public String getClientId() {
+        return getMongoEntity().getClientId();
+    }
+
+    @Override
+    public boolean isAllowRemoteResourceManagement() {
+        return getMongoEntity().isAllowRemoteResourceManagement();
+    }
+
+    @Override
+    public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+        getMongoEntity().setAllowRemoteResourceManagement(allowRemoteResourceManagement);
+        updateMongoEntity();
+    }
+
+    @Override
+    public PolicyEnforcementMode getPolicyEnforcementMode() {
+        return getMongoEntity().getPolicyEnforcementMode();
+    }
+
+    @Override
+    public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
+        getMongoEntity().setPolicyEnforcementMode(enforcementMode);
+        updateMongoEntity();
+    }
+
+    @Override
+    protected ResourceServerEntity getMongoEntity() {
+        return this.entity;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ScopeAdapter.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ScopeAdapter.java
new file mode 100644
index 0000000..72196ca
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/adapter/ScopeAdapter.java
@@ -0,0 +1,60 @@
+package org.keycloak.authorization.mongo.adapter;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.mongo.entities.ScopeEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.adapters.AbstractMongoAdapter;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeAdapter extends AbstractMongoAdapter<ScopeEntity> implements Scope {
+
+    private final ScopeEntity entity;
+    private final AuthorizationProvider authorizationProvider;
+
+    public ScopeAdapter(ScopeEntity entity, MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        super(invocationContext);
+        this.entity = entity;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public String getId() {
+        return getMongoEntity().getId();
+    }
+
+    @Override
+    public String getName() {
+        return getMongoEntity().getName();
+    }
+
+    @Override
+    public void setName(String name) {
+        getMongoEntity().setName(name);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getIconUri() {
+        return getMongoEntity().getIconUri();
+    }
+
+    @Override
+    public void setIconUri(String iconUri) {
+        getMongoEntity().setIconUri(iconUri);
+        updateMongoEntity();
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        return this.authorizationProvider.getStoreFactory().getResourceServerStore().findById(getMongoEntity().getResourceServerId());
+    }
+
+    @Override
+    protected ScopeEntity getMongoEntity() {
+        return this.entity;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/PolicyEntity.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/PolicyEntity.java
new file mode 100644
index 0000000..9230b88
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/PolicyEntity.java
@@ -0,0 +1,166 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.entities;
+
+import org.keycloak.authorization.model.Policy.DecisionStrategy;
+import org.keycloak.authorization.model.Policy.Logic;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@MongoCollection(collectionName = "policies")
+public class PolicyEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String name;
+
+    private String description;
+
+    private String type;
+
+    private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
+
+    private Logic logic = Logic.POSITIVE;
+
+    private Map<String, String> config = new HashMap();
+
+    private String resourceServerId;
+
+    private Set<String> associatedPolicies = new HashSet<>();
+
+    private Set<String> resources = new HashSet<>();
+
+    private Set<String> scopes = new HashSet<>();
+
+    public String getType() {
+        return this.type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public DecisionStrategy getDecisionStrategy() {
+        return this.decisionStrategy;
+    }
+
+    public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+        this.decisionStrategy = decisionStrategy;
+    }
+
+    public Logic getLogic() {
+        return this.logic;
+    }
+
+    public void setLogic(Logic logic) {
+        this.logic = logic;
+    }
+
+    public Map<String, String> getConfig() {
+        return this.config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+
+    public void setResourceServerId(String resourceServerId) {
+        this.resourceServerId = resourceServerId;
+    }
+
+    public Set<String> getAssociatedPolicies() {
+        return this.associatedPolicies;
+    }
+
+    public void setAssociatedPolicies(Set<String> associatedPolicies) {
+        this.associatedPolicies = associatedPolicies;
+    }
+
+    public Set<String> getResources() {
+        return this.resources;
+    }
+
+    public void setResources(Set<String> resources) {
+        this.resources = resources;
+    }
+
+    public Set<String> getScopes() {
+        return this.scopes;
+    }
+
+    public void setScopes(Set<String> scopes) {
+        this.scopes = scopes;
+    }
+
+    public void addScope(String scopeId) {
+        getScopes().add(scopeId);
+    }
+
+    public void removeScope(String scopeId) {
+        getScopes().remove(scopeId);
+    }
+
+    public void addAssociatedPolicy(String policyId) {
+        getAssociatedPolicies().add(policyId);
+    }
+
+    public void removeAssociatedPolicy(String policyId) {
+        getAssociatedPolicies().remove(policyId);
+    }
+
+    public void addResource(String resourceId) {
+        getResources().add(resourceId);
+    }
+
+    public void removeResource(String resourceId) {
+        getResources().remove(resourceId);
+    }
+
+    public void afterRemove(MongoStoreInvocationContext invocationContext) {
+
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceEntity.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceEntity.java
new file mode 100644
index 0000000..b2e15da
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceEntity.java
@@ -0,0 +1,142 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.entities;
+
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@MongoCollection(collectionName = "resources")
+public class ResourceEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String name;
+
+    private String uri;
+
+    private String type;
+
+    private String iconUri;
+
+    private String owner;
+
+    private String resourceServerId;
+
+    private List<String> scopes = new ArrayList<>();
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public List<String> getScopes() {
+        return this.scopes;
+    }
+
+    public void setScopes(List<String> scopes) {
+        this.scopes = scopes;
+    }
+
+    public String getIconUri() {
+        return iconUri;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public String getResourceServerId() {
+        return resourceServerId;
+    }
+
+    public void setResourceServerId(String resourceServerId) {
+        this.resourceServerId = resourceServerId;
+    }
+
+    public String getOwner() {
+        return this.owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public void updateScopes(Set<Scope> toUpdate) {
+        for (Scope scope : toUpdate) {
+            boolean hasScope = false;
+
+            for (String existingScope : this.scopes) {
+                if (existingScope.equals(scope.getId())) {
+                    hasScope = true;
+                }
+            }
+
+            if (!hasScope) {
+                this.scopes.add(scope.getId());
+            }
+        }
+
+        for (String scopeId : new HashSet<String>(this.scopes)) {
+            boolean hasScope = false;
+
+            for (Scope scope : toUpdate) {
+                if (scopeId.equals(scope.getId())) {
+                    hasScope = true;
+                }
+            }
+
+            if (!hasScope) {
+                this.scopes.remove(scopeId);
+            }
+        }
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext invocationContext) {
+
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceServerEntity.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceServerEntity.java
new file mode 100644
index 0000000..7013e1b
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ResourceServerEntity.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.entities;
+
+import org.keycloak.authorization.model.ResourceServer.PolicyEnforcementMode;
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@MongoCollection(collectionName = "resource-servers")
+public class ResourceServerEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String clientId;
+
+    private boolean allowRemoteResourceManagement;
+
+    private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
+
+    public String getClientId() {
+        return this.clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public boolean isAllowRemoteResourceManagement() {
+        return this.allowRemoteResourceManagement;
+    }
+
+    public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+        this.allowRemoteResourceManagement = allowRemoteResourceManagement;
+    }
+
+    public PolicyEnforcementMode getPolicyEnforcementMode() {
+        return this.policyEnforcementMode;
+    }
+
+    public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
+        this.policyEnforcementMode = policyEnforcementMode;
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext invocationContext) {
+
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ScopeEntity.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ScopeEntity.java
new file mode 100644
index 0000000..152127d
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/entities/ScopeEntity.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.entities;
+
+import org.keycloak.connections.mongo.api.MongoCollection;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@MongoCollection(collectionName = "scopes")
+public class ScopeEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String name;
+
+    private String iconUri;
+
+    private String resourceServerId;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getIconUri() {
+        return iconUri;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public String getResourceServerId() {
+        return resourceServerId;
+    }
+
+    public void setResourceServerId(String resourceServerId) {
+        this.resourceServerId = resourceServerId;
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext invocationContext) {
+
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoAuthorizationStoreFactory.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoAuthorizationStoreFactory.java
new file mode 100644
index 0000000..9a484ad
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoAuthorizationStoreFactory.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.store.AuthorizationStoreFactory;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoAuthorizationStoreFactory implements AuthorizationStoreFactory {
+    @Override
+    public StoreFactory  create(KeycloakSession session) {
+        MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
+        AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
+        return new MongoStoreFactory(connection.getInvocationContext(), provider);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "mongo";
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
new file mode 100644
index 0000000..6f0ba5d
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoPolicyStore.java
@@ -0,0 +1,171 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.mongo.adapter.PolicyAdapter;
+import org.keycloak.authorization.mongo.entities.PolicyEntity;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoPolicyStore implements PolicyStore {
+
+    private final MongoStoreInvocationContext invocationContext;
+    private final AuthorizationProvider authorizationProvider;
+
+    public MongoPolicyStore(MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        this.invocationContext = invocationContext;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public Policy create(String name, String type, ResourceServer resourceServer) {
+        PolicyEntity entity = new PolicyEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setType(type);
+        entity.setResourceServerId(resourceServer.getId());
+
+        getMongoStore().insertEntity(entity, getInvocationContext());
+
+        return new PolicyAdapter(entity, getInvocationContext(), this.authorizationProvider) ;
+    }
+
+    @Override
+    public void delete(String id) {
+        getMongoStore().removeEntity(PolicyEntity.class, id, getInvocationContext());
+    }
+
+    @Override
+    public Policy findById(String id) {
+        PolicyEntity entity = getMongoStore().loadEntity(PolicyEntity.class, id, getInvocationContext());
+
+        if (entity == null) {
+            return null;
+        }
+
+        return new PolicyAdapter(entity, getInvocationContext(), this.authorizationProvider);
+    }
+
+
+    @Override
+    public Policy findByName(String name, String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .and("name").is(name)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
+    }
+
+    @Override
+    public List<Policy> findByResourceServer(String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public List<Policy> findByResource(String resourceId) {
+        DBObject query = new QueryBuilder()
+                .and("resources").is(resourceId)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .filter(policyEntity -> {
+                    String defaultResourceType = policyEntity.getConfig().get("defaultResourceType");
+                    return defaultResourceType != null && defaultResourceType.equals(resourceType);
+                })
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .and("scopes").in(scopeIds)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public List<Policy> findByType(String type) {
+        DBObject query = new QueryBuilder()
+                .and("type").is(type)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public List<Policy> findDependentPolicies(String policyId) {
+        DBObject query = new QueryBuilder()
+                .and("associatedPolicies").is(policyId)
+                .get();
+
+        return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    private MongoStoreInvocationContext getInvocationContext() {
+        return this.invocationContext;
+    }
+
+    private MongoStore getMongoStore() {
+        return getInvocationContext().getMongoStore();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceServerStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceServerStore.java
new file mode 100644
index 0000000..25e5f67
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceServerStore.java
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.mongo.adapter.ResourceServerAdapter;
+import org.keycloak.authorization.mongo.entities.ResourceServerEntity;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoResourceServerStore implements ResourceServerStore {
+
+    private final MongoStoreInvocationContext invocationContext;
+    private final AuthorizationProvider authorizationProvider;
+
+    public MongoResourceServerStore(MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        this.invocationContext = invocationContext;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public ResourceServer create(String clientId) {
+        ResourceServerEntity entity = new ResourceServerEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setClientId(clientId);
+
+        getMongoStore().insertEntity(entity, getInvocationContext());
+
+        return new ResourceServerAdapter(entity, getInvocationContext());
+    }
+
+    @Override
+    public void delete(String id) {
+        getMongoStore().removeEntity(ResourceServerEntity.class, id, getInvocationContext());
+    }
+
+    @Override
+    public ResourceServer findById(String id) {
+        ResourceServerEntity entity = getMongoStore().loadEntity(ResourceServerEntity.class, id, getInvocationContext());
+
+        if (entity == null) {
+            return null;
+        }
+
+        return new ResourceServerAdapter(entity, getInvocationContext());
+    }
+
+    @Override
+    public ResourceServer findByClient(String clientId) {
+        DBObject query = new QueryBuilder()
+                .and("clientId").is(clientId)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceServerEntity.class, query, getInvocationContext()).stream()
+                .map(resourceServer -> findById(resourceServer.getId())).findFirst().orElse(null);
+    }
+
+    private MongoStoreInvocationContext getInvocationContext() {
+        return this.invocationContext;
+    }
+
+    private MongoStore getMongoStore() {
+        return getInvocationContext().getMongoStore();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
new file mode 100644
index 0000000..11b735b
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoResourceStore.java
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.mongo.adapter.ResourceAdapter;
+import org.keycloak.authorization.mongo.entities.ResourceEntity;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoResourceStore implements ResourceStore {
+
+    private final MongoStoreInvocationContext invocationContext;
+    private final AuthorizationProvider authorizationProvider;
+
+    public MongoResourceStore(MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        this.invocationContext = invocationContext;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public Resource create(String name, ResourceServer resourceServer, String owner) {
+        ResourceEntity entity = new ResourceEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setResourceServerId(resourceServer.getId());
+        entity.setOwner(owner);
+
+        getMongoStore().insertEntity(entity, getInvocationContext());
+
+        return new ResourceAdapter(entity, getInvocationContext(), this.authorizationProvider);
+    }
+
+    @Override
+    public void delete(String id) {
+        getMongoStore().removeEntity(ResourceEntity.class, id, getInvocationContext());
+    }
+
+    @Override
+    public Resource findById(String id) {
+        ResourceEntity entity = getMongoStore().loadEntity(ResourceEntity.class, id, getInvocationContext());
+
+        if (entity == null) {
+            return null;
+        }
+
+        return new ResourceAdapter(entity, getInvocationContext(), this.authorizationProvider);
+    }
+
+    @Override
+    public List<Resource> findByOwner(String ownerId) {
+        DBObject query = new QueryBuilder()
+                .and("owner").is(ownerId)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
+                .map(scope -> findById(scope.getId())).collect(toList());
+    }
+
+    @Override
+    public List findByResourceServer(String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
+                .map(scope -> findById(scope.getId())).collect(toList());
+    }
+
+    @Override
+    public List<Resource> findByScope(String... id) {
+        DBObject query = new QueryBuilder()
+                .and("scopes.id").in(id)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    @Override
+    public Resource findByName(String name, String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("name").is(name)
+                .and("resourceServerId").is(resourceServerId)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
+    }
+
+    @Override
+    public List<Resource> findByType(String type) {
+        DBObject query = new QueryBuilder()
+                .and("type").is(type)
+                .get();
+
+        return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    private MongoStoreInvocationContext getInvocationContext() {
+        return this.invocationContext;
+    }
+
+    private MongoStore getMongoStore() {
+        return getInvocationContext().getMongoStore();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
new file mode 100644
index 0000000..e57b69b
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoScopeStore.java
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.mongo.adapter.ScopeAdapter;
+import org.keycloak.authorization.mongo.entities.ScopeEntity;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.connections.mongo.api.MongoStore;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoScopeStore implements ScopeStore {
+
+    private final MongoStoreInvocationContext invocationContext;
+    private final AuthorizationProvider authorizationProvider;
+
+    public MongoScopeStore(MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        this.invocationContext = invocationContext;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public Scope create(final String name, final ResourceServer resourceServer) {
+        ScopeEntity entity = new ScopeEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setName(name);
+        entity.setResourceServerId(resourceServer.getId());
+
+        getMongoStore().insertEntity(entity, getInvocationContext());
+
+        return new ScopeAdapter(entity, getInvocationContext(), this.authorizationProvider);
+    }
+
+    @Override
+    public void delete(String id) {
+        getMongoStore().removeEntity(ScopeEntity.class, id, getInvocationContext());
+    }
+
+    @Override
+    public Scope findById(String id) {
+        ScopeEntity entity = getMongoStore().loadEntity(ScopeEntity.class, id, getInvocationContext());
+
+        if (entity == null) {
+            return null;
+        }
+
+        return new ScopeAdapter(entity, getInvocationContext(), this.authorizationProvider);
+    }
+
+    @Override
+    public Scope findByName(String name, String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .and("name").is(name)
+                .get();
+
+        return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
+                .map(scope -> findById(scope.getId())).findFirst().orElse(null);
+    }
+
+    @Override
+    public List<Scope> findByResourceServer(String resourceServerId) {
+        DBObject query = new QueryBuilder()
+                .and("resourceServerId").is(resourceServerId)
+                .get();
+
+        return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
+                .map(policyEntity -> findById(policyEntity.getId()))
+                .collect(toList());
+    }
+
+    private MongoStoreInvocationContext getInvocationContext() {
+        return this.invocationContext;
+    }
+
+    private MongoStore getMongoStore() {
+        return getInvocationContext().getMongoStore();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoStoreFactory.java b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoStoreFactory.java
new file mode 100644
index 0000000..7a94ba5
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/authorization/mongo/store/MongoStoreFactory.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.mongo.store;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MongoStoreFactory implements StoreFactory {
+
+    private final MongoStoreInvocationContext invocationContext;
+    private final AuthorizationProvider authorizationProvider;
+
+    public MongoStoreFactory(MongoStoreInvocationContext invocationContext, AuthorizationProvider authorizationProvider) {
+        this.invocationContext = invocationContext;
+        this.authorizationProvider = authorizationProvider;
+    }
+
+    @Override
+    public PolicyStore getPolicyStore() {
+        return new MongoPolicyStore(this.invocationContext, this.authorizationProvider);
+    }
+
+    @Override
+    public ResourceServerStore getResourceServerStore() {
+        return new MongoResourceServerStore(this.invocationContext, this.authorizationProvider);
+    }
+
+    @Override
+    public ResourceStore getResourceStore() {
+        return new MongoResourceStore(this.invocationContext, this.authorizationProvider);
+    }
+
+    @Override
+    public ScopeStore getScopeStore() {
+        return new MongoScopeStore(this.invocationContext, this.authorizationProvider);
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index 3bc9c7a..7fc3439 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -22,6 +22,7 @@ import java.net.UnknownHostException;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.net.ssl.SSLSocketFactory;
 
@@ -33,6 +34,8 @@ import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocati
 import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.dblock.DBLockManager;
+import org.keycloak.models.dblock.DBLockProvider;
 import org.keycloak.provider.ServerInfoAwareProviderFactory;
 
 import com.mongodb.DB;
@@ -74,6 +77,10 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
             "org.keycloak.models.entities.RequiredActionProviderEntity",
             "org.keycloak.models.entities.PersistentUserSessionEntity",
             "org.keycloak.models.entities.PersistentClientSessionEntity",
+            "org.keycloak.authorization.mongo.entities.PolicyEntity",
+            "org.keycloak.authorization.mongo.entities.ResourceEntity",
+            "org.keycloak.authorization.mongo.entities.ResourceServerEntity",
+            "org.keycloak.authorization.mongo.entities.ScopeEntity"
     };
 
     private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
@@ -166,6 +173,11 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
                 throw new RuntimeException("Can't update database: Mongo updater provider not found");
             }
 
+            DBLockProvider dbLock = new DBLockManager(session).getDBLock();
+            if (!dbLock.hasLock()) {
+                throw new IllegalStateException("Trying to update database, but don't have a DB lock acquired");
+            }
+
             if (databaseSchema.equals("update")) {
                 mongoUpdater.update(session, db);
             } else if (databaseSchema.equals("validate")) {
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
index 1b36889..a11f729 100644
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
@@ -91,6 +91,7 @@ public class MongoDBLockProvider implements DBLockProvider {
             WriteResult wr = db.getCollection(DB_LOCK_COLLECTION).update(query, update, true, false);
             if (wr.getN() == 1) {
                 logger.debugf("Successfully acquired DB lock");
+                factory.setHasLock(true);
                 return true;
             } else {
                 return false;
@@ -115,6 +116,7 @@ public class MongoDBLockProvider implements DBLockProvider {
         try {
             WriteResult wr = db.getCollection(DB_LOCK_COLLECTION).update(query, update, true, false);
             if (wr.getN() > 0) {
+                factory.setHasLock(false);
                 logger.debugf("Successfully released DB lock");
             } else {
                 logger.warnf("Attempt to release DB lock, but nothing was released");
@@ -125,6 +127,11 @@ public class MongoDBLockProvider implements DBLockProvider {
     }
 
     @Override
+    public boolean hasLock() {
+        return factory.hasLock();
+    }
+
+    @Override
     public boolean supportsForcedUnlock() {
         return true;
     }
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProviderFactory.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProviderFactory.java
index 7bd6e02..64f65f6 100644
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProviderFactory.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProviderFactory.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.connections.mongo.lock;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
 import com.mongodb.DB;
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
@@ -37,6 +39,9 @@ public class MongoDBLockProviderFactory implements DBLockProviderFactory {
     private long lockRecheckTimeMillis;
     private long lockWaitTimeoutMillis;
 
+    // True if this node has a lock acquired
+    private AtomicBoolean hasLock = new AtomicBoolean(false);
+
     protected long getLockRecheckTimeMillis() {
         return lockRecheckTimeMillis;
     }
@@ -81,4 +86,13 @@ public class MongoDBLockProviderFactory implements DBLockProviderFactory {
     public String getId() {
         return "mongo";
     }
+
+    public boolean hasLock() {
+        return hasLock.get();
+    }
+
+    public void setHasLock(boolean hasLock) {
+        this.hasLock.set(hasLock);
+    }
+
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
index cef8a5a..14e5b03 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
@@ -129,10 +129,26 @@ public class MongoRealmProvider implements RealmProvider {
 
     @Override
     public boolean removeRealm(String id) {
-        RealmModel realm = getRealm(id);
+        final RealmModel realm = getRealm(id);
         if (realm == null) return false;
         session.users().preRemove(realm);
-        return getMongoStore().removeEntity(MongoRealmEntity.class, id, invocationContext);
+        boolean removed = getMongoStore().removeEntity(MongoRealmEntity.class, id, invocationContext);
+
+        if (removed) {
+            session.getKeycloakSessionFactory().publish(new RealmModel.RealmRemovedEvent() {
+                @Override
+                public RealmModel getRealm() {
+                    return realm;
+                }
+
+                @Override
+                public KeycloakSession getKeycloakSession() {
+                    return session;
+                }
+            });
+        }
+
+        return removed;
     }
 
     protected MongoStore getMongoStore() {
@@ -408,12 +424,27 @@ public class MongoRealmProvider implements RealmProvider {
     @Override
     public boolean removeClient(String id, RealmModel realm) {
         if (id == null) return false;
-        ClientModel client = getClientById(id, realm);
+        final ClientModel client = getClientById(id, realm);
         if (client == null) return false;
 
         session.users().preRemove(realm, client);
+        boolean removed = getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext);
+
+        if (removed) {
+            session.getKeycloakSessionFactory().publish(new RealmModel.ClientRemovedEvent() {
+                @Override
+                public ClientModel getClient() {
+                    return client;
+                }
+
+                @Override
+                public KeycloakSession getKeycloakSession() {
+                    return session;
+                }
+            });
+        }
 
-        return getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext);
+        return removed;
     }
 
     @Override
diff --git a/model/mongo/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory b/model/mongo/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory
new file mode 100644
index 0000000..e1d801c
--- /dev/null
+++ b/model/mongo/src/main/resources/META-INF/services/org.keycloak.authorization.store.AuthorizationStoreFactory
@@ -0,0 +1,37 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.authorization.mongo.store.MongoAuthorizationStoreFactory
\ No newline at end of file

pom.xml 41(+39 -2)

diff --git a/pom.xml b/pom.xml
index 38efdcf..42df2d8 100755
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,9 @@
         <wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
         <xmlsec.version>2.0.5</xmlsec.version>
 
+        <!-- Authorization Drools Policy Provider -->
+        <version.org.drools>6.4.0.Final</version.org.drools>
+
         <!-- Others -->
         <apacheds.version>2.0.0-M17</apacheds.version>
         <apacheds.codec.version>1.0.0-M23</apacheds.codec.version>
@@ -99,6 +102,7 @@
         <picketlink.version>2.7.0.Final</picketlink.version>
         <selenium.version>2.35.0</selenium.version>
         <xml-apis.version>1.4.01</xml-apis.version>
+        <subethasmtp.version>3.1.7</subethasmtp.version>
 
         <!-- Maven Plugins -->
         <embedmongo.plugin.version>0.1.12</embedmongo.plugin.version>
@@ -176,6 +180,7 @@
         <module>wildfly</module>
         <module>integration</module>
         <module>adapters</module>
+        <module>authz</module>
         <module>examples</module>
         <module>testsuite</module>
     </modules>
@@ -412,6 +417,15 @@
                 <version>${google.zxing.version}</version>
             </dependency>
 
+            <!-- Authorization Drools Policy Provider -->
+            <dependency>
+                <groupId>org.drools</groupId>
+                <artifactId>drools-bom</artifactId>
+                <type>pom</type>
+                <version>${version.org.drools}</version>
+                <scope>import</scope>
+            </dependency>
+
             <!-- Email Test Servers -->
             <dependency>
                 <groupId>com.icegreen</groupId>
@@ -419,6 +433,12 @@
                 <version>${greenmail.version}</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.subethamail</groupId>
+                <artifactId>subethasmtp</artifactId>
+                <version>${subethasmtp.version}</version>
+                <scope>test</scope>
+            </dependency>
 
             <!-- Apache DS -->
             <dependency>
@@ -976,6 +996,24 @@
                 <artifactId>keycloak-services</artifactId>
                 <version>${project.version}</version>
             </dependency>
+
+            <!-- Authorization -->
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-authz-client</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-authz-policy-drools</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-authz-policy-common</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.keycloak</groupId>
                 <artifactId>keycloak-saml-as7-modules</artifactId>
@@ -1043,7 +1081,7 @@
             </dependency>
             <dependency>
                 <groupId>org.keycloak</groupId>
-                <artifactId>keycloak-docs-dist</artifactId>
+                <artifactId>keycloak-api-docs-dist</artifactId>
                 <version>${project.version}</version>
                 <type>zip</type>
             </dependency>
@@ -1357,7 +1395,6 @@
         <profile>
             <id>jboss-release</id>
             <modules>
-                <module>docbook</module>
                 <module>distribution</module>
             </modules>
         </profile>
diff --git a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
index e65b788..4d83d39 100755
--- a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
+++ b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
@@ -27,9 +27,9 @@ public class SPMetadataDescriptor {
                 "<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
                 "    <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\"\n" +
                 "            protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
+                "        <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
                 "        <NameIDFormat>" + nameIDPolicyFormat + "\n" +
                 "        </NameIDFormat>\n" +
-                "        <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
                 "        <AssertionConsumerService\n" +
                 "                Binding=\"" + binding + "\" Location=\"" + assertionEndpoint + "\"\n" +
                 "                index=\"1\" isDefault=\"true\" />\n";
diff --git a/server-spi/pom.xml b/server-spi/pom.xml
index 047a065..9b71e2d 100755
--- a/server-spi/pom.xml
+++ b/server-spi/pom.xml
@@ -30,6 +30,11 @@
     <name>Keycloak Server SPI</name>
     <description/>
 
+    <properties>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.jboss.resteasy</groupId>
diff --git a/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java b/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java
new file mode 100644
index 0000000..ce2bc51
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java
@@ -0,0 +1,143 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.attribute;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * <p>Holds attributes, their values and provides utlity methods to manage them.
+ *
+ * <p>In the future, it may be useful to provide different implementations for this interface in order to plug or integrate with different
+ * Policy Information Point (PIP).</p>
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Attributes {
+
+    static Attributes from(Map<String, Collection<String>> attributes) {
+        return () -> attributes;
+    }
+
+    /**
+     * Converts to a {@link Map}.
+     *
+     * @return
+     */
+    Map<String, Collection<String>> toMap();
+
+    /**
+     * Checks if there is an attribute with the given <code>name</code>.
+     *
+     * @param name the attribute name
+     * @return true if any attribute with <code>name</code> exist. Otherwise, returns false.
+     */
+    default boolean exists(String name) {
+        return toMap().containsKey(name);
+    }
+
+    /**
+     * Checks if there is an attribute with the given <code>name</code> and <code>value</code>.
+     *
+     * @param name the attribute name
+     * @param value the attribute value
+     * @return true if any attribute with <code>name</code> and <code>value</code> exist. Otherwise, returns false.
+     */
+    default boolean containsValue(String name, String value) {
+        return toMap().getOrDefault(name, emptyList()).stream().anyMatch(value::equals);
+    }
+
+    /**
+     * Returns a {@link Entry} from where values can be obtained and parsed accordingly.
+     *
+     * @param name the attribute name
+     * @return an {@link Entry} holding the values for an attribute
+     */
+    default Entry getValue(String name) {
+        Collection<String> value = toMap().get(name);
+
+        if (value != null) {
+            return new Entry(name, value);
+        }
+
+        return null;
+    }
+
+    /**
+     * Holds an attribute and its values, providing useful methods for obtaining and formatting values. Specially useful
+     * for writing rule-based policies.
+     */
+    class Entry {
+
+        private final String[] values;
+        private final String name;
+
+        Entry(String name, Collection<String> values) {
+            this.name = name;
+            this.values = values.toArray(new String[values.size()]);
+        }
+
+        private String getName() {
+            return this.name;
+        }
+
+        public int size() {
+            return values.length;
+        }
+
+        public String asString(int idx) {
+            if (idx >= values.length) {
+                throw new IllegalArgumentException("Invalid index [" + idx + "]. Values are [" + values + "].");
+            }
+
+            return values[idx];
+        }
+
+        public int asInt(int idx) {
+            return Integer.parseInt(asString(idx));
+        }
+
+        public Date asDate(int idx, String pattern) {
+            try {
+                return new SimpleDateFormat(pattern).parse(asString(idx));
+            } catch (ParseException e) {
+                throw new RuntimeException("Error parsing date.", e);
+            }
+        }
+
+        public InetAddress asInetAddress(int idx) {
+            try {
+                return InetAddress.getByName(asString(idx));
+            } catch (UnknownHostException e) {
+                throw new RuntimeException("Error parsing address.", e);
+            }
+        }
+
+        public long asLong(int idx) {
+            return Long.parseLong(asString(idx));
+        }
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/attribute/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/attribute/package-info.java
new file mode 100644
index 0000000..c75b520
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/attribute/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides classes related with the representation of attributes and their manipulation.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.attribute;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
new file mode 100644
index 0000000..ff646bb
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -0,0 +1,137 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.keycloak.authorization.permission.evaluator.Evaluators;
+import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+/**
+ * <p>The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances.  Usually
+ * an application has a single {@link AuthorizationProvider} instance and threads servicing client requests obtain {@link org.keycloak.authorization.core.permission.evaluator.PermissionEvaluator}
+ * from the {@link #evaluators()} method.
+ *
+ * <p>The internal state of a {@link AuthorizationProvider} is immutable.  This internal state includes all of the metadata
+ * used during the evaluation of policies.
+ *
+ * <p>Once created, {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances can be obtained from the {@link #evaluators()} method:
+ *
+ * <pre>
+ *     List<ResourcePermission> permissionsToEvaluate = getPermissions(); // the permissions to evaluate
+ *     EvaluationContext evaluationContext = createEvaluationContext(); // the context with runtime environment information
+ *     PermissionEvaluator evaluator = authorization.evaluators().from(permissionsToEvaluate, context);
+ *
+ *     evaluator.evaluate(new Decision() {
+ *
+ *         public void onDecision(Evaluation evaluation) {
+ *              // do something on grant
+ *         }
+ *
+ *     });
+ * </pre>
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public final class AuthorizationProvider implements Provider {
+
+    private final DefaultPolicyEvaluator policyEvaluator;
+    private final Executor scheduller;
+    private final StoreFactory storeFactory;
+    private final List<PolicyProviderFactory> policyProviderFactories;
+    private final KeycloakSession keycloakSession;
+
+    public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory, Executor scheduller) {
+        this.keycloakSession = session;
+        this.storeFactory = storeFactory;
+        this.scheduller = scheduller;
+        this.policyProviderFactories = configurePolicyProviderFactories(session);
+        this.policyEvaluator = new DefaultPolicyEvaluator(this, this.policyProviderFactories);
+    }
+
+    public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory) {
+        this(session, storeFactory, Runnable::run);
+    }
+
+    /**
+     * Returns a {@link Evaluators} instance from where {@link org.keycloak.authorization.policy.evaluation.PolicyEvaluator} instances
+     * can be obtained.
+     *
+     * @return a {@link Evaluators} instance
+     */
+    public Evaluators evaluators() {
+        return new Evaluators(this.policyProviderFactories, this.policyEvaluator, this.scheduller);
+    }
+
+    /**
+     * Returns a {@link StoreFactory}.
+     *
+     * @return the {@link StoreFactory}
+     */
+    public StoreFactory getStoreFactory() {
+        return this.storeFactory;
+    }
+
+    /**
+     * Returns the registered {@link PolicyProviderFactory}.
+     *
+     * @return a {@link List} containing all registered {@link PolicyProviderFactory}
+     */
+    public List<PolicyProviderFactory> getProviderFactories() {
+        return this.policyProviderFactories;
+    }
+
+    /**
+     * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
+     *
+     * @param type the type of the policy provider
+     * @param <F> the expected type of the provider
+     * @return a {@link PolicyProviderFactory} with the given <code>type</code>
+     */
+    public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
+        return (F) getProviderFactories().stream().filter(policyProviderFactory -> policyProviderFactory.getId().equals(type)).findFirst().orElse(null);
+    }
+
+    public KeycloakSession getKeycloakSession() {
+        return this.keycloakSession;
+    }
+
+    private List<PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSession session) {
+        List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class);
+
+        if (providerFactories.isEmpty()) {
+            throw new RuntimeException("Could not find any policy provider.");
+        }
+
+        return providerFactories.stream().map(providerFactory -> (PolicyProviderFactory) providerFactory).collect(Collectors.toList());
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java
new file mode 100644
index 0000000..ae4dcc2
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface AuthorizationProviderFactory extends ProviderFactory<AuthorizationProvider> {
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationSpi.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationSpi.java
new file mode 100644
index 0000000..65028b3
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationSpi.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationSpi implements Spi {
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "authorization";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return AuthorizationProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return AuthorizationProviderFactory.class;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/Decision.java b/server-spi/src/main/java/org/keycloak/authorization/Decision.java
new file mode 100644
index 0000000..6ebd086
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/Decision.java
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Decision<D extends Evaluation> {
+
+    enum Effect {
+        PERMIT,
+        DENY
+    }
+
+    void onDecision(D evaluation);
+
+    default void onError(Throwable cause) {
+        throw new RuntimeException("Not implemented.", cause);
+    }
+
+    default void onComplete() {
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/identity/Identity.java b/server-spi/src/main/java/org/keycloak/authorization/identity/Identity.java
new file mode 100644
index 0000000..ebc9679
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/identity/Identity.java
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.identity;
+
+import org.keycloak.authorization.attribute.Attributes;
+
+/**
+ * <p>Represents a security identity, which can be a person or non-person entity that was previously authenticated.
+ *
+ * <p>An {@link Identity} plays an important role during the evaluation of policies as they represent the entity to which one or more permissions
+ * should be granted or not, providing additional information and attributes that can be relevant to the different
+ * access control methods involved during the evaluation of policies.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Identity {
+
+    /**
+     * Returns the unique identifier of this identity.
+     *
+     * @return the unique identifier of this identity
+     */
+    String getId();
+
+    /**
+     * Returns the attributes or claims associated with this identity.
+     *
+     * @return the attributes or claims associated with this identity
+     */
+    Attributes getAttributes();
+
+    /**
+     * Indicates if this identity is granted with a role with the given <code>roleName</code>.
+     *
+     * @param roleName the name of the role
+     *
+     * @return true if the identity has the given role. Otherwise, it returns false.
+     */
+    default boolean hasRole(String roleName) {
+        return getAttributes().containsValue("roles", roleName);
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/identity/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/identity/package-info.java
new file mode 100644
index 0000000..47a5746
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/identity/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides classes related with the representation and management of identities.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.identity;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/model/package-info.java
new file mode 100644
index 0000000..38f9a8f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/model/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides the domain model and any other type related with it
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.model;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Policy.java b/server-spi/src/main/java/org/keycloak/authorization/model/Policy.java
new file mode 100644
index 0000000..1960d6a
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/model/Policy.java
@@ -0,0 +1,193 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.model;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents an authorization policy and all the configuration associated with it.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Policy {
+
+    /**
+     * Returns the unique identifier for this instance.
+     *
+     * @return the unique identifier for this instance
+     */
+    String getId();
+
+    /**
+     * Returns the type of this policy.
+     *
+     * @return the type of this policy
+     */
+    String getType();
+
+    /**
+     * Returns the {@link DecisionStrategy} for this policy.
+     *
+     * @return the decision strategy defined for this policy
+     */
+    DecisionStrategy getDecisionStrategy();
+
+    /**
+     * Sets the {DecisionStrategy} for this policy.
+     *
+     * @return the decision strategy for this policy
+     */
+    void setDecisionStrategy(DecisionStrategy decisionStrategy);
+
+    /**
+     * Returns the {@link Logic} for this policy.
+     *
+     * @return the decision strategy defined for this policy
+     */
+    Logic getLogic();
+
+    /**
+     * Sets the {Logic} for this policy.
+     *
+     * @return the decision strategy for this policy
+     */
+    void setLogic(Logic logic);
+
+    /**
+     * Returns a {@link Map} holding string-based key/value pairs representing any additional configuration for this policy.
+     *
+     * @return a map with any additional configuration defined for this policy.
+     */
+    Map<String, String> getConfig();
+
+    /**
+     * Sets a {@link Map} with string-based key/value pairs representing any additional configuration for this policy.
+     *
+     * @return a map with any additional configuration for this policy.
+     */
+    void setConfig(Map<String, String> config);
+
+    /**
+     * Returns the name of this policy.
+     *
+     * @return the name of this policy
+     */
+    String getName();
+
+    /**
+     * Sets an unique name to this policy.
+     *
+     * @param name an unique name
+     */
+    void setName(String name);
+
+    /**
+     * Returns the description of this policy.
+     *
+     * @return a description or null of there is no description
+     */
+    String getDescription();
+
+    /**
+     * Sets the description for this policy.
+     *
+     * @param description a description
+     */
+    void setDescription(String description);
+
+    /**
+     * Returns the {@link ResourceServer} where this policy belongs to.
+     *
+     * @return a resource server
+     */
+    <R extends ResourceServer> R getResourceServer();
+
+    /**
+     * Returns the {@link Policy} instances associated with this policy and used to evaluate authorization decisions when
+     * this policy applies.
+     *
+     * @return the associated policies or an empty set if no policy is associated with this policy
+     */
+    <P extends Policy> Set<P> getAssociatedPolicies();
+
+    /**
+     * Returns the {@link Resource} instances where this policy applies.
+     *
+     * @return a set with all resource instances where this policy applies. Or an empty set if there is no resource associated with this policy
+     */
+    <R extends Resource> Set<R> getResources();
+
+    /**
+     * Returns the {@link Scope} instances where this policy applies.
+     *
+     * @return a set with all scope instances where this policy applies. Or an empty set if there is no scope associated with this policy
+     */
+    <S extends Scope> Set<S> getScopes();
+
+    void addScope(Scope scope);
+
+    void removeScope(Scope scope);
+
+    void addAssociatedPolicy(Policy associatedPolicy);
+
+    void removeAssociatedPolicy(Policy associatedPolicy);
+
+    void addResource(Resource resource);
+
+    void removeResource(Resource resource);
+
+    /**
+     * The decision strategy dictates how the policies associated with a given policy are evaluated and how a final decision
+     * is obtained.
+     */
+    enum DecisionStrategy {
+        /**
+         * Defines that at least one policy must evaluate to a positive decision in order to the overall decision be also positive.
+         */
+        AFFIRMATIVE,
+
+        /**
+         * Defines that all policies must evaluate to a positive decision in order to the overall decision be also positive.
+         */
+        UNANIMOUS,
+
+        /**
+         * Defines that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same,
+         * the final decision will be negative.
+         */
+        CONSENSUS
+    }
+
+    /**
+     * The decision strategy dictates how the policies associated with a given policy are evaluated and how a final decision
+     * is obtained.
+     */
+    enum Logic {
+        /**
+         * Defines that this policy follows a positive logic. In other words, the final decision is the policy outcome.
+         */
+        POSITIVE,
+
+        /**
+         * Defines that this policy uses a logical negation. In other words, the final decision would be a negative of the policy outcome.
+         */
+        NEGATIVE,
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Resource.java b/server-spi/src/main/java/org/keycloak/authorization/model/Resource.java
new file mode 100644
index 0000000..2bf2c6f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/model/Resource.java
@@ -0,0 +1,116 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.model;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents a resource, which is usually protected by a set of policies within a resource server.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Resource {
+
+    /**
+     * Returns the unique identifier for this instance.
+     *
+     * @return the unique identifier for this instance
+     */
+    String getId();
+
+    /**
+     * Returns the resource's name.
+     *
+     * @return the name of this resource
+     */
+    String getName();
+
+    /**
+     * Sets a name for this resource. The name must be unique.
+     *
+     * @param name the name of this resource
+     */
+    void setName(String name);
+
+    /**
+     * Returns a {@link java.net.URI} that uniquely identify this resource.
+     *
+     * @return an {@link java.net.URI} for this resource or null if not defined.
+     */
+    String getUri();
+
+    /**
+     * Sets a {@link java.net.URI} that uniquely identify this resource.
+     *
+     * @param uri an {@link java.net.URI} for this resource
+     */
+    void setUri(String uri);
+
+    /**
+     * Returns a string representing the type of this resource.
+     *
+     * @return the type of this resource or null if not defined
+     */
+    String getType();
+
+    /**
+     * Sets a string representing the type of this resource.
+     *
+     * @return the type of this resource or null if not defined
+     */
+    void setType(String type);
+
+    /**
+     * Returns a {@link List} containing all the {@link Scope} associated with this resource.
+     *
+     * @return a list with all scopes associated with this resource
+     */
+    <S extends Scope> List<S> getScopes();
+
+    /**
+     * Returns an icon {@link java.net.URI} for this resource.
+     *
+     * @return a uri for an icon
+     */
+    String getIconUri();
+
+    /**
+     * Sets an icon {@link java.net.URI} for this resource.
+     *
+     * @return a uri for an icon
+     */
+    void setIconUri(String iconUri);
+
+    /**
+     * Returns the {@link ResourceServer} to where this resource belongs to.
+     *
+     * @return the resource server associated with this resource
+     */
+    <R extends ResourceServer> R getResourceServer();
+
+    /**
+     * Returns the resource's owner, which is usually an identifier that uniquely identifies the resource's owner.
+     *
+     * @return the owner of this resource
+     */
+    String getOwner();
+
+    void updateScopes(Set<Scope> scopes);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/ResourceServer.java b/server-spi/src/main/java/org/keycloak/authorization/model/ResourceServer.java
new file mode 100644
index 0000000..2424c8d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/model/ResourceServer.java
@@ -0,0 +1,91 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.model;
+
+/**
+ * Represents a resource server, whose resources are managed and protected. A resource server is basically an existing
+ * client application in Keycloak that will also act as a resource server.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ResourceServer {
+
+    /**
+     * Returns the unique identifier for this instance.
+     *
+     * @return the unique identifier for this instance
+     */
+    String getId();
+
+    /**
+     * Returns the identifier of the client application (which already exists in Keycloak) that is also acting as a resource
+     * server.
+     *
+     * @return the identifier of the client application associated with this instance.
+     */
+    String getClientId();
+
+    /**
+     * Indicates if the resource server is allowed to manage its own resources remotely using the Protection API.
+     *
+     * {@code true} if the resource server is allowed to managed them remotely
+     */
+    boolean isAllowRemoteResourceManagement();
+
+    /**
+     * Indicates if the resource server is allowed to manage its own resources remotely using the Protection API.
+     *
+     * @param allowRemoteResourceManagement {@code true} if the resource server is allowed to managed them remotely
+     */
+    void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement);
+
+    /**
+     * Returns the {@code PolicyEnforcementMode} configured for this instance.
+     *
+     * @return the {@code PolicyEnforcementMode} configured for this instance.
+     */
+    PolicyEnforcementMode getPolicyEnforcementMode();
+
+    /**
+     * Defines a {@code PolicyEnforcementMode} for this instance.
+     *
+     * @param enforcementMode one of the available options in {@code PolicyEnforcementMode}
+     */
+    void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode);
+
+    /**
+     * The policy enforcement mode dictates how authorization requests are handled by the server.
+     */
+    enum PolicyEnforcementMode {
+        /**
+         * Requests are denied by default even when there is no policy associated with a given resource.
+         */
+        ENFORCING,
+
+        /**
+         * Requests are allowed even when there is no policy associated with a given resource.
+         */
+        PERMISSIVE,
+
+        /**
+         * Completely disables the evaluation of policies and allow access to any resource.
+         */
+        DISABLED
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Scope.java b/server-spi/src/main/java/org/keycloak/authorization/model/Scope.java
new file mode 100644
index 0000000..e13a789
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/model/Scope.java
@@ -0,0 +1,70 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.model;
+
+/**
+ * Represents a scope, which is usually associated with one or more resources in order to define the actions that can be performed
+ * or a specific access context.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Scope {
+
+    /**
+     * Returns the unique identifier for this instance.
+     *
+     * @return the unique identifier for this instance
+     */
+    String getId();
+
+    /**
+     * Returns the name of this scope.
+     *
+     * @return the name of this scope
+     */
+    String getName();
+
+    /**
+     * Sets a name for this scope. The name must be unique.
+     *
+     * @param name the name of this scope
+     */
+    void setName(String name);
+
+    /**
+     * Returns an icon {@link java.net.URI} for this scope.
+     *
+     * @return a uri for an icon
+     */
+    String getIconUri();
+
+    /**
+     * Sets an icon {@link java.net.URI} for this scope.
+     *
+     * @return a uri for an icon
+     */
+    void setIconUri(String iconUri);
+
+    /**
+     * Returns the {@link ResourceServer} instance to where this scope belongs to.
+     *
+     * @return
+     */
+    ResourceServer getResourceServer();
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/package-info.java
new file mode 100644
index 0000000..6ff51af
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Fine-grained Authorization SPI.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
new file mode 100644
index 0000000..e26ad1c
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.evaluator;
+
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A factory for the different {@link PermissionEvaluator} implementations.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public final class Evaluators {
+
+    private final List<PolicyProviderFactory> policyProviderFactories;
+    private final DefaultPolicyEvaluator policyEvaluator;
+    private final Executor scheduler;
+
+    public Evaluators(List<PolicyProviderFactory> policyProviderFactories, DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
+        this.policyProviderFactories = policyProviderFactories;
+        this.policyEvaluator = policyEvaluator;
+        this.scheduler = scheduler;
+    }
+
+    public PermissionEvaluator from(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
+        return schedule(permissions, evaluationContext);
+    }
+
+    public PermissionEvaluator schedule(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
+        return new ScheduledPermissionEvaluator(new IterablePermissionEvaluator(permissions.iterator(), evaluationContext, this.policyEvaluator), this.scheduler);
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java
new file mode 100644
index 0000000..dfda6a7
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.evaluator;
+
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.policy.evaluation.PolicyEvaluator;
+
+import java.util.Iterator;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class IterablePermissionEvaluator implements PermissionEvaluator {
+
+    private final Iterator<ResourcePermission> permissions;
+    private final EvaluationContext executionContext;
+    private final PolicyEvaluator policyEvaluator;
+
+    IterablePermissionEvaluator(Iterator<ResourcePermission> permissions, EvaluationContext executionContext, PolicyEvaluator policyEvaluator) {
+        this.permissions = permissions;
+        this.executionContext = executionContext;
+        this.policyEvaluator = policyEvaluator;
+    }
+
+    @Override
+    public void evaluate(Decision decision) {
+        try {
+            while (this.permissions.hasNext()) {
+                this.policyEvaluator.evaluate(this.permissions.next(), this.executionContext, decision);
+            }
+            decision.onComplete();
+        } catch (Throwable cause) {
+            decision.onError(cause);
+        }
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java
new file mode 100644
index 0000000..c129caf
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.evaluator;
+
+import org.keycloak.authorization.Decision;
+
+/**
+ * An {@link PermissionEvaluator} represents a source of {@link org.keycloak.authorization.permission.ResourcePermission}, responsible for emitting these permissions
+ * to a consumer in order to evaluate the authorization policies based on a {@link org.keycloak.authorization.policy.evaluation.EvaluationContext}.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PermissionEvaluator {
+
+    void evaluate(Decision decision);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java
new file mode 100644
index 0000000..13e08e4
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.evaluator;
+
+import org.keycloak.authorization.Decision;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ * @see PermissionEvaluator
+ */
+class ScheduledPermissionEvaluator implements PermissionEvaluator {
+
+    private final PermissionEvaluator publisher;
+    private final Executor scheduler;
+
+    ScheduledPermissionEvaluator(PermissionEvaluator publisher, Executor scheduler) {
+        this.publisher = publisher;
+        this.scheduler = scheduler;
+    }
+
+    @Override
+    public void evaluate(Decision decision) {
+        CompletableFuture.runAsync(() -> publisher.evaluate(decision), scheduler);
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java b/server-spi/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java
new file mode 100644
index 0000000..1eef22a
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission;
+
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a permission for a given resource.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermission {
+
+    private final Resource resource;
+    private final List<Scope> scopes;
+    private ResourceServer resourceServer;
+
+    public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer) {
+        this.resource = resource;
+        this.scopes = scopes;
+        this.resourceServer = resourceServer;
+    }
+
+    /**
+     * Returns the resource to which this permission applies.
+     *
+     * @return the resource to which this permission applies
+     */
+    public Resource getResource() {
+        return this.resource;
+    }
+
+    /**
+     * Returns a list of permitted scopes associated with the resource
+     *
+     * @return a lit of permitted scopes
+     */
+    public List<Scope> getScopes() {
+        return Collections.unmodifiableList(this.scopes);
+    }
+
+    /**
+     * Returns the resource server associated with this permission.
+     *
+     * @return the resource server
+     */
+    public ResourceServer getResourceServer() {
+        return this.resourceServer;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
new file mode 100644
index 0000000..f06eb3f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
@@ -0,0 +1,102 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.permission.ResourcePermission;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class DecisionResultCollector implements Decision<DefaultEvaluation> {
+
+    private Map<ResourcePermission, Result> results = new HashMap();
+
+    @Override
+    public void onDecision(DefaultEvaluation evaluation) {
+        if (evaluation.getParentPolicy() != null) {
+            results.computeIfAbsent(evaluation.getPermission(), Result::new).policy(evaluation.getParentPolicy()).policy(evaluation.getPolicy()).setStatus(evaluation.getEffect());
+        } else {
+            results.computeIfAbsent(evaluation.getPermission(), Result::new).setStatus(evaluation.getEffect());
+        }
+    }
+
+    @Override
+    public void onComplete() {
+        for (Result result : results.values()) {
+            for (Result.PolicyResult policyResult : result.getResults()) {
+                if (isGranted(policyResult)) {
+                    policyResult.setStatus(Effect.PERMIT);
+                } else {
+                    policyResult.setStatus(Effect.DENY);
+                }
+            }
+
+            if (result.getResults().stream()
+                    .filter(policyResult -> Effect.DENY.equals(policyResult.getStatus())).count() > 0) {
+                result.setStatus(Effect.DENY);
+            } else {
+                result.setStatus(Effect.PERMIT);
+            }
+        }
+
+        onComplete(results.values().stream().collect(Collectors.toList()));
+    }
+
+    protected abstract void onComplete(List<Result> results);
+
+    private boolean isGranted(Result.PolicyResult policyResult) {
+        List<Result.PolicyResult> values = policyResult.getAssociatedPolicies();
+
+        int grantCount = 0;
+        int denyCount = policyResult.getPolicy().getAssociatedPolicies().size();
+
+        for (Result.PolicyResult decision : values) {
+            if (decision.getStatus().equals(Effect.PERMIT)) {
+                grantCount++;
+                denyCount--;
+            }
+        }
+
+        Policy policy = policyResult.getPolicy();
+        Policy.DecisionStrategy decisionStrategy = policy.getDecisionStrategy();
+
+        if (decisionStrategy == null) {
+            decisionStrategy = Policy.DecisionStrategy.UNANIMOUS;
+        }
+
+        if (Policy.DecisionStrategy.AFFIRMATIVE.equals(decisionStrategy) && grantCount > 0) {
+            return true;
+        } else if (Policy.DecisionStrategy.UNANIMOUS.equals(decisionStrategy) && denyCount == 0) {
+            return true;
+        } else if (Policy.DecisionStrategy.CONSENSUS.equals(decisionStrategy)) {
+            if (grantCount > denyCount) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java
new file mode 100644
index 0000000..df379af
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java
@@ -0,0 +1,105 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.Decision.Effect;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Policy.Logic;
+import org.keycloak.authorization.permission.ResourcePermission;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DefaultEvaluation implements Evaluation {
+
+    private final ResourcePermission permission;
+    private final EvaluationContext executionContext;
+    private final Decision decision;
+    private final Policy policy;
+    private final Policy parentPolicy;
+    private Effect effect;
+
+    public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision) {
+        this.permission = permission;
+        this.executionContext = executionContext;
+        this.parentPolicy = parentPolicy;
+        this.policy = policy;
+        this.decision = decision;
+    }
+
+    /**
+     * Returns the {@link ResourcePermission} to be evaluated.
+     *
+     * @return the permission to be evaluated
+     */
+    public ResourcePermission getPermission() {
+        return this.permission;
+    }
+
+    /**
+     * Returns the {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator}. Which provides access to the whole evaluation runtime context.
+     *
+     * @return the evaluation context
+     */
+    public EvaluationContext getContext() {
+        return this.executionContext;
+    }
+
+    /**
+     * Grants all the requested permissions to the caller.
+     */
+    public void grant() {
+        if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
+            this.effect = Effect.DENY;
+        } else {
+            this.effect = Effect.PERMIT;
+        }
+
+        this.decision.onDecision(this);
+    }
+
+    public void deny() {
+        if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
+            this.effect = Effect.PERMIT;
+        } else {
+            this.effect = Effect.DENY;
+        }
+
+        this.decision.onDecision(this);
+    }
+
+    public Policy getPolicy() {
+        return this.policy;
+    }
+
+    public Policy getParentPolicy() {
+        return this.parentPolicy;
+    }
+
+    public Effect getEffect() {
+        return effect;
+    }
+
+    void denyIfNoEffect() {
+        if (this.effect == null) {
+            deny();
+        }
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
new file mode 100644
index 0000000..8b12558
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -0,0 +1,156 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.ResourceServer.PolicyEnforcementMode;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.StoreFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DefaultPolicyEvaluator implements PolicyEvaluator {
+
+    private final AuthorizationProvider authorization;
+    private Map<String, PolicyProviderFactory> policyProviders = new HashMap<>();
+
+    public DefaultPolicyEvaluator(AuthorizationProvider authorization, List<PolicyProviderFactory> policyProviderFactories) {
+        this.authorization = authorization;
+
+        for (PolicyProviderFactory providerFactory : policyProviderFactories) {
+            this.policyProviders.put(providerFactory.getId(), providerFactory);
+        }
+    }
+
+    @Override
+    public void evaluate(ResourcePermission permission, EvaluationContext executionContext, Decision decision) {
+        ResourceServer resourceServer = permission.getResourceServer();
+
+        if (PolicyEnforcementMode.DISABLED.equals(resourceServer.getPolicyEnforcementMode())) {
+            createEvaluation(permission, executionContext, decision, null, null).grant();
+            return;
+        }
+
+        StoreFactory storeFactory = this.authorization.getStoreFactory();
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+        AtomicInteger policiesCount = new AtomicInteger(0);
+        Consumer<Policy> consumer = createDecisionConsumer(permission, executionContext, decision, policiesCount);
+        Resource resource = permission.getResource();
+
+        if (resource != null) {
+            List<? extends Policy> resourcePolicies = policyStore.findByResource(resource.getId());
+
+            if (!resourcePolicies.isEmpty()) {
+                resourcePolicies.forEach(consumer);
+            }
+
+            if (resource.getType() != null) {
+                policyStore.findByResourceType(resource.getType(), resourceServer.getId()).forEach(consumer);
+            }
+
+            if (permission.getScopes().isEmpty() && !resource.getScopes().isEmpty()) {
+                policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
+            }
+        }
+
+        if (!permission.getScopes().isEmpty()) {
+            policyStore.findByScopeIds(permission.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
+        }
+
+        if (PolicyEnforcementMode.PERMISSIVE.equals(resourceServer.getPolicyEnforcementMode()) && policiesCount.get() == 0) {
+            createEvaluation(permission, executionContext, decision, null, null).grant();
+        }
+    }
+
+    private  Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicInteger policiesCount) {
+        return (parentPolicy) -> {
+            if (hasRequestedScopes(permission, parentPolicy)) {
+                for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
+                    PolicyProviderFactory providerFactory = policyProviders.get(associatedPolicy.getType());
+
+                    if (providerFactory == null) {
+                        throw new RuntimeException("Could not find a policy provider for policy type [" + associatedPolicy.getType() + "].");
+                    }
+
+                    PolicyProvider policyProvider = providerFactory.create(associatedPolicy, this.authorization);
+
+                    if (policyProvider == null) {
+                        throw new RuntimeException("Unknown parentPolicy provider for type [" + associatedPolicy.getType() + "].");
+                    }
+
+                    DefaultEvaluation evaluation = createEvaluation(permission, executionContext, decision, parentPolicy, associatedPolicy);
+
+                    policyProvider.evaluate(evaluation);
+                    evaluation.denyIfNoEffect();
+
+                    policiesCount.incrementAndGet();
+                }
+            }
+        };
+    }
+
+    private DefaultEvaluation createEvaluation(ResourcePermission permission, EvaluationContext executionContext, Decision decision, Policy parentPolicy, Policy associatedPolicy) {
+        return new DefaultEvaluation(permission, executionContext, parentPolicy, associatedPolicy, decision);
+    }
+
+    private boolean hasRequestedScopes(final ResourcePermission permission, final Policy policy) {
+        if (permission.getScopes().isEmpty()) {
+            return true;
+        }
+
+        if (policy.getScopes().isEmpty()) {
+            return true;
+        }
+
+        boolean hasScope = true;
+
+        for (Scope givenScope : policy.getScopes()) {
+            boolean hasGivenScope = false;
+
+            for (Scope scope : permission.getScopes()) {
+                if (givenScope.getId().equals(scope.getId())) {
+                    hasGivenScope = true;
+                    break;
+                }
+            }
+
+            if (!hasGivenScope) {
+                return false;
+            }
+        }
+
+        return hasScope;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java
new file mode 100644
index 0000000..f5b0868
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.permission.ResourcePermission;
+
+/**
+ * <p>An {@link Evaluation} is mainly used by {@link org.keycloak.authorization.policy.provider.PolicyProvider} in order to evaluate a single
+ * and specific {@link ResourcePermission} against the configured policies.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Evaluation {
+
+    /**
+     * Returns the {@link ResourcePermission} to be evaluated.
+     *
+     * @return the permission to be evaluated
+     */
+    ResourcePermission getPermission();
+
+    /**
+     * Returns the {@link EvaluationContext}. Which provides access to the whole evaluation runtime context.
+     *
+     * @return the evaluation context
+     */
+    EvaluationContext getContext();
+
+    /**
+     * Grants the requested permission to the caller.
+     */
+    void grant();
+
+    /**
+     * Denies the requested permission.
+     */
+    void deny();
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java
new file mode 100644
index 0000000..db5ed04
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java
@@ -0,0 +1,45 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.identity.Identity;
+
+/**
+ * This interface serves as a bridge between the policy evaluation runtime and the environment in which it is running. When evaluating
+ * policies, this interface can be used to query information from the execution environment/context and enrich decisions.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface EvaluationContext {
+
+    /**
+     * Returns the {@link Identity} that represents an entity (person or non-person) to which the permissions must be granted, or not.
+     *
+     * @return the identity to which the permissions must be granted, or not
+     */
+    Identity getIdentity();
+
+    /**
+     * Returns all attributes within the current execution and runtime environment.
+     *
+     * @return the attributes within the current execution and runtime environment
+     */
+    Attributes getAttributes();
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java
new file mode 100644
index 0000000..dcae2ed
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides classes related with the evaluation of policies.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.policy.evaluation;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java
new file mode 100644
index 0000000..c380ba6
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.permission.ResourcePermission;
+
+/**
+ * <p>A {@link PolicyEvaluator} evaluates authorization policies based on a given {@link ResourcePermission}, sending
+ * the results to a {@link Decision} point through the methods defined in that interface.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PolicyEvaluator {
+
+    /**
+     * Starts the evaluation of the configured authorization policies.
+     *
+     * @param decision a {@link Decision} point to where notifications events will be delivered during the evaluation
+     */
+    void evaluate(ResourcePermission permission, EvaluationContext executionContext, Decision decision);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java
new file mode 100644
index 0000000..325af3d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java
@@ -0,0 +1,120 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.evaluation;
+
+import org.keycloak.authorization.Decision.Effect;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.permission.ResourcePermission;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Result {
+
+    private final ResourcePermission permission;
+    private List<PolicyResult> results = new ArrayList<>();
+    private Effect status;
+
+    public Result(ResourcePermission permission) {
+        this.permission = permission;
+    }
+
+    public ResourcePermission getPermission() {
+        return permission;
+    }
+
+    public List<PolicyResult> getResults() {
+        return results;
+    }
+
+    public PolicyResult policy(Policy policy) {
+        for (PolicyResult result : this.results) {
+            if (result.getPolicy().equals(policy)) {
+                return result;
+            }
+        }
+
+        PolicyResult policyResult = new PolicyResult(policy);
+
+        this.results.add(policyResult);
+
+        return policyResult;
+    }
+
+    public void setStatus(final Effect status) {
+        this.status = status;
+    }
+
+    public Effect getEffect() {
+        return status;
+    }
+
+    public static class PolicyResult {
+
+        private final Policy policy;
+        private List<PolicyResult> associatedPolicies = new ArrayList<>();
+        private Effect status;
+
+        public PolicyResult(Policy policy) {
+            this.policy = policy;
+        }
+
+        public PolicyResult status(Effect status) {
+            this.status = status;
+            return this;
+        }
+
+        public PolicyResult policy(Policy policy) {
+            return getPolicy(policy, this.associatedPolicies);
+        }
+
+        private PolicyResult getPolicy(Policy policy, List<PolicyResult> results) {
+            for (PolicyResult result : results) {
+                if (result.getPolicy().equals(policy)) {
+                    return result;
+                }
+            }
+
+            PolicyResult policyResult = new PolicyResult(policy);
+
+            results.add(policyResult);
+
+            return policyResult;
+        }
+
+        public Policy getPolicy() {
+            return policy;
+        }
+
+        public List<PolicyResult> getAssociatedPolicies() {
+            return associatedPolicies;
+        }
+
+        public Effect getStatus() {
+            return status;
+        }
+
+        public void setStatus(final Effect status) {
+            this.status = status;
+        }
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/package-info.java
new file mode 100644
index 0000000..6a66949
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides classes and a SPI to plug different policy providers.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.policy.provider;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java
new file mode 100644
index 0000000..2405c3b
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java
@@ -0,0 +1,29 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.provider;
+
+import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PolicyProvider extends Provider {
+
+    void evaluate(Evaluation evaluation);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java
new file mode 100644
index 0000000..d26208e
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.provider;
+
+import org.keycloak.authorization.model.Policy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PolicyProviderAdminService {
+
+    void onCreate(Policy policy);
+
+    void onUpdate(Policy policy);
+
+    void onRemove(Policy policy);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
new file mode 100644
index 0000000..1beedd9
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.provider;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
+
+    String getName();
+
+    String getGroup();
+
+    PolicyProvider create(Policy policy, AuthorizationProvider authorization);
+
+    PolicyProviderAdminService getAdminResource(ResourceServer resourceServer);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java
new file mode 100644
index 0000000..422981d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.policy.provider;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicySpi implements Spi {
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "policy";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return PolicyProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return PolicyProviderFactory.class;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java b/server-spi/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java
new file mode 100644
index 0000000..dac1b33
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.store.syncronization.ClientApplicationSynchronizer;
+import org.keycloak.authorization.store.syncronization.RealmSynchronizer;
+import org.keycloak.authorization.store.syncronization.Synchronizer;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel.ClientRemovedEvent;
+import org.keycloak.models.RealmModel.RealmRemovedEvent;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface AuthorizationStoreFactory extends ProviderFactory<StoreFactory> {
+
+    @Override
+    default void postInit(KeycloakSessionFactory factory) {
+        registerSynchronizationListeners(factory);
+    }
+
+    default void registerSynchronizationListeners(KeycloakSessionFactory factory) {
+        Map<Class<? extends ProviderEvent>, Synchronizer> synchronizers = new HashMap<>();
+
+        synchronizers.put(ClientRemovedEvent.class, new ClientApplicationSynchronizer());
+        synchronizers.put(RealmRemovedEvent.class, new RealmSynchronizer());
+
+        factory.register(event -> {
+            try {
+                synchronizers.forEach((eventType, synchronizer) -> {
+                    if (eventType.isInstance(event)) {
+                        synchronizer.synchronize(event, factory);
+                    }
+                });
+            } catch (Exception e) {
+                throw new RuntimeException("Error synchronizing authorization data.", e);
+            }
+        });
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/package-info.java b/server-spi/src/main/java/org/keycloak/authorization/store/package-info.java
new file mode 100644
index 0000000..d9800da
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+/**
+ * Provides classes and a SPI to plug different metadata storage implementations.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+package org.keycloak.authorization.store;
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java
new file mode 100644
index 0000000..f55db99
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java
@@ -0,0 +1,119 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+import java.util.List;
+
+/**
+ * A {@link PolicyStore} is responsible to manage the persistence of {@link Policy} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PolicyStore {
+
+    /**
+     * Creates a new {@link Policy} instance. The new instance is not necessarily persisted though, which may require
+     * a call to the {#save} method to actually make it persistent.
+     *
+     * @param name           the name of the policy
+     * @param type           the type of the policy
+     * @param resourceServer the resource server to which this policy belongs
+     * @return a new instance of {@link Policy}
+     */
+    Policy create(String name, String type, ResourceServer resourceServer);
+
+    /**
+     * Deletes a policy from the underlying persistence mechanism.
+     *
+     * @param id the id of the policy to delete
+     */
+    void delete(String id);
+
+    /**
+     * Returns a {@link Policy} with the given <code>id</code>
+     *
+     * @param id the identifier of the policy
+     * @return a policy with the given identifier.
+     */
+    Policy findById(String id);
+
+    /**
+     * Returns a {@link Policy} with the given <code>name</code>
+     *
+     * @param name             the name of the policy
+     * @param resourceServerId the resource server id
+     * @return a policy with the given name.
+     */
+    Policy findByName(String name, String resourceServerId);
+
+    /**
+     * Returns a list of {@link Policy} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
+     *
+     * @param resourceServerId the identifier of a resource server
+     * @return a list of policies that belong to the given resource server
+     */
+    List<Policy> findByResourceServer(String resourceServerId);
+
+    /**
+     * Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
+     *
+     * @param resourceId the identifier of a resource
+     * @return a list of policies associated with the given resource
+     */
+    List<Policy> findByResource(String resourceId);
+
+    /**
+     * Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>type</code>.
+     *
+     * @param resourceType     the type of a resource
+     * @param resourceServerId the resource server id
+     * @return a list of policies associated with the given resource type
+     */
+    List<Policy> findByResourceType(String resourceType, String resourceServerId);
+
+    /**
+     * Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Scope} with the given <code>scopeIds</code>.
+     *
+     * @param scopeIds the id of the scopes
+     * @param resourceServerId the resource server id
+     * @return a list of policies associated with the given scopes
+     */
+    List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId);
+
+    /**
+     * Returns a list of {@link Policy} with the given <code>type</code>.
+     *
+     * @param type the type of the policy
+     * @return a list of policies with the given type
+     */
+    List<Policy> findByType(String type);
+
+    /**
+     * Returns a list of {@link Policy} that depends on another policy with the given <code>id</code>.
+     *
+     * @param id the id of the policy to query its dependents
+     * @return a list of policies that depends on the a policy with the given identifier
+     */
+    List<Policy> findDependentPolicies(String id);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java
new file mode 100644
index 0000000..742f98b
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+import org.keycloak.authorization.model.ResourceServer;
+
+/**
+ * A {@link ResourceServerStore} is responsible to manage the persistence of {@link ResourceServer} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ResourceServerStore {
+
+    /**
+     * <p>Creates a {@link ResourceServer} instance backed by this persistent storage implementation.
+     *
+     * @param clientId the client id acting as a resource server
+     *
+     * @return an instance backed by the underlying storage implementation
+     */
+    ResourceServer create(String clientId);
+
+    /**
+     * Removes a {@link ResourceServer} instance, with the given {@code id} from the persistent storage.
+     *
+     * @param id the identifier of an existing resource server instance
+     */
+    void delete(String id);
+
+    /**
+     * Returns a {@link ResourceServer} instance based on its identifier.
+     *
+     * @param id the identifier of an existing resource server instance
+     *
+     * @return the resource server instance with the given identifier or null if no instance was found
+     */
+    ResourceServer findById(String id);
+
+    /**
+     * Returns a {@link ResourceServer} instance based on the identifier of a client application.
+     *
+     * @param id the identifier of an existing client application
+     * 
+     * @return the resource server instance, with the given client id or null if no instance was found
+     */
+    ResourceServer findByClient(String id);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java
new file mode 100644
index 0000000..5b92808
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A {@link ResourceStore} is responsible to manage the persistence of {@link Resource} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ResourceStore {
+
+    /**
+     * <p>Creates a {@link Resource} instance backed by this persistent storage implementation.
+     *
+     * @param name the name of this resource. It must be unique.
+     * @param resourceServer the resource server to where the given resource belongs to
+     * @param owner the owner of this resource or null if the resource server is the owner
+     * @return an instance backed by the underlying storage implementation
+     */
+    Resource create(String name, ResourceServer resourceServer, String owner);
+
+    /**
+     * Removes a {@link Resource} instance, with the given {@code id} from the persistent storage.
+     *
+     * @param id the identifier of an existing resource instance
+     */
+    void delete(String id);
+
+    /**
+     * Returns a {@link Resource} instance based on its identifier.
+     *
+     * @param id the identifier of an existing resource instance
+     * @return the resource instance with the given identifier or null if no instance was found
+     */
+    Resource findById(String id);
+
+    /**
+     * Finds all {@link Resource} instances with the given {@code ownerId}.
+     *
+     * @param ownerId the identifier of the owner
+     * @return a list with all resource instances owned by the given owner
+     */
+    List<Resource> findByOwner(String ownerId);
+
+    /**
+     * Finds all {@link Resource} instances associated with a given resource server.
+     *
+     * @param resourceServerId the identifier of the resource server
+     * @return a list with all resources associated with the given resource server
+     */
+    List<Resource> findByResourceServer(String resourceServerId);
+
+    /**
+     * Finds all {@link Resource} associated with a given scope.
+     *
+     * @param id one or more scope identifiers
+     * @return a list of resources associated with the given scope(s)
+     */
+    List<Resource> findByScope(String... id);
+
+    /**
+     * Find a {@link Resource} by its name.
+     *
+     * @param name the name of the resource
+     * @param resourceServerId the identifier of the resource server
+     * @return a resource with the given name
+     */
+    Resource findByName(String name, String resourceServerId);
+
+    /**
+     * Finds all {@link Resource} with the given type.
+     *
+     * @param type the type of the resource
+     * @return a list of resources with the given type
+     */
+    List<Resource> findByType(String type);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java b/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java
new file mode 100644
index 0000000..501217f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+
+import java.util.List;
+
+/**
+ * A {@link ScopeStore} is responsible to manage the persistence of {@link Scope} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ScopeStore {
+
+    /**
+     * Creates a new {@link Scope} instance. The new instance is not necessarily persisted though, which may require
+     * a call to the {#save} method to actually make it persistent.
+     *
+     * @param name the name of the scope
+     * @param resourceServer the resource server to which this scope belongs
+     *
+     * @return a new instance of {@link Scope}
+     */
+    Scope create(String name, ResourceServer resourceServer);
+
+    /**
+     * Deletes a scope from the underlying persistence mechanism.
+     *
+     * @param id the id of the scope to delete
+     */
+    void delete(String id);
+
+    /**
+     * Returns a {@link Scope} with the given <code>id</code>
+     *
+     * @param id the identifier of the scope
+     *
+     * @return a scope with the given identifier.
+     */
+    Scope findById(String id);
+
+    /**
+     * Returns a {@link Scope} with the given <code>name</code>
+     *
+     * @param name the name of the scope
+     *
+     * @param resourceServerId
+     * @return a scope with the given name.
+     */
+    Scope findByName(String name, String resourceServerId);
+
+    /**
+     * Returns a list of {@link Scope} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
+     *
+     * @param resourceServerId the identifier of a resource server
+     *
+     * @return a list of scopes that belong to the given resource server
+     */
+    List<Scope> findByResourceServer(String id);
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactory.java b/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactory.java
new file mode 100644
index 0000000..4f50c11
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactory.java
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * <p>A factory for the different types of storages that manage the persistence of the domain model types.
+ *
+ * <p>Implementations of this interface are usually related with the creation of those storage types accordingly with a
+ * specific persistence mechanism such as relational and NoSQL databases, filesystem, etc.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface StoreFactory extends Provider {
+
+    /**
+     * Returns a {@link ResourceStore}.
+     *
+     * @return the resource store
+     */
+    ResourceStore getResourceStore();
+
+    /**
+     * Returns a {@link ResourceServerStore}.
+     *
+     * @return the resource server store
+     */
+    ResourceServerStore getResourceServerStore();
+
+    /**
+     * Returns a {@link ScopeStore}.
+     *
+     * @return the scope store
+     */
+    ScopeStore getScopeStore();
+
+    /**
+     * Returns a {@link PolicyStore}.
+     *
+     * @return the policy store
+     */
+    PolicyStore getPolicyStore();
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java b/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java
new file mode 100644
index 0000000..53bfb25
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class StoreFactorySpi implements Spi {
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "authorizationPersister";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return StoreFactory.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return AuthorizationStoreFactory.class;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java
new file mode 100644
index 0000000..67683ff
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store.syncronization;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.AuthorizationStoreFactory;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel.ClientRemovedEvent;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientApplicationSynchronizer implements Synchronizer<ClientRemovedEvent> {
+
+    @Override
+    public void synchronize(ClientRemovedEvent event, KeycloakSessionFactory factory) {
+        ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
+        AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession());
+        StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+        ResourceServerStore store = storeFactory.getResourceServerStore();
+        ResourceServer resourceServer = store.findByClient(event.getClient().getId());
+
+        if (resourceServer != null) {
+            String id = resourceServer.getId();
+            storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId()));
+            storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId()));
+            storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId()));
+            storeFactory.getResourceServerStore().delete(id);
+        }
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
new file mode 100644
index 0000000..4f0ef32
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store.syncronization;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel.RealmRemovedEvent;
+import org.keycloak.provider.ProviderFactory;
+
+/*
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RealmSynchronizer implements Synchronizer<RealmRemovedEvent> {
+    @Override
+    public void synchronize(RealmRemovedEvent event, KeycloakSessionFactory factory) {
+        ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
+        AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession());
+        StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+
+        event.getRealm().getClients().forEach(clientModel -> {
+            ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getClientId());
+
+            if (resourceServer != null) {
+                String id = resourceServer.getId();
+                storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId()));
+                storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId()));
+                storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId()));
+                storeFactory.getResourceServerStore().delete(id);
+            }
+        });
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java
new file mode 100644
index 0000000..eb07947
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.store.syncronization;
+
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderEvent;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface Synchronizer<E extends ProviderEvent> {
+
+    void synchronize(E event, KeycloakSessionFactory factory);
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/AdminRoles.java b/server-spi/src/main/java/org/keycloak/models/AdminRoles.java
index 34cdb36..24455b8 100755
--- a/server-spi/src/main/java/org/keycloak/models/AdminRoles.java
+++ b/server-spi/src/main/java/org/keycloak/models/AdminRoles.java
@@ -37,13 +37,15 @@ public class AdminRoles {
     public static String VIEW_CLIENTS = "view-clients";
     public static String VIEW_EVENTS = "view-events";
     public static String VIEW_IDENTITY_PROVIDERS = "view-identity-providers";
+    public static String VIEW_AUTHORIZATION = "view-authorization";
 
     public static String MANAGE_REALM = "manage-realm";
     public static String MANAGE_USERS = "manage-users";
     public static String MANAGE_IDENTITY_PROVIDERS = "manage-identity-providers";
     public static String MANAGE_CLIENTS = "manage-clients";
     public static String MANAGE_EVENTS = "manage-events";
+    public static String MANAGE_AUTHORIZATION = "manage-authorization";
 
-    public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
+    public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, VIEW_AUTHORIZATION, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS, MANAGE_AUTHORIZATION};
 
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java
new file mode 100644
index 0000000..3be3b78
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.cache.authorization;
+
+import org.keycloak.authorization.store.StoreFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface CachedStoreFactoryProvider extends StoreFactory {
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java
new file mode 100644
index 0000000..226949d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.cache.authorization;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedStoreFactorySpi implements Spi {
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "authz-fached-store-factory";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return CachedStoreFactoryProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return CachedStoreProviderFactory.class;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java
new file mode 100644
index 0000000..b8563cb
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.models.cache.authorization;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface CachedStoreProviderFactory extends ProviderFactory<CachedStoreFactoryProvider> {
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/Constants.java b/server-spi/src/main/java/org/keycloak/models/Constants.java
index 460d08f..7f998df 100755
--- a/server-spi/src/main/java/org/keycloak/models/Constants.java
+++ b/server-spi/src/main/java/org/keycloak/models/Constants.java
@@ -37,6 +37,10 @@ public interface Constants {
     String[] BROKER_SERVICE_ROLES = {READ_TOKEN_ROLE};
     String OFFLINE_ACCESS_ROLE = OAuth2Constants.OFFLINE_ACCESS;
 
+    String AUTHZ_UMA_PROTECTION = "uma_protection";
+    String AUTHZ_UMA_AUTHORIZATION = "uma_authorization";
+    String[] AUTHZ_DEFAULT_AUTHORIZATION_ROLES = {AUTHZ_UMA_AUTHORIZATION};
+
     String DEFAULT_HASH_ALGORITHM = "pbkdf2";
 
     // 15 minutes
diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java b/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
index d9dc131..d7d6053 100644
--- a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
@@ -39,6 +39,13 @@ public interface DBLockProvider extends Provider {
      */
     void releaseLock();
 
+    /**
+     * Check if I have lock
+     *
+     * @return
+     */
+    boolean hasLock();
+
 
     /**
      * @return true if provider supports forced unlock at startup
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index 38f45bd..936382c 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -38,10 +38,20 @@ public interface RealmModel extends RoleContainerModel {
         RealmModel getCreatedRealm();
     }
 
+    interface RealmRemovedEvent extends ProviderEvent {
+        RealmModel getRealm();
+        KeycloakSession getKeycloakSession();
+    }
+
     interface ClientCreationEvent extends ProviderEvent {
         ClientModel getCreatedClient();
     }
 
+    interface ClientRemovedEvent extends ProviderEvent {
+        ClientModel getClient();
+        KeycloakSession getKeycloakSession();
+    }
+
     interface UserFederationProviderCreationEvent extends ProviderEvent {
         UserFederationProviderModel getCreatedFederationProvider();
         RealmModel getRealm();
diff --git a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
index 24c60b3..00542eb 100755
--- a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models;
 
+import org.keycloak.provider.ProviderEvent;
+
 import java.util.List;
 import java.util.Set;
 
@@ -25,6 +27,12 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public interface RoleContainerModel {
+
+    interface RoleRemovedEvent extends ProviderEvent {
+        RoleModel getRole();
+        KeycloakSession getKeycloakSession();
+    }
+
     String getId();
 
     RoleModel getRole(String name);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserModel.java b/server-spi/src/main/java/org/keycloak/models/UserModel.java
index d74ccaf..28c35ab 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserModel.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models;
 
+import org.keycloak.provider.ProviderEvent;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -32,6 +34,11 @@ public interface UserModel extends RoleMapperModel {
     String EMAIL = "email";
     String LOCALE = "locale";
 
+    interface UserRemovedEvent extends ProviderEvent {
+        UserModel getUser();
+        KeycloakSession getKeycloakSession();
+    }
+
     String getId();
 
     String getUsername();
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index d824584..585558c 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -48,9 +48,9 @@ public interface UserSessionProvider extends Provider {
     void removeUserSessions(RealmModel realm);
     void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
 
-    UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username);
-    UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username);
-    void removeUserLoginFailure(RealmModel realm, String username);
+    UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId);
+    UserLoginFailureModel addUserLoginFailure(RealmModel realm, String userId);
+    void removeUserLoginFailure(RealmModel realm, String userId);
     void removeAllUserLoginFailures(RealmModel realm);
 
     void onRealmRemoved(RealmModel realm);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index dce7009..63b34be 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -17,6 +17,9 @@
 
 package org.keycloak.models.utils;
 
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.hash.Pbkdf2PasswordHashProvider;
 import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.Constants;
@@ -88,6 +91,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
+import static java.lang.Boolean.TRUE;
+
 public class RepresentationToModel {
 
     private static Logger logger = Logger.getLogger(RepresentationToModel.class);
@@ -984,6 +989,21 @@ public class RepresentationToModel {
         if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
         else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
 
+        boolean createResourceServer = TRUE.equals(resourceRep.getAuthorizationServicesEnabled());
+
+        if (createResourceServer) {
+            AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
+            ResourceServerStore resourceServerStore = provider.getStoreFactory().getResourceServerStore();
+
+            client.setServiceAccountsEnabled(true);
+            client.setBearerOnly(false);
+            client.setPublicClient(false);
+
+            ResourceServer resourceServer = resourceServerStore.create(client.getId());
+
+            resourceServer.setAllowRemoteResourceManagement(true);
+            resourceServer.setPolicyEnforcementMode(ResourceServer.PolicyEnforcementMode.ENFORCING);
+        }
 
         return client;
     }
diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java
new file mode 100644
index 0000000..e30f5da
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.Provider;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * Provides introspection for a determined OAuth2 token type.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TokenIntrospectionProvider extends Provider {
+
+    /**
+     * Introspect the <code>token</code>.
+     *
+     * @param token the token to introspect.
+     * @return the response with the information about the token
+     */
+    Response introspect(String token);
+}
diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java
new file mode 100644
index 0000000..48b7556
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java
@@ -0,0 +1,28 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * A factory that creates {@link TokenIntrospectionProvider} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TokenIntrospectionProviderFactory extends ProviderFactory<TokenIntrospectionProvider> {
+}
diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java
new file mode 100644
index 0000000..4eb6d39
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * <p>A {@link Spi} to support additional tokens types to the OAuth2 Token Introspection Endpoint.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TokenIntrospectionSpi implements Spi {
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "oauth2-token-introspection";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return TokenIntrospectionProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return TokenIntrospectionProviderFactory.class;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java b/server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java
index 76c2950..2d7a07a 100644
--- a/server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java
+++ b/server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java
@@ -24,6 +24,19 @@ import java.util.List;
  */
 public interface ProviderLoader {
 
+    /**
+     * Load the SPI definitions themselves.
+     *
+     * @return a list of Spi definition objects
+     */
+    List<Spi> loadSpis();
+
+    /**
+     * Load all provider factories of a specific SPI.
+     *
+     * @param spi the Spi definition
+     * @return a list of provider factories
+     */
     List<ProviderFactory> load(Spi spi);
 
 }
diff --git a/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
index 33ba774..e884b02 100755
--- a/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
+++ b/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java
@@ -20,6 +20,7 @@ package org.keycloak.services.managers;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.provider.Provider;
 
 /**
@@ -27,7 +28,7 @@ import org.keycloak.provider.Provider;
  * @version $Revision: 1 $
  */
 public interface BruteForceProtector extends Provider {
-    void failedLogin(RealmModel realm, String username, ClientConnection clientConnection);
+    void failedLogin(RealmModel realm, UserModel user, ClientConnection clientConnection);
 
-    boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username);
+    boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, UserModel user);
 }
diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 3ef7094..89c6e2d 100755
--- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -54,4 +54,9 @@ org.keycloak.authentication.RequiredActionSpi
 org.keycloak.authentication.FormAuthenticatorSpi
 org.keycloak.authentication.FormActionSpi
 org.keycloak.cluster.ClusterSpi
+org.keycloak.authorization.policy.provider.PolicySpi
+org.keycloak.authorization.store.StoreFactorySpi
+org.keycloak.authorization.AuthorizationSpi
+org.keycloak.models.cache.authorization.CachedStoreFactorySpi
+org.keycloak.protocol.oidc.TokenIntrospectionSpi
 

services/pom.xml 2(+2 -0)

diff --git a/services/pom.xml b/services/pom.xml
index 41ca54d..16b19e7 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -32,6 +32,8 @@
 
     <properties>
         <version.swagger.doclet>1.0.5</version.swagger.doclet>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
     </properties>
 
     <dependencies>
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 68c0620..cd921bb 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -37,6 +37,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.FormMessage;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.protocol.oidc.TokenManager;
@@ -543,8 +544,10 @@ public class AuthenticationProcessor {
             if (username == null) {
 
             } else {
-                getBruteForceProtector().failedLogin(realm, username, connection);
-
+                UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
+                if (user != null) {
+                    getBruteForceProtector().failedLogin(realm, user, connection);
+                }
             }
         }
     }
@@ -851,7 +854,7 @@ public class AuthenticationProcessor {
         if (authenticatedUser == null) return;
         if (!authenticatedUser.isEnabled()) throw new AuthenticationFlowException(AuthenticationFlowError.USER_DISABLED);
         if (realm.isBruteForceProtected()) {
-            if (getBruteForceProtector().isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
+            if (getBruteForceProtector().isTemporarilyDisabled(session, realm, authenticatedUser)) {
                 throw new AuthenticationFlowException(AuthenticationFlowError.USER_TEMPORARILY_DISABLED);
             }
         }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
index 137e370..5046534 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
@@ -100,7 +100,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
             return false;
         }
         if (context.getRealm().isBruteForceProtected()) {
-            if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
+            if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user)) {
                 context.getEvent().user(user);
                 context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
                 Response challengeResponse = temporarilyDisabledUser(context);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
index 26aeaeb..de48a3c 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
@@ -84,7 +84,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
             return;
         }
         if (context.getRealm().isBruteForceProtected()) {
-            if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
+            if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user)) {
                 context.getEvent().user(user);
                 context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
                 Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account temporarily disabled");
diff --git a/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java
new file mode 100644
index 0000000..a4ff868
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+import javax.ws.rs.Path;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationService {
+
+    private final RealmAuth auth;
+    private final ClientModel client;
+    private final KeycloakSession session;
+    private final ResourceServer resourceServer;
+    private final AuthorizationProvider authorization;
+
+    public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth) {
+        this.session = session;
+        this.client = client;
+        this.authorization = session.getProvider(AuthorizationProvider.class);
+        this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId());
+        this.auth = auth;
+
+        if (auth != null) {
+            this.auth.init(RealmAuth.Resource.AUTHORIZATION);
+        }
+    }
+
+    @Path("/resource-server")
+    public ResourceServerService resourceServer() {
+        ResourceServerService resource = new ResourceServerService(this.authorization, this.resourceServer, this.client, this.auth);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    public void enable() {
+        if (!isEnabled()) {
+            resourceServer().create();
+        }
+    }
+
+    public void disable() {
+        if (isEnabled()) {
+            resourceServer().delete();
+        }
+    }
+
+    public boolean isEnabled() {
+        return this.resourceServer != null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
new file mode 100644
index 0000000..2847d6d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -0,0 +1,205 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.PolicyEvaluationRequest;
+import org.keycloak.authorization.admin.representation.PolicyEvaluationResponse;
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.common.KeycloakEvaluationContext;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.authorization.util.Permissions;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.Urls;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.Arrays.asList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEvaluationService {
+
+    private final AuthorizationProvider authorization;
+    @Context
+    private HttpRequest httpRequest;
+
+    private final ResourceServer resourceServer;
+
+    PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization) {
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
+        EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest);
+        authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, asyncResponse));
+    }
+
+    private DecisionResultCollector createDecisionCollector(PolicyEvaluationRequest evaluationRequest, AuthorizationProvider authorization, AsyncResponse asyncResponse) {
+        return new DecisionResultCollector() {
+            @Override
+            protected void onComplete(List<Result> results) {
+                try {
+                    asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(evaluationRequest,  results, resourceServer,  authorization)).build());
+                } catch (Throwable cause) {
+                    asyncResponse.resume(cause);
+                }
+            }
+
+            @Override
+            public void onError(Throwable cause) {
+                asyncResponse.resume(cause);
+            }
+        };
+    }
+
+    private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation) {
+        return new KeycloakEvaluationContext(createIdentity(representation), this.authorization.getKeycloakSession()) {
+            @Override
+            public Attributes getAttributes() {
+                Map<String, Collection<String>> attributes = new HashMap<>(super.getAttributes().toMap());
+                Map<String, String> givenAttributes = representation.getContext().get("attributes");
+
+                if (givenAttributes != null) {
+                    givenAttributes.forEach((key, entryValue) -> {
+                        if (entryValue != null) {
+                            List<String> values = new ArrayList();
+
+                            for (String value : entryValue.split(",")) {
+                                values.add(value);
+                            }
+
+                            attributes.put(key, values);
+                        }
+                    });
+                }
+
+                return Attributes.from(attributes);
+            }
+        };
+    }
+
+    private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
+        if (representation.isEntitlements()) {
+            return Permissions.all(this.resourceServer, evaluationContext.getIdentity(), authorization);
+        }
+
+        return representation.getResources().stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
+            Set<String> givenScopes = resource.getScopes();
+
+            if (givenScopes == null) {
+                givenScopes = new HashSet();
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+
+            List<Scope> scopes = givenScopes.stream().map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
+
+            if (resource.getId() != null) {
+                Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId());
+                return Stream.of(new ResourcePermission(resourceModel, scopes, resourceServer));
+            } else if (resource.getType() != null) {
+                return storeFactory.getResourceStore().findByType(resource.getType()).stream().map(resource1 -> new ResourcePermission(resource1, scopes, resourceServer));
+            } else {
+                return scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer));
+            }
+        }).collect(Collectors.toList());
+    }
+
+    private KeycloakIdentity createIdentity(PolicyEvaluationRequest representation) {
+        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
+        AccessToken accessToken = new AccessToken();
+
+        accessToken.subject(representation.getUserId());
+        accessToken.issuedFor(representation.getClientId());
+        accessToken.audience(representation.getClientId());
+        accessToken.issuer(Urls.realmIssuer(this.authorization.getKeycloakSession().getContext().getUri().getBaseUri(), realm.getName()));
+        accessToken.setRealmAccess(new AccessToken.Access());
+
+        Map<String, Object> claims = accessToken.getOtherClaims();
+        Map<String, String> givenAttributes = representation.getContext().get("attributes");
+
+        if (givenAttributes != null) {
+            givenAttributes.forEach((key, value) -> claims.put(key, asList(value)));
+        }
+
+        String subject = accessToken.getSubject();
+
+        if (subject != null) {
+            UserModel userModel = this.authorization.getKeycloakSession().users().getUserById(subject, realm);
+
+            if (userModel != null) {
+                Set<RoleModel> roleMappings = userModel.getRoleMappings();
+
+                roleMappings.stream().map(RoleModel::getName).forEach(roleName -> accessToken.getRealmAccess().addRole(roleName));
+
+                String clientId = representation.getClientId();
+
+                if (clientId != null) {
+                    ClientModel clientModel = realm.getClientById(clientId);
+
+                    accessToken.addAccess(clientModel.getClientId());
+
+                    userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> accessToken.getResourceAccess(clientModel.getClientId()).addRole(roleName));
+
+                    //TODO: would be awesome if we could transform the access token using the configured protocol mappers. Tried, but without a clientSession and userSession is tuff.
+                }
+            }
+        }
+
+        if (representation.getRoleIds() != null) {
+            representation.getRoleIds().forEach(roleName -> accessToken.getRealmAccess().addRole(roleName));
+        }
+
+        return new KeycloakIdentity(accessToken, this.authorization.getKeycloakSession());
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
new file mode 100644
index 0000000..c34893f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -0,0 +1,367 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.PolicyProviderRepresentation;
+import org.keycloak.authorization.admin.representation.PolicyRepresentation;
+import org.keycloak.authorization.admin.util.Models;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.stream.Collectors;
+
+import static org.keycloak.authorization.admin.util.Models.toRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyService {
+
+    private final ResourceServer resourceServer;
+    private final AuthorizationProvider authorization;
+    private final RealmAuth auth;
+
+    public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+        this.auth = auth;
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(PolicyRepresentation representation) {
+        this.auth.requireManage();
+        Policy policy = Models.toModel(representation, this.resourceServer, authorization);
+
+        updateResources(policy, authorization);
+        updateAssociatedPolicies(policy);
+        updateScopes(policy, authorization);
+
+        PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
+
+        if (resource != null) {
+            try {
+                resource.onCreate(policy);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        representation.setId(policy.getId());
+
+        return Response.status(Status.CREATED).entity(representation).build();
+    }
+
+    @Path("{id}")
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response update(@PathParam("id") String id, PolicyRepresentation representation) {
+        this.auth.requireManage();
+        representation.setId(id);
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Policy policy = storeFactory.getPolicyStore().findById(representation.getId());
+
+        if (policy == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        policy.setName(representation.getName());
+        policy.setDescription(representation.getDescription());
+        policy.setConfig(representation.getConfig());
+        policy.setDecisionStrategy(representation.getDecisionStrategy());
+        policy.setLogic(representation.getLogic());
+
+        updateResources(policy, authorization);
+        updateAssociatedPolicies(policy);
+        updateScopes(policy, authorization);
+
+        PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
+
+        if (resource != null) {
+            try {
+                resource.onUpdate(policy);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        return Response.status(Status.CREATED).build();
+    }
+
+    @Path("{id}")
+    @DELETE
+    public Response delete(@PathParam("id") String id) {
+        this.auth.requireManage();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+        Policy policy = policyStore.findById(id);
+
+        if (policy == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
+
+        if (resource != null) {
+            try {
+                resource.onRemove(policy);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        policyStore.findDependentPolicies(id).forEach(dependentPolicy -> {
+            dependentPolicy.removeAssociatedPolicy(policy);
+        });
+
+        policyStore.delete(policy.getId());
+
+        return Response.noContent().build();
+    }
+
+    @Path("{id}")
+    @GET
+    @Produces("application/json")
+    public Response findById(@PathParam("id") String id) {
+        this.auth.requireView();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Policy model = storeFactory.getPolicyStore().findById(id);
+
+        if (model == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        return Response.ok(toRepresentation(model, authorization)).build();
+    }
+
+    @GET
+    @Produces("application/json")
+    public Response findAll() {
+        this.auth.requireView();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        return Response.ok(
+                storeFactory.getPolicyStore().findByResourceServer(resourceServer.getId()).stream()
+                        .map(policy -> toRepresentation(policy, authorization))
+                        .collect(Collectors.toList()))
+                .build();
+    }
+
+    @Path("providers")
+    @GET
+    @Produces("application/json")
+    public Response findPolicyProviders() {
+        this.auth.requireView();
+        return Response.ok(
+                authorization.getProviderFactories().stream()
+                        .map(provider -> {
+                            PolicyProviderRepresentation representation = new PolicyProviderRepresentation();
+
+                            representation.setName(provider.getName());
+                            representation.setGroup(provider.getGroup());
+                            representation.setType(provider.getId());
+
+                            return representation;
+                        })
+                        .collect(Collectors.toList()))
+                .build();
+    }
+
+    @Path("evaluate")
+    public PolicyEvaluationService getPolicyEvaluateResource() {
+        this.auth.requireView();
+        PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    @Path("{policyType}")
+    public Object getPolicyTypeResource(@PathParam("policyType") String policyType) {
+        this.auth.requireView();
+        return getPolicyProviderAdminResource(policyType, this.authorization);
+    }
+
+    private PolicyProviderAdminService getPolicyProviderAdminResource(String policyType, AuthorizationProvider authorization) {
+        PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType);
+
+        if (providerFactory != null) {
+            return providerFactory.getAdminResource(this.resourceServer);
+        }
+
+        return null;
+    }
+
+    private void updateScopes(Policy policy, AuthorizationProvider authorization) {
+        String scopes = policy.getConfig().get("scopes");
+        if (scopes != null) {
+            String[] scopeIds;
+
+            try {
+                scopeIds = new ObjectMapper().readValue(scopes, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+
+            for (String scopeId : scopeIds) {
+                boolean hasScope = false;
+
+                for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
+                    if (scopeModel.getId().equals(scopeId)) {
+                        hasScope = true;
+                    }
+                }
+                if (!hasScope) {
+                    policy.addScope(storeFactory.getScopeStore().findById(scopeId));
+                }
+            }
+
+            for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
+                boolean hasScope = false;
+
+                for (String scopeId : scopeIds) {
+                    if (scopeModel.getId().equals(scopeId)) {
+                        hasScope = true;
+                    }
+                }
+                if (!hasScope) {
+                    policy.removeScope(scopeModel);
+                }
+            }
+        }
+    }
+
+    private void updateAssociatedPolicies(Policy policy) {
+        String policies = policy.getConfig().get("applyPolicies");
+
+        if (policies != null) {
+            String[] policyIds;
+
+            try {
+                policyIds = new ObjectMapper().readValue(policies, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+
+            for (String policyId : policyIds) {
+                boolean hasPolicy = false;
+
+                for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
+                    if (policyModel.getId().equals(policyId)) {
+                        hasPolicy = true;
+                    }
+                }
+
+
+                if (!hasPolicy) {
+                    Policy associatedPolicy = policyStore.findById(policyId);
+
+                    if (associatedPolicy == null) {
+                        associatedPolicy = policyStore.findByName(policyId, this.resourceServer.getId());
+                    }
+
+                    policy.addAssociatedPolicy(associatedPolicy);
+                }
+            }
+
+            for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
+                boolean hasPolicy = false;
+
+                for (String policyId : policyIds) {
+                    if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) {
+                        hasPolicy = true;
+                    }
+                }
+                if (!hasPolicy) {
+                    policy.removeAssociatedPolicy(policyModel);;
+                }
+            }
+        }
+    }
+
+    private void updateResources(Policy policy, AuthorizationProvider authorization) {
+        String resources = policy.getConfig().get("resources");
+        if (resources != null) {
+            String[] resourceIds;
+
+            try {
+                resourceIds = new ObjectMapper().readValue(resources, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+
+            for (String resourceId : resourceIds) {
+                boolean hasResource = false;
+                for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
+                    if (resourceModel.getId().equals(resourceId)) {
+                        hasResource = true;
+                    }
+                }
+                if (!hasResource && !"".equals(resourceId)) {
+                    policy.addResource(storeFactory.getResourceStore().findById(resourceId));
+                }
+            }
+
+            for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
+                boolean hasResource = false;
+
+                for (String resourceId : resourceIds) {
+                    if (resourceModel.getId().equals(resourceId)) {
+                        hasResource = true;
+                    }
+                }
+
+                if (!hasResource) {
+                    policy.removeResource(resourceModel);
+                }
+            }
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java
new file mode 100644
index 0000000..17edef9
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationRequest.java
@@ -0,0 +1,123 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEvaluationRequest {
+
+    private Map<String, Map<String, String>> context;
+    private List<Resource> resources;
+    private String clientId;
+    private String userId;
+    private List<String> roleIds;
+    private boolean entitlements;
+
+    public Map<String, Map<String, String>> getContext() {
+        return this.context;
+    }
+
+    public void setContext(Map<String, Map<String, String>> context) {
+        this.context = context;
+    }
+
+    public List<Resource> getResources() {
+        return this.resources;
+    }
+
+    public void setResources(List<Resource> resources) {
+        this.resources = resources;
+    }
+
+    public String getClientId() {
+        return this.clientId;
+    }
+
+    public void setClientId(final String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getUserId() {
+        return this.userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public List<String> getRoleIds() {
+        return this.roleIds;
+    }
+
+    public void setRoleIds(List<String> roleIds) {
+        this.roleIds = roleIds;
+    }
+
+    public boolean isEntitlements() {
+        return entitlements;
+    }
+
+    public void setEntitlements(boolean entitlements) {
+        this.entitlements = entitlements;
+    }
+
+    public static class Resource {
+        private String id;
+        private String name;
+        private String type;
+        private Set<String> scopes;
+
+        public String getId() {
+            return this.id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getName() {
+            return this.name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void setType(final String type) {
+            this.type = type;
+        }
+
+        public Set<String> getScopes() {
+            return scopes;
+        }
+
+        public void setScopes(final Set<String> scopes) {
+            this.scopes = scopes;
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
new file mode 100644
index 0000000..57b3e4e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
@@ -0,0 +1,223 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.Decision.Effect;
+import org.keycloak.authorization.admin.util.Models;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.authorization.util.Permissions;
+import org.keycloak.representations.authorization.Permission;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEvaluationResponse {
+
+    private List<EvaluationResultRepresentation> results;
+    private boolean entitlements;
+    private Effect status;
+
+    private PolicyEvaluationResponse() {
+
+    }
+
+    public static PolicyEvaluationResponse build(PolicyEvaluationRequest evaluationRequest, List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        PolicyEvaluationResponse response = new PolicyEvaluationResponse();
+        List<EvaluationResultRepresentation> resultsRep = new ArrayList<>();
+
+        response.entitlements = evaluationRequest.isEntitlements();
+
+        if (response.entitlements) {
+            List<Permission> entitlements = Permissions.allPermits(results);
+
+            if (entitlements.isEmpty()) {
+                response.status = Effect.DENY;
+            } else {
+                StoreFactory storeFactory = authorization.getStoreFactory();
+
+                for (Permission permission : entitlements) {
+                    EvaluationResultRepresentation rep = new EvaluationResultRepresentation();
+
+                    rep.setStatus(Effect.PERMIT);
+                    resultsRep.add(rep);
+
+                    Resource resource = storeFactory.getResourceStore().findById(permission.getResourceSetId());
+
+                    if (resource != null) {
+                        rep.setResource(Models.toRepresentation(resource, resourceServer, authorization));
+                    } else {
+                        ResourceRepresentation representation = new ResourceRepresentation();
+
+                        representation.setName("Any Resource with Scopes " + permission.getScopes());
+
+                        rep.setResource(representation);
+                    }
+
+                    rep.setScopes(permission.getScopes().stream().map(ScopeRepresentation::new).collect(Collectors.toList()));
+                }
+            }
+        } else {
+            if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Effect.DENY))) {
+                response.status = Effect.DENY;
+            } else {
+                response.status = Effect.PERMIT;
+            }
+
+            for (Result result : results) {
+                EvaluationResultRepresentation rep = new EvaluationResultRepresentation();
+
+                rep.setStatus(result.getEffect());
+                resultsRep.add(rep);
+
+                if (result.getPermission().getResource() != null) {
+                    rep.setResource(Models.toRepresentation(result.getPermission().getResource(), resourceServer, authorization));
+                } else {
+                    ResourceRepresentation resource = new ResourceRepresentation();
+
+                    resource.setName("Any Resource with Scopes " + result.getPermission().getScopes());
+
+                    rep.setResource(resource);
+                }
+
+                rep.setScopes(result.getPermission().getScopes().stream().map(new Function<Scope, ScopeRepresentation>() {
+                    @Override
+                    public ScopeRepresentation apply(Scope scope) {
+                        return Models.toRepresentation(scope, authorization);
+                    }
+                }).collect(Collectors.toList()));
+
+                List<PolicyResultRepresentation> policies = new ArrayList<>();
+
+                for (PolicyResult policy : result.getResults()) {
+                    policies.add(toRepresentation(policy, authorization));
+                }
+
+                rep.setPolicies(policies);
+            }
+        }
+
+        response.results = resultsRep;
+
+        return response;
+    }
+
+    private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) {
+        PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation();
+
+        policyResultRep.setPolicy(Models.toRepresentation(policy.getPolicy(), authorization));
+        policyResultRep.setStatus(policy.getStatus());
+        policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
+
+        return policyResultRep;
+    }
+
+    public List<EvaluationResultRepresentation> getResults() {
+        return results;
+    }
+
+    public Effect getStatus() {
+        return status;
+    }
+
+    public boolean isEntitlements() {
+        return entitlements;
+    }
+
+    public static class EvaluationResultRepresentation {
+
+        private ResourceRepresentation resource;
+        private List<ScopeRepresentation> scopes;
+        private List<PolicyResultRepresentation> policies;
+        private Effect status;
+
+        public void setResource(final ResourceRepresentation resource) {
+            this.resource = resource;
+        }
+
+        public ResourceRepresentation getResource() {
+            return resource;
+        }
+
+        public void setScopes(List<ScopeRepresentation> scopes) {
+            this.scopes = scopes;
+        }
+
+        public List<ScopeRepresentation> getScopes() {
+            return scopes;
+        }
+
+        public void setPolicies(final List<PolicyResultRepresentation> policies) {
+            this.policies = policies;
+        }
+
+        public List<PolicyResultRepresentation> getPolicies() {
+            return policies;
+        }
+
+        public void setStatus(final Effect status) {
+            this.status = status;
+        }
+
+        public Effect getStatus() {
+            return status;
+        }
+    }
+
+    public static class PolicyResultRepresentation {
+
+        private PolicyRepresentation policy;
+        private Effect status;
+        private List<PolicyResultRepresentation> associatedPolicies;
+
+        public PolicyRepresentation getPolicy() {
+            return policy;
+        }
+
+        public void setPolicy(final PolicyRepresentation policy) {
+            this.policy = policy;
+        }
+
+        public Effect getStatus() {
+            return status;
+        }
+
+        public void setStatus(final Effect status) {
+            this.status = status;
+        }
+
+        public List<PolicyResultRepresentation> getAssociatedPolicies() {
+            return associatedPolicies;
+        }
+
+        public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) {
+            this.associatedPolicies = associatedPolicies;
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyProviderRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyProviderRepresentation.java
new file mode 100644
index 0000000..add09b0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyProviderRepresentation.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyProviderRepresentation {
+
+    private String type;
+    private String name;
+    private String group;
+
+    public String getType() {
+        return this.type;
+    }
+
+    public void setType( String type) {
+        this.type = type;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName( String name) {
+        this.name = name;
+    }
+
+    public String getGroup() {
+        return this.group;
+    }
+
+    public void setGroup( String group) {
+        this.group = group;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyRepresentation.java
new file mode 100644
index 0000000..a3c302b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyRepresentation.java
@@ -0,0 +1,119 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import org.keycloak.authorization.model.Policy.DecisionStrategy;
+import org.keycloak.authorization.model.Policy.Logic;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyRepresentation {
+
+    private String id;
+    private String name;
+    private String description;
+    private String type;
+    private Logic logic = Logic.POSITIVE;
+    private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
+    private Map<String, String> config = new HashMap();
+    private List<PolicyRepresentation> dependentPolicies;
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public DecisionStrategy getDecisionStrategy() {
+        return this.decisionStrategy;
+    }
+
+    public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+        this.decisionStrategy = decisionStrategy;
+    }
+
+    public Logic getLogic() {
+        return logic;
+    }
+
+    public void setLogic(Logic logic) {
+        this.logic = logic;
+    }
+
+    public Map<String, String> getConfig() {
+        return this.config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        final PolicyRepresentation policy = (PolicyRepresentation) o;
+        return Objects.equals(getId(), policy.getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId());
+    }
+
+    public void setDependentPolicies(List<PolicyRepresentation> dependentPolicies) {
+        this.dependentPolicies = dependentPolicies;
+    }
+
+    public List<PolicyRepresentation> getDependentPolicies() {
+        return this.dependentPolicies;
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceOwnerRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceOwnerRepresentation.java
new file mode 100644
index 0000000..498ab9f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceOwnerRepresentation.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceOwnerRepresentation {
+
+    private String id;
+    private String name;
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceRepresentation.java
new file mode 100644
index 0000000..b56248b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceRepresentation.java
@@ -0,0 +1,170 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * <p>One or more resources that the resource server manages as a set of protected resources.
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.2">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceRepresentation {
+
+    @JsonProperty("_id")
+    private String id;
+
+    private String name;
+    private String uri;
+    private String type;
+    private Set<ScopeRepresentation> scopes;
+
+    @JsonProperty("icon_uri")
+    private String iconUri;
+    private ResourceOwnerRepresentation owner;
+
+    private List<PolicyRepresentation> policies;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     * @param iconUri a {@link URI} for a graphic icon representing the resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type, String iconUri) {
+        this.name = name;
+        this.scopes = scopes;
+        this.uri = uri;
+        this.type = type;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type) {
+        this(name, scopes, uri, type, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param serverUri a {@link URI} that identifies this resource server
+     * @param scopes the available scopes for this resource set
+     */
+    public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes) {
+        this(name, scopes, null, null, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     */
+    public ResourceRepresentation() {
+        this(null, null, null, null, null);
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getUri() {
+        return this.uri;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public Set<ScopeRepresentation> getScopes() {
+        if (this.scopes == null) {
+            return Collections.emptySet();
+        }
+
+        return Collections.unmodifiableSet(this.scopes);
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setScopes(Set<ScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public ResourceOwnerRepresentation getOwner() {
+        return this.owner;
+    }
+
+    public void setOwner(ResourceOwnerRepresentation owner) {
+        this.owner = owner;
+    }
+
+    public List<PolicyRepresentation> getPolicies() {
+        return this.policies;
+    }
+
+    public void setPolicies(List<PolicyRepresentation> policies) {
+        this.policies = policies;
+    }
+
+    <T> T test(Predicate<T> t) {
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceServerRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceServerRepresentation.java
new file mode 100644
index 0000000..4549ef5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/ResourceServerRepresentation.java
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import org.keycloak.authorization.model.ResourceServer.PolicyEnforcementMode;
+
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceServerRepresentation {
+
+    private String id;
+
+    private String clientId;
+    private String name;
+    private boolean allowRemoteResourceManagement = true;
+    private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
+    private List<ResourceRepresentation> resources = emptyList();
+    private List<PolicyRepresentation> policies = emptyList();
+    private List<ScopeRepresentation> scopes = emptyList();
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isAllowRemoteResourceManagement() {
+        return this.allowRemoteResourceManagement;
+    }
+
+    public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
+        this.allowRemoteResourceManagement = allowRemoteResourceManagement;
+    }
+
+    public PolicyEnforcementMode getPolicyEnforcementMode() {
+        return this.policyEnforcementMode;
+    }
+
+    public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
+        this.policyEnforcementMode = policyEnforcementMode;
+    }
+
+    public void setResources(List<ResourceRepresentation> resources) {
+        this.resources = resources;
+    }
+
+    public List<ResourceRepresentation> getResources() {
+        return resources;
+    }
+
+    public void setPolicies(List<PolicyRepresentation> policies) {
+        this.policies = policies;
+    }
+
+    public List<PolicyRepresentation> getPolicies() {
+        return policies;
+    }
+
+    public void setScopes(List<ScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
+    public List<ScopeRepresentation> getScopes() {
+        return scopes;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/ScopeRepresentation.java b/services/src/main/java/org/keycloak/authorization/admin/representation/ScopeRepresentation.java
new file mode 100644
index 0000000..74efa7a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/ScopeRepresentation.java
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.representation;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * <p>A bounded extent of access that is possible to perform on a resource set. In authorization policy terminology,
+ * a scope is one of the potentially many "verbs" that can logically apply to a resource set ("object").
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.1">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeRepresentation {
+
+    private String id;
+    private String name;
+    private String iconUri;
+    private List<PolicyRepresentation> policies;
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     * @param iconUri a {@link URI} for a graphic icon representing the scope
+     */
+    public ScopeRepresentation(String name, String iconUri) {
+        this.name = name;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     */
+    public ScopeRepresentation(String name) {
+        this(name, null);
+    }
+
+    /**
+     * Creates an instance.
+     */
+    public ScopeRepresentation() {
+        this(null, null);
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public List<PolicyRepresentation> getPolicies() {
+        return this.policies;
+    }
+
+    public void setPolicies(List<PolicyRepresentation> policies) {
+        this.policies = policies;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ScopeRepresentation scope = (ScopeRepresentation) o;
+        return Objects.equals(getName(), scope.getName());
+    }
+
+    public int hashCode() {
+        return Objects.hash(getName());
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
new file mode 100644
index 0000000..84e5295
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
@@ -0,0 +1,630 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.PolicyRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceOwnerRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceServerRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.admin.util.Models;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserFederationManager;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceServerService {
+
+    private final AuthorizationProvider authorization;
+    private final RealmAuth auth;
+    private final RealmModel realm;
+    private final KeycloakSession session;
+    private ResourceServer resourceServer;
+    private final ClientModel client;
+
+    public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth) {
+        this.authorization = authorization;
+        this.session = authorization.getKeycloakSession();
+        this.client = client;
+        this.resourceServer = resourceServer;
+        this.realm = client.getRealm();
+        this.auth = auth;
+    }
+
+    public void create() {
+        this.auth.requireManage();
+        this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId());
+        createDefaultRoles();
+        createDefaultPermission(createDefaultResource(), createDefaultPolicy());
+    }
+
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response update(ResourceServerRepresentation server) {
+        this.auth.requireManage();
+        this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
+        this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
+
+        return Response.noContent().build();
+    }
+
+    public void delete() {
+        this.auth.requireManage();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceStore resourceStore = storeFactory.getResourceStore();
+        String id = resourceServer.getId();
+
+        resourceStore.findByResourceServer(id).forEach(resource -> resourceStore.delete(resource.getId()));
+
+        ScopeStore scopeStore = storeFactory.getScopeStore();
+
+        scopeStore.findByResourceServer(id).forEach(scope -> scopeStore.delete(scope.getId()));
+
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+
+        policyStore.findByResourceServer(id).forEach(scope -> policyStore.delete(scope.getId()));
+
+        storeFactory.getResourceServerStore().delete(id);
+    }
+
+    @GET
+    @Produces("application/json")
+    public Response findById() {
+        this.auth.requireView();
+        return Response.ok(Models.toRepresentation(this.resourceServer, this.realm)).build();
+    }
+
+    @Path("/settings")
+    @GET
+    @Produces("application/json")
+    public Response exportSettings() {
+        this.auth.requireManage();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceServerRepresentation settings = Models.toRepresentation(resourceServer, this.realm);
+
+        settings.setId(null);
+        settings.setName(null);
+        settings.setClientId(null);
+
+        List<ResourceRepresentation> resources = storeFactory.getResourceStore().findByResourceServer(resourceServer.getId())
+                .stream().map(resource -> {
+                    ResourceRepresentation rep = Models.toRepresentation(resource, resourceServer, authorization);
+
+                    rep.getOwner().setId(null);
+                    rep.setId(null);
+                    rep.setPolicies(null);
+                    rep.getScopes().forEach(scopeRepresentation -> {
+                        scopeRepresentation.setId(null);
+                        scopeRepresentation.setIconUri(null);
+                    });
+
+                    return rep;
+                }).collect(Collectors.toList());
+
+        settings.setResources(resources);
+
+        List<PolicyRepresentation> policies = new ArrayList<>();
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+
+        policies.addAll(policyStore.findByResourceServer(resourceServer.getId())
+                .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope"))
+                .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList()));
+        policies.addAll(policyStore.findByResourceServer(resourceServer.getId())
+                .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope"))
+                .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList()));
+
+        settings.setPolicies(policies);
+
+        List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(resourceServer.getId()).stream().map(scope -> {
+            ScopeRepresentation rep = Models.toRepresentation(scope, authorization);
+
+            rep.setId(null);
+
+            rep.getPolicies().forEach(policyRepresentation -> {
+                policyRepresentation.setId(null);
+                policyRepresentation.setConfig(null);
+                policyRepresentation.setType(null);
+                policyRepresentation.setDecisionStrategy(null);
+                policyRepresentation.setDescription(null);
+                policyRepresentation.setDependentPolicies(null);
+            });
+
+            return rep;
+        }).collect(Collectors.toList());
+
+        settings.setScopes(scopes);
+
+        return Response.ok(settings).build();
+    }
+
+    @POST
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public Response importSettings(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+        this.auth.requireManage();
+        Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
+        List<InputPart> inputParts = uploadForm.get("file");
+
+        for (InputPart inputPart : inputParts) {
+            ResourceServerRepresentation rep = JsonSerialization.readValue(inputPart.getBodyAsString(), ResourceServerRepresentation.class);
+
+            resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
+            resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+            ResourceStore resourceStore = storeFactory.getResourceStore();
+            ScopeStore scopeStore = storeFactory.getScopeStore();
+            ScopeService scopeResource = new ScopeService(resourceServer, this.authorization, this.auth);
+
+            ResteasyProviderFactory.getInstance().injectProperties(scopeResource);
+
+            rep.getScopes().forEach(scope -> {
+                Scope existing = scopeStore.findByName(scope.getName(), resourceServer.getId());
+
+                if (existing != null) {
+                    scopeResource.update(existing.getId(), scope);
+                } else {
+                    scopeResource.create(scope);
+                }
+            });
+
+            ResourceSetService resourceSetResource = new ResourceSetService(resourceServer, this.authorization, this.auth);
+
+            rep.getResources().forEach(resourceRepresentation -> {
+                ResourceOwnerRepresentation owner = resourceRepresentation.getOwner();
+
+                if (owner == null) {
+                    owner = new ResourceOwnerRepresentation();
+                }
+
+                owner.setId(resourceServer.getClientId());
+
+                if (owner.getName() != null) {
+                    UserModel user = this.session.users().getUserByUsername(owner.getName(), this.realm);
+
+                    if (user != null) {
+                        owner.setId(user.getId());
+                    }
+                }
+
+                Resource existing = resourceStore.findByName(resourceRepresentation.getName(), this.resourceServer.getId());
+
+                if (existing != null) {
+                    resourceSetResource.update(existing.getId(), resourceRepresentation);
+                } else {
+                    resourceSetResource.create(resourceRepresentation);
+                }
+            });
+
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+            PolicyService policyResource = new PolicyService(resourceServer, this.authorization, this.auth);
+
+            ResteasyProviderFactory.getInstance().injectProperties(policyResource);
+
+            rep.getPolicies().forEach(policyRepresentation -> {
+                Map<String, String> config = policyRepresentation.getConfig();
+
+                String roles = config.get("roles");
+
+                if (roles != null && !roles.isEmpty()) {
+                    roles = roles.replace("[", "");
+                    roles = roles.replace("]", "");
+
+                    if (!roles.isEmpty()) {
+                        String roleNames = "";
+
+                        for (String role : roles.split(",")) {
+                            if (!roleNames.isEmpty()) {
+                                roleNames = roleNames + ",";
+                            }
+
+                            role = role.replace("\"", "");
+
+                            roleNames = roleNames + "\"" + this.realm.getRole(role).getId() + "\"";
+                        }
+
+                        config.put("roles", "[" + roleNames + "]");
+                    }
+                }
+
+                String users = config.get("users");
+
+                if (users != null) {
+                    users = users.replace("[", "");
+                    users = users.replace("]", "");
+
+                    if (!users.isEmpty()) {
+                        String userNames = "";
+
+                        for (String user : users.split(",")) {
+                            if (!userNames.isEmpty()) {
+                                userNames =  userNames + ",";
+                            }
+
+                            user = user.replace("\"", "");
+
+                            userNames = userNames + "\"" + this.session.users().getUserByUsername(user, this.realm).getId() + "\"";
+                        }
+
+                        config.put("users", "[" + userNames + "]");
+                    }
+                }
+
+                String scopes = config.get("scopes");
+
+                if (scopes != null && !scopes.isEmpty()) {
+                    scopes = scopes.replace("[", "");
+                    scopes = scopes.replace("]", "");
+
+                    if (!scopes.isEmpty()) {
+                        String scopeNames = "";
+
+                        for (String scope : scopes.split(",")) {
+                            if (!scopeNames.isEmpty()) {
+                                scopeNames =  scopeNames + ",";
+                            }
+
+                            scope = scope.replace("\"", "");
+
+                            Scope newScope = scopeStore.findByName(scope, resourceServer.getId());
+
+                            if (newScope == null) {
+                                throw new RuntimeException("Scope with name [" + scope + "] not defined.");
+                            }
+
+                            scopeNames = scopeNames + "\"" + newScope.getId() + "\"";
+                        }
+
+                        config.put("scopes", "[" + scopeNames + "]");
+                    }
+                }
+
+                String policyResources = config.get("resources");
+
+                if (policyResources != null && !policyResources.isEmpty()) {
+                    policyResources = policyResources.replace("[", "");
+                    policyResources = policyResources.replace("]", "");
+
+                    if (!policyResources.isEmpty()) {
+                        String resourceNames = "";
+
+                        for (String resource : policyResources.split(",")) {
+                            if (!resourceNames.isEmpty()) {
+                                resourceNames =  resourceNames + ",";
+                            }
+
+                            resource = resource.replace("\"", "");
+
+                            if ("".equals(resource)) {
+                                continue;
+                            }
+
+                            resourceNames = resourceNames + "\"" + storeFactory.getResourceStore().findByName(resource, resourceServer.getId()).getId() + "\"";
+                        }
+
+                        config.put("resources", "[" + resourceNames + "]");
+                    }
+                }
+
+                String applyPolicies = config.get("applyPolicies");
+
+                if (applyPolicies != null && !applyPolicies.isEmpty()) {
+                    applyPolicies = applyPolicies.replace("[", "");
+                    applyPolicies = applyPolicies.replace("]", "");
+
+                    if (!applyPolicies.isEmpty()) {
+                        String policyNames = "";
+
+                        for (String pId : applyPolicies.split(",")) {
+                            if (!policyNames.isEmpty()) {
+                                policyNames = policyNames + ",";
+                            }
+
+                            pId = pId.replace("\"", "").trim();
+
+                            Policy policy = policyStore.findByName(pId, resourceServer.getId());
+
+                            if (policy == null) {
+                                throw new RuntimeException("Policy with name [" + pId + "] not defined.");
+                            }
+
+                            policyNames = policyNames + "\"" + policy.getId() + "\"";
+                        }
+
+                        config.put("applyPolicies", "[" + policyNames + "]");
+                    }
+                }
+
+                Policy existing = policyStore.findByName(policyRepresentation.getName(), this.resourceServer.getId());
+
+                if (existing != null) {
+                    policyResource.update(existing.getId(), policyRepresentation);
+                } else {
+                    policyResource.create(policyRepresentation);
+                }
+            });
+        }
+
+        return Response.noContent().build();
+    }
+
+    @Path("/resource")
+    public ResourceSetService getResourceSetResource() {
+        ResourceSetService resource = new ResourceSetService(this.resourceServer, this.authorization, this.auth);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    @Path("/scope")
+    public ScopeService getScopeResource() {
+        ScopeService resource = new ScopeService(this.resourceServer, this.authorization, this.auth);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    @Path("/policy")
+    public PolicyService getPolicyResource() {
+        PolicyService resource = new PolicyService(this.resourceServer, this.authorization, this.auth);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    private void createDefaultPermission(ResourceRepresentation resource, PolicyRepresentation policy) {
+        PolicyRepresentation defaultPermission = new PolicyRepresentation();
+
+        defaultPermission.setName("Default Permission");
+        defaultPermission.setType("resource");
+        defaultPermission.setDescription("A permission that applies to the default resource type");
+        defaultPermission.setDecisionStrategy(Policy.DecisionStrategy.UNANIMOUS);
+        defaultPermission.setLogic(Policy.Logic.POSITIVE);
+
+        HashMap<String, String> defaultPermissionConfig = new HashMap<>();
+
+        defaultPermissionConfig.put("default", "true");
+        defaultPermissionConfig.put("defaultResourceType", resource.getType());
+        defaultPermissionConfig.put("applyPolicies", "[\"" + policy.getName() + "\"]");
+
+        defaultPermission.setConfig(defaultPermissionConfig);
+
+        getPolicyResource().create(defaultPermission);
+    }
+
+    private PolicyRepresentation createDefaultPolicy() {
+        PolicyRepresentation defaultPolicy = new PolicyRepresentation();
+
+        defaultPolicy.setName("Only From Realm Policy");
+        defaultPolicy.setDescription("A policy that grants access only for users within this realm");
+        defaultPolicy.setType("js");
+        defaultPolicy.setDecisionStrategy(Policy.DecisionStrategy.AFFIRMATIVE);
+        defaultPolicy.setLogic(Policy.Logic.POSITIVE);
+
+        HashMap<String, String> defaultPolicyConfig = new HashMap<>();
+
+        defaultPolicyConfig.put("code", "var context = $evaluation.getContext();\n" +
+                "\n" +
+                "// using attributes from the evaluation context to obtain the realm\n" +
+                "var contextAttributes = context.getAttributes();\n" +
+                "var realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n" +
+                "\n" +
+                "// using attributes from the identity to obtain the issuer\n" +
+                "var identity = context.getIdentity();\n" +
+                "var identityAttributes = identity.getAttributes();\n" +
+                "var issuer = identityAttributes.getValue('iss').asString(0);\n" +
+                "\n" +
+                "// only users from the realm have access granted \n" +
+                "if (issuer.endsWith(realmName)) {\n" +
+                "    $evaluation.grant();\n" +
+                "}");
+
+        defaultPolicy.setConfig(defaultPolicyConfig);
+
+        getPolicyResource().create(defaultPolicy);
+
+        return defaultPolicy;
+    }
+
+    private ResourceRepresentation createDefaultResource() {
+        ResourceRepresentation defaultResource = new ResourceRepresentation();
+
+        defaultResource.setName("Default Resource");
+        defaultResource.setUri("/*");
+        defaultResource.setType("urn:" + this.client.getClientId() + ":resources:default");
+
+        getResourceSetResource().create(defaultResource);
+        return defaultResource;
+    }
+
+    private void createDefaultRoles() {
+        RoleModel umaProtectionRole = client.getRole(Constants.AUTHZ_UMA_PROTECTION);
+
+        if (umaProtectionRole == null) {
+            umaProtectionRole = client.addRole(Constants.AUTHZ_UMA_PROTECTION);
+        }
+
+        UserModel serviceAccount = this.session.users().getUserByServiceAccountClient(client);
+
+        if (!serviceAccount.hasRole(umaProtectionRole)) {
+            serviceAccount.grantRole(umaProtectionRole);
+        }
+    }
+
+    private PolicyRepresentation createPolicyRepresentation(StoreFactory storeFactory, Policy policy) {
+        PolicyRepresentation rep = Models.toRepresentation(policy, authorization);
+
+        rep.setId(null);
+        rep.setDependentPolicies(null);
+
+        Map<String, String> config = rep.getConfig();
+
+        String roles = config.get("roles");
+
+        if (roles != null && !roles.isEmpty()) {
+            roles = roles.replace("[", "");
+            roles = roles.replace("]", "");
+
+            if (!roles.isEmpty()) {
+                String roleNames = "";
+
+                for (String role : roles.split(",")) {
+                    if (!roleNames.isEmpty()) {
+                        roleNames = roleNames + ",";
+                    }
+
+                    role = role.replace("\"", "");
+
+                    roleNames = roleNames + "\"" + this.realm.getRoleById(role).getName() + "\"";
+                }
+
+                config.put("roles", "[" + roleNames + "]");
+            }
+        }
+
+        String users = config.get("users");
+
+        if (users != null) {
+            users = users.replace("[", "");
+            users = users.replace("]", "");
+
+            if (!users.isEmpty()) {
+                UserFederationManager userManager = this.session.users();
+                String userNames = "";
+
+                for (String user : users.split(",")) {
+                    if (!userNames.isEmpty()) {
+                        userNames =  userNames + ",";
+                    }
+
+                    user = user.replace("\"", "");
+
+                    userNames = userNames + "\"" + userManager.getUserById(user, this.realm).getUsername() + "\"";
+                }
+
+                config.put("users", "[" + userNames + "]");
+            }
+        }
+
+        String scopes = config.get("scopes");
+
+        if (scopes != null && !scopes.isEmpty()) {
+            scopes = scopes.replace("[", "");
+            scopes = scopes.replace("]", "");
+
+            if (!scopes.isEmpty()) {
+                ScopeStore scopeStore = storeFactory.getScopeStore();
+                String scopeNames = "";
+
+                for (String scope : scopes.split(",")) {
+                    if (!scopeNames.isEmpty()) {
+                        scopeNames =  scopeNames + ",";
+                    }
+
+                    scope = scope.replace("\"", "");
+
+                    scopeNames = scopeNames + "\"" + scopeStore.findById(scope).getName() + "\"";
+                }
+
+                config.put("scopes", "[" + scopeNames + "]");
+            }
+        }
+
+        String policyResources = config.get("resources");
+
+        if (policyResources != null && !policyResources.isEmpty()) {
+            policyResources = policyResources.replace("[", "");
+            policyResources = policyResources.replace("]", "");
+
+            if (!policyResources.isEmpty()) {
+                ResourceStore resourceStore = storeFactory.getResourceStore();
+                String resourceNames = "";
+
+                for (String resource : policyResources.split(",")) {
+                    if (!resourceNames.isEmpty()) {
+                        resourceNames =  resourceNames + ",";
+                    }
+
+                    resource = resource.replace("\"", "");
+
+                    resourceNames = resourceNames + "\"" + resourceStore.findById(resource).getName() + "\"";
+                }
+
+                config.put("resources", "[" + resourceNames + "]");
+            }
+        }
+
+        String policyNames = "";
+        Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+
+        if (!associatedPolicies.isEmpty()) {
+            for (Policy associatedPolicy : associatedPolicies) {
+                if (!policyNames.isEmpty()) {
+                    policyNames = policyNames + ",";
+                }
+
+                policyNames = policyNames + "\"" + associatedPolicy.getName() + "\"";
+            }
+
+            config.put("applyPolicies", "[" + policyNames + "]");
+        }
+
+        return rep;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
new file mode 100644
index 0000000..c9b30b2
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -0,0 +1,176 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.admin.util.Models;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceSetService {
+
+    private final AuthorizationProvider authorization;
+    private final RealmAuth auth;
+    private ResourceServer resourceServer;
+
+    public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+        this.auth = auth;
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(ResourceRepresentation resource) {
+        requireManage();
+        StoreFactory storeFactory = this.authorization.getStoreFactory();
+        Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), this.resourceServer.getId());
+
+        if (existingResource != null && existingResource.getResourceServer().getId().equals(this.resourceServer.getId())
+                && existingResource.getOwner().equals(resource.getOwner())) {
+            return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
+        }
+
+        Resource model = Models.toModel(resource, this.resourceServer, authorization);
+
+        ResourceRepresentation representation = new ResourceRepresentation();
+
+        representation.setId(model.getId());
+
+        return Response.status(Status.CREATED).entity(representation).build();
+    }
+
+    @Path("{id}")
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response update(@PathParam("id") String id, ResourceRepresentation resource) {
+        requireManage();
+        resource.setId(id);
+        StoreFactory storeFactory = this.authorization.getStoreFactory();
+        ResourceStore resourceStore = storeFactory.getResourceStore();
+        Resource model = resourceStore.findById(resource.getId());
+
+        if (model == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        model.setName(resource.getName());
+        model.setType(resource.getType());
+        model.setUri(resource.getUri());
+        model.setIconUri(resource.getIconUri());
+
+        model.updateScopes(resource.getScopes().stream()
+                .map((ScopeRepresentation scope) -> Models.toModel(scope, this.resourceServer, authorization))
+                .collect(Collectors.toSet()));
+
+        return Response.noContent().build();
+    }
+
+    @Path("{id}")
+    @DELETE
+    public Response delete(@PathParam("id") String id) {
+        requireManage();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Resource resource = storeFactory.getResourceStore().findById(id);
+
+        if (resource == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+        List<Policy> policies = policyStore.findByResource(id);
+
+        for (Policy policyModel : policies) {
+            if (policyModel.getResources().size() == 1) {
+                policyStore.delete(policyModel.getId());
+            } else {
+                policyModel.addResource(resource);
+            }
+        }
+
+        storeFactory.getResourceStore().delete(id);
+
+        return Response.noContent().build();
+    }
+
+    @Path("{id}")
+    @GET
+    @Produces("application/json")
+    public Response findById(@PathParam("id") String id) {
+        requireView();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Resource model = storeFactory.getResourceStore().findById(id);
+
+        if (model == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        return Response.ok(Models.toRepresentation(model, this.resourceServer, authorization)).build();
+    }
+
+    @GET
+    @Produces("application/json")
+    public Response findAll() {
+        requireView();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+
+        return Response.ok(
+                storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream()
+                        .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                        .collect(Collectors.toList()))
+                .build();
+    }
+
+    private void requireView() {
+        if (this.auth != null) {
+            this.auth.requireView();
+        }
+    }
+
+    private void requireManage() {
+        if (this.auth != null) {
+            this.auth.requireManage();
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
new file mode 100644
index 0000000..56291c8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.keycloak.authorization.admin.util.Models.toModel;
+import static org.keycloak.authorization.admin.util.Models.toRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeService {
+
+    private final AuthorizationProvider authorization;
+    private final RealmAuth auth;
+    private ResourceServer resourceServer;
+
+    public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+        this.auth = auth;
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(ScopeRepresentation scope) {
+        this.auth.requireManage();
+        Scope model = toModel(scope, this.resourceServer, authorization);
+
+        scope.setId(model.getId());
+
+        return Response.status(Status.CREATED).entity(scope).build();
+    }
+
+    @Path("{id}")
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response update(@PathParam("id") String id, ScopeRepresentation scope) {
+        this.auth.requireManage();
+        scope.setId(id);
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Scope model = storeFactory.getScopeStore().findById(scope.getId());
+
+        if (model == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        model.setName(scope.getName());
+        model.setIconUri(scope.getIconUri());
+
+        return Response.noContent().build();
+    }
+
+    @Path("{id}")
+    @DELETE
+    public Response delete(@PathParam("id") String id) {
+        this.auth.requireManage();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        List<Resource> resources = storeFactory.getResourceStore().findByScope(id);
+
+        if (!resources.isEmpty()) {
+            return ErrorResponse.exists("Scopes can not be removed while associated with resources.");
+        }
+
+        Scope scope = storeFactory.getScopeStore().findById(id);
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+        List<Policy> policies = policyStore.findByScopeIds(Arrays.asList(scope.getId()), resourceServer.getId());
+
+        for (Policy policyModel : policies) {
+            if (policyModel.getScopes().size() == 1) {
+                policyStore.delete(policyModel.getId());
+            } else {
+                policyModel.removeScope(scope);
+            }
+        }
+
+        storeFactory.getScopeStore().delete(id);
+
+        return Response.noContent().build();
+    }
+
+    @Path("{id}")
+    @GET
+    @Produces("application/json")
+    public Response findById(@PathParam("id") String id) {
+        this.auth.requireView();
+        Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id);
+
+        if (model == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        return Response.ok(toRepresentation(model, this.authorization)).build();
+    }
+
+    @GET
+    @Produces("application/json")
+    public Response findAll() {
+        this.auth.requireView();
+        return Response.ok(
+                this.authorization.getStoreFactory().getScopeStore().findByResourceServer(this.resourceServer.getId()).stream()
+                        .map(scope -> toRepresentation(scope, this.authorization))
+                        .collect(Collectors.toList()))
+                .build();
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/util/Models.java b/services/src/main/java/org/keycloak/authorization/admin/util/Models.java
new file mode 100644
index 0000000..abdd980
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/util/Models.java
@@ -0,0 +1,285 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.admin.util;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.ErrorCode;
+import org.keycloak.authorization.admin.representation.PolicyRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceOwnerRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceServerRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.Response.Status;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Some utility methods to transform models to representations and vice-versa.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public final class Models {
+
+    public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
+        ScopeRepresentation scope = new ScopeRepresentation();
+
+        scope.setId(model.getId());
+        scope.setName(model.getName());
+        scope.setIconUri(model.getIconUri());
+        scope.setPolicies(new ArrayList<>());
+
+        Set<Policy> policies = new HashSet<>();
+
+        policies.addAll(authorizationProvider.getStoreFactory().getPolicyStore().findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()));
+
+        for (Policy policyModel : policies) {
+            PolicyRepresentation policy = new PolicyRepresentation();
+
+            policy.setId(policyModel.getId());
+            policy.setName(policyModel.getName());
+            policy.setType(policyModel.getType());
+
+            if (!scope.getPolicies().contains(policy)) {
+                scope.getPolicies().add(policy);
+            }
+        }
+
+        return scope;
+    }
+
+    public static Scope toModel(ScopeRepresentation scope, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Scope model = storeFactory.getScopeStore().findByName(scope.getName(), resourceServer.getId());
+
+        if (model == null) {
+            model = storeFactory.getScopeStore().create(scope.getName(), resourceServer);
+
+            model.setIconUri(scope.getIconUri());
+        }
+
+        return model;
+    }
+
+    public static ResourceServerRepresentation toRepresentation(ResourceServer model, RealmModel realm) {
+        ResourceServerRepresentation server = new ResourceServerRepresentation();
+
+        server.setId(model.getId());
+        server.setClientId(model.getClientId());
+        ClientModel clientById = realm.getClientById(model.getClientId());
+        server.setName(clientById.getClientId());
+        server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement());
+        server.setPolicyEnforcementMode(model.getPolicyEnforcementMode());
+
+        return server;
+    }
+
+    public static ResourceServer toModel(ResourceServerRepresentation server, AuthorizationProvider authorization) {
+        RealmModel realm = authorization.getKeycloakSession().getContext().getRealm();
+        ClientModel client = realm.getClientById(server.getClientId());
+
+        if (client == null) {
+            throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Client with id [" + server.getClientId() + "] not found in realm [" + realm.getName()  + "].", Status.BAD_REQUEST);
+        }
+
+        if (!client.isServiceAccountsEnabled()) {
+            throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Client with id [" + server.getClientId() + "] must have a service account.", Status.BAD_REQUEST);
+        }
+
+        ResourceServer existingResourceServer = authorization.getStoreFactory().getResourceServerStore().findByClient(client.getId());
+
+        if (existingResourceServer != null) {
+            throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Resource server already exists with client id [" + server.getClientId() + "].", Status.BAD_REQUEST);
+        }
+
+        if (server.getName() == null) {
+            server.setName(client.getName());
+        }
+
+        ResourceServer model = authorization.getStoreFactory().getResourceServerStore().create(client.getId());
+
+        model.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
+        model.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
+
+        return model;
+    }
+
+    public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
+        PolicyRepresentation representation = new PolicyRepresentation();
+
+        representation.setId(model.getId());
+        representation.setName(model.getName());
+        representation.setDescription(model.getDescription());
+        representation.setType(model.getType());
+        representation.setDecisionStrategy(model.getDecisionStrategy());
+        representation.setLogic(model.getLogic());
+        representation.setConfig(new HashMap<>(model.getConfig()));
+
+        List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId());
+
+        representation.setDependentPolicies(policies.stream().map(policy -> {
+            PolicyRepresentation representation1 = new PolicyRepresentation();
+
+            representation1.setId(policy.getId());
+            representation1.setName(policy.getName());
+
+            return representation1;
+        }).collect(Collectors.toList()));
+
+        List<String> obj = model.getAssociatedPolicies().stream().map(new Function<Policy, String>() {
+            @Override
+            public String apply(Policy policy) {
+                return policy.getId();
+            }
+        }).collect(Collectors.toList());
+
+        try {
+            representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return representation;
+    }
+
+    public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorizationManager) {
+        Policy model = authorizationManager.getStoreFactory().getPolicyStore().create(policy.getName(), policy.getType(), resourceServer);
+
+        model.setDescription(policy.getDescription());
+        model.setDecisionStrategy(policy.getDecisionStrategy());
+        model.setLogic(policy.getLogic());
+        model.setConfig(policy.getConfig());
+
+        return model;
+    }
+
+    public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setId(model.getId());
+        resource.setType(model.getType());
+        resource.setName(model.getName());
+        resource.setUri(model.getUri());
+        resource.setIconUri(model.getIconUri());
+
+        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
+
+        owner.setId(model.getOwner());
+
+        KeycloakSession keycloakSession = authorization.getKeycloakSession();
+        RealmModel realm = keycloakSession.getContext().getRealm();
+
+        if (owner.getId().equals(resourceServer.getClientId())) {
+            ClientModel clientModel = realm.getClientById(resourceServer.getClientId());
+            owner.setName(clientModel.getClientId());
+        } else {
+            UserModel userModel = keycloakSession.users().getUserById(owner.getId(), realm);
+
+            if (userModel == null) {
+                throw new ErrorResponseException("invalid_owner", "Could not find the user [" + owner.getId() + "] who owns the Resource [" + resource.getId() + "].", Status.BAD_REQUEST);
+            }
+
+            owner.setName(userModel.getUsername());
+        }
+
+        resource.setOwner(owner);
+
+        resource.setScopes(model.getScopes().stream().map(model1 -> {
+            ScopeRepresentation scope = new ScopeRepresentation();
+            scope.setId(model1.getId());
+            scope.setName(model1.getName());
+            String iconUri = model1.getIconUri();
+            if (iconUri != null) {
+                scope.setIconUri(iconUri);
+            }
+            return scope;
+        }).collect(Collectors.toSet()));
+
+        resource.setPolicies(new ArrayList<>());
+
+        Set<Policy> policies = new HashSet<>();
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+
+        policies.addAll(policyStore.findByResource(resource.getId()));
+        policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId()));
+        policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
+
+        for (Policy policyModel : policies) {
+            PolicyRepresentation policy = new PolicyRepresentation();
+
+            policy.setId(policyModel.getId());
+            policy.setName(policyModel.getName());
+            policy.setType(policyModel.getType());
+
+            if (!resource.getPolicies().contains(policy)) {
+                resource.getPolicies().add(policy);
+            }
+        }
+
+        return resource;
+    }
+
+    public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        ResourceOwnerRepresentation owner = resource.getOwner();
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+            owner.setId(resourceServer.getClientId());
+        }
+
+        if (owner.getId() == null) {
+            throw new ErrorResponseException("invalid_owner", "No owner specified for resource [" + resource.getName() + "].", Status.BAD_REQUEST);
+        }
+
+        ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+        Resource model = resourceStore.create(resource.getName(), resourceServer, owner.getId());
+
+        model.setType(resource.getType());
+        model.setUri(resource.getUri());
+        model.setIconUri(resource.getIconUri());
+
+        Set<ScopeRepresentation> scopes = resource.getScopes();
+
+        if (scopes != null) {
+            model.updateScopes(scopes.stream().map((Function<ScopeRepresentation, Scope>) scope -> toModel(scope, resourceServer, authorization)).collect(Collectors.toSet()));
+        }
+
+        return model;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
new file mode 100644
index 0000000..ad154a6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -0,0 +1,241 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.OAuthErrorException;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.authorization.representation.AuthorizationRequest;
+import org.keycloak.authorization.authorization.representation.AuthorizationResponse;
+import org.keycloak.authorization.common.KeycloakEvaluationContext;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.protection.permission.PermissionTicket;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.authorization.util.Permissions;
+import org.keycloak.authorization.util.Tokens;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.authorization.Permission;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.Cors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationTokenService {
+
+    private final AuthorizationProvider authorization;
+
+    @Context
+    private HttpRequest httpRequest;
+
+    public AuthorizationTokenService(AuthorizationProvider authorization) {
+        this.authorization = authorization;
+    }
+
+    @OPTIONS
+    public Response authorizepPreFlight() {
+        return Cors.add(this.httpRequest, Response.ok()).auth().preflight().build();
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public void authorize(AuthorizationRequest authorizationRequest, @Suspended AsyncResponse asyncResponse) {
+        KeycloakEvaluationContext evaluationContext = new KeycloakEvaluationContext(this.authorization.getKeycloakSession());
+        KeycloakIdentity identity = (KeycloakIdentity) evaluationContext.getIdentity();
+
+        if (!identity.hasRole("uma_authorization")) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_authorization scope.", Status.FORBIDDEN);
+        }
+
+        if (authorizationRequest == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid authorization request.", Status.BAD_REQUEST);
+        }
+
+        PermissionTicket ticket = verifyPermissionTicket(authorizationRequest);
+
+        authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate(new DecisionResultCollector() {
+            @Override
+            public void onComplete(List<Result> results) {
+                List<Permission> entitlements = Permissions.allPermits(results);
+
+                if (entitlements.isEmpty()) {
+                    asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN)
+                            .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
+                            .allowedOrigins(identity.getAccessToken())
+                            .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+                } else {
+                    AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken()));
+                    asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken())
+                            .allowedMethods("POST")
+                            .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+                }
+            }
+
+            @Override
+            public void onError(Throwable cause) {
+                asyncResponse.resume(cause);
+            }
+        });
+    }
+
+    private List<ResourcePermission> createPermissions(PermissionTicket ticket, AuthorizationRequest request, AuthorizationProvider authorization) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Map<String, Set<String>> permissionsToEvaluate = new HashMap<>();
+
+        ticket.getResources().forEach(requestedResource -> {
+            Resource resource;
+
+            if (requestedResource.getId() != null) {
+                resource = storeFactory.getResourceStore().findById(requestedResource.getId());
+            } else {
+                resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId());
+            }
+
+            if (resource == null) {
+                throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getId() + "] or name [" + requestedResource.getName() + "] does not exist.", Status.FORBIDDEN);
+            }
+
+            Set<ScopeRepresentation> requestedScopes = requestedResource.getScopes();
+
+            permissionsToEvaluate.put(resource.getId(), requestedScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet()));
+        });
+
+        String rpt = request.getRpt();
+
+        if (rpt != null && !"".equals(rpt)) {
+            if (!Tokens.verifySignature(rpt, getRealm().getPublicKey())) {
+                throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
+            }
+
+            AccessToken requestingPartyToken;
+
+            try {
+                requestingPartyToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
+            } catch (JWSInputException e) {
+                throw new ErrorResponseException("invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
+            }
+
+            if (requestingPartyToken.isActive()) {
+                AccessToken.Authorization authorizationData = requestingPartyToken.getAuthorization();
+
+                if (authorizationData != null) {
+                    List<Permission> permissions = authorizationData.getPermissions();
+
+                    if (permissions != null) {
+                        permissions.forEach(permission -> {
+                            Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
+
+                            if (resourcePermission != null) {
+                                Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
+
+                                if (scopes == null) {
+                                    scopes = new HashSet<>();
+                                    permissionsToEvaluate.put(resourcePermission.getId(), scopes);
+                                }
+
+                                Set<String> scopePermission = permission.getScopes();
+
+                                if (scopePermission != null) {
+                                    scopes.addAll(scopePermission);
+                                }
+                            }
+                        });
+                    }
+                }
+            }
+        }
+
+        return permissionsToEvaluate.entrySet().stream()
+                .flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
+                    Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
+                    if (entry.getValue().isEmpty()) {
+                        return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream();
+                    } else {
+                        return entry.getValue().stream()
+                                .map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId()))
+                                .filter(scope -> scope != null)
+                                .map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer()));
+                    }
+                }).collect(Collectors.toList());
+    }
+
+    private RealmModel getRealm() {
+        return this.authorization.getKeycloakSession().getContext().getRealm();
+    }
+
+    private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken) {
+        AccessToken.Authorization authorization = new AccessToken.Authorization();
+
+        authorization.setPermissions(permissions);
+        accessToken.setAuthorization(authorization);
+
+        return new TokenManager().encodeToken(getRealm(), accessToken);
+    }
+
+    private PermissionTicket verifyPermissionTicket(AuthorizationRequest request) {
+        String ticketString = request.getTicket();
+
+        if (ticketString == null || !Tokens.verifySignature(ticketString, getRealm().getPublicKey())) {
+            throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
+        }
+
+        try {
+            PermissionTicket ticket = new JWSInput(ticketString).readJsonContent(PermissionTicket.class);
+
+            if (!ticket.isActive()) {
+                throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN);
+            }
+
+            return ticket;
+        } catch (JWSInputException e) {
+            throw new ErrorResponseException("invalid_ticket", "Could not parse permission ticket.", Status.FORBIDDEN);
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationRequest.java b/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationRequest.java
new file mode 100644
index 0000000..d4f0f24
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationRequest.java
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationRequest {
+
+    private String ticket;
+    private String rpt;
+
+    public AuthorizationRequest(String ticket, String rpt) {
+        this.ticket = ticket;
+        this.rpt = rpt;
+    }
+
+    public AuthorizationRequest(String ticket) {
+        this(ticket, null);
+    }
+
+    public AuthorizationRequest() {
+        this(null, null);
+    }
+
+    public String getTicket() {
+        return this.ticket;
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationResponse.java b/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationResponse.java
new file mode 100644
index 0000000..cd0a521
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationResponse.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationResponse {
+
+    private String rpt;
+
+    public AuthorizationResponse(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public AuthorizationResponse() {
+        this(null);
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+
+    public void setRpt(final String rpt) {
+        this.rpt = rpt;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/AuthorizationService.java b/services/src/main/java/org/keycloak/authorization/AuthorizationService.java
new file mode 100644
index 0000000..f519b40
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/AuthorizationService.java
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.authorization.AuthorizationTokenService;
+import org.keycloak.authorization.entitlement.EntitlementService;
+import org.keycloak.authorization.protection.ProtectionService;
+
+import javax.ws.rs.Path;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationService {
+
+    private final AuthorizationProvider authorization;
+
+    public AuthorizationService(AuthorizationProvider authorization) {
+        this.authorization = authorization;
+    }
+
+    @Path("/entitlement")
+    public Object getEntitlementService() {
+        EntitlementService service = new EntitlementService(this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(service);
+
+        return service;
+    }
+
+    @Path("/protection")
+    public Object getProtectionService() {
+        ProtectionService service = new ProtectionService(this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(service);
+
+        return service;
+    }
+
+    @Path("/authorize")
+    public Object authorize() {
+        AuthorizationTokenService resource = new AuthorizationTokenService(this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
new file mode 100644
index 0000000..fc929ec
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.common;
+
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.representations.AccessToken;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakEvaluationContext implements EvaluationContext {
+
+    private final KeycloakIdentity identity;
+    private final KeycloakSession keycloakSession;
+
+    public KeycloakEvaluationContext(KeycloakSession keycloakSession) {
+        this(new KeycloakIdentity(keycloakSession), keycloakSession);
+    }
+
+    public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
+        this.identity = identity;
+        this.keycloakSession = keycloakSession;
+    }
+
+    @Override
+    public Identity getIdentity() {
+        return this.identity;
+    }
+
+    @Override
+    public Attributes getAttributes() {
+        HashMap<String, Collection<String>> attributes = new HashMap<>();
+
+        attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date())));
+        attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr()));
+        attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost()));
+
+        AccessToken accessToken = this.identity.getAccessToken();
+
+        if (accessToken != null) {
+            attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
+        }
+
+        List<String> userAgents = this.keycloakSession.getContext().getRequestHeaders().getRequestHeader("User-Agent");
+
+        if (userAgents != null) {
+            attributes.put("kc.client.user_agent", userAgents);
+        }
+
+        attributes.put("kc.realm.name", Arrays.asList(this.keycloakSession.getContext().getRealm().getName()));
+
+        return Attributes.from(attributes);
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
new file mode 100644
index 0000000..9227854
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -0,0 +1,155 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.common;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.util.Tokens;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakIdentity implements Identity {
+
+    private final AccessToken accessToken;
+    private final RealmModel realm;
+    private final KeycloakSession keycloakSession;
+    private final Attributes attributes;
+
+    public KeycloakIdentity(KeycloakSession keycloakSession) {
+        this(Tokens.getAccessToken(keycloakSession), keycloakSession);
+    }
+
+    public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
+        this.accessToken = accessToken;
+
+        if (this.accessToken == null) {
+            throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
+        }
+
+        this.keycloakSession = keycloakSession;
+        this.realm = keycloakSession.getContext().getRealm();
+
+        HashMap<String, Collection<String>> attributes = new HashMap<>();
+
+        try {
+            ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken);
+            Iterator<String> iterator = objectNode.fieldNames();
+            List<String> roleNames = new ArrayList<>();
+
+            while (iterator.hasNext()) {
+                String fieldName = iterator.next();
+                JsonNode fieldValue = objectNode.get(fieldName);
+                List<String> values = new ArrayList<>();
+
+                values.add(fieldValue.asText());
+
+                if (fieldName.equals("realm_access")) {
+                    JsonNode grantedRoles = fieldValue.get("roles");
+
+                    if (grantedRoles != null) {
+                        Iterator<JsonNode> rolesIt = grantedRoles.iterator();
+
+                        while (rolesIt.hasNext()) {
+                            roleNames.add(rolesIt.next().asText());
+                        }
+                    }
+                }
+
+                if (fieldName.equals("resource_access")) {
+                    Iterator<JsonNode> resourceAccessIt = fieldValue.iterator();
+
+                    while (resourceAccessIt.hasNext()) {
+                        JsonNode grantedRoles = resourceAccessIt.next().get("roles");
+
+                        if (grantedRoles != null) {
+                            Iterator<JsonNode> rolesIt = grantedRoles.iterator();
+
+                            while (rolesIt.hasNext()) {
+                                roleNames.add(rolesIt.next().asText());
+                            }
+                        }
+                    }
+                }
+
+                attributes.put(fieldName, values);
+            }
+
+            attributes.put("roles", roleNames);
+        } catch (Exception e) {
+            throw new RuntimeException("Error while reading attributes from security token.", e);
+        }
+
+        this.attributes = Attributes.from(attributes);
+    }
+
+    @Override
+    public String getId() {
+        if (isResourceServer()) {
+            ClientSessionModel clientSession = this.keycloakSession.sessions().getClientSession(this.accessToken.getClientSession());
+            return clientSession.getClient().getId();
+        }
+
+        return this.accessToken.getSubject();
+    }
+
+    @Override
+    public Attributes getAttributes() {
+        return this.attributes;
+    }
+
+    public AccessToken getAccessToken() {
+        return this.accessToken;
+    }
+
+    private  boolean isResourceServer() {
+        UserModel clientUser = null;
+
+        if (this.accessToken.getClientSession() != null) {
+            ClientSessionModel clientSession = this.keycloakSession.sessions().getClientSession(this.accessToken.getClientSession());
+            clientUser = this.keycloakSession.users().getUserByServiceAccountClient(clientSession.getClient());
+        } else if (this.accessToken.getIssuedFor() != null) {
+            ClientModel clientModel = this.keycloakSession.realms().getClientById(this.accessToken.getIssuedFor(), this.realm);
+            clientUser = this.keycloakSession.users().getUserByServiceAccountClient(clientModel);
+        }
+
+
+        if (clientUser == null) {
+            return false;
+        }
+
+        return this.accessToken.getSubject().equals(clientUser.getId());
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/config/Configuration.java b/services/src/main/java/org/keycloak/authorization/config/Configuration.java
new file mode 100644
index 0000000..6669edc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/config/Configuration.java
@@ -0,0 +1,269 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.config;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.keycloak.protocol.oidc.OIDCWellKnownProvider.DEFAULT_GRANT_TYPES_SUPPORTED;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Configuration {
+
+    private static final String UMA_VERSION = "1.0";
+    private static final List<String> DEFAULT_TOKEN_PROFILES = Arrays.asList("bearer");
+
+    public static final Configuration fromDefault(String authzServerUri,
+                                                  String realm,
+                                                  URI authorizationEndpoint,
+                                                  URI tokenEndpoint, String publicKeyPem) {
+        Configuration configuration = new Configuration();
+
+        if (authzServerUri.endsWith("/")) {
+            authzServerUri = authzServerUri.substring(0, authzServerUri.lastIndexOf("/"));
+        }
+
+        configuration.setVersion(UMA_VERSION);
+        configuration.setIssuer(URI.create(authzServerUri));
+        configuration.setPatProfiles(DEFAULT_TOKEN_PROFILES);
+        configuration.setAatProfiles(DEFAULT_TOKEN_PROFILES);
+        configuration.setRptProfiles(DEFAULT_TOKEN_PROFILES);
+        configuration.setPatGrantTypes(DEFAULT_GRANT_TYPES_SUPPORTED);
+        configuration.setAatGrantTypes(DEFAULT_GRANT_TYPES_SUPPORTED);
+        configuration.setTokenEndpoint(tokenEndpoint);
+        configuration.setAuthorizationEndpoint(authorizationEndpoint);
+        configuration.setResourceSetRegistrationEndpoint(URI.create(authzServerUri + "/authz/protection/resource_set"));
+        configuration.setPermissionRegistrationEndpoint(URI.create(authzServerUri + "/authz/protection/permission"));
+        configuration.setRptEndpoint(URI.create(authzServerUri + "/authz/authorize"));
+        configuration.setRealmPublicKey(publicKeyPem);
+        configuration.setServerUrl(URI.create(authzServerUri));
+        configuration.setRealm(realm);
+
+        return configuration;
+    }
+
+    private String realmPublicKey;
+    private String version;
+    private URI issuer;
+
+    @JsonProperty("pat_profiles_supported")
+    private List<String> patProfiles;
+
+    @JsonProperty("pat_grant_types_supported")
+    private List<String> patGrantTypes;
+
+    @JsonProperty("aat_profiles_supported")
+    private List<String> aatProfiles;
+
+    @JsonProperty("aat_grant_types_supported")
+    private List<String> aatGrantTypes;
+
+    @JsonProperty("rpt_profiles_supported")
+    private List<String> rptProfiles;
+
+    @JsonProperty("claim_token_profiles_supported")
+    private List<String> claimTokenProfiles;
+
+    @JsonProperty("dynamic_client_endpoint")
+    private URI dynamicClientEndpoint;
+
+    @JsonProperty("token_endpoint")
+    private URI tokenEndpoint;
+
+    @JsonProperty("authorization_endpoint")
+    private URI authorizationEndpoint;
+
+    @JsonProperty("requesting_party_claims_endpoint")
+    private URI requestingPartyClaimsEndpoint;
+
+    @JsonProperty("resource_set_registration_endpoint")
+    private URI resourceSetRegistrationEndpoint;
+
+    @JsonProperty("introspection_endpoint")
+    private URI introspectionEndpoint;
+
+    @JsonProperty("permission_registration_endpoint")
+    private URI permissionRegistrationEndpoint;
+
+    @JsonProperty("rpt_endpoint")
+    private URI rptEndpoint;
+
+    /**
+     * Non-standard, Keycloak specific configuration options
+     */
+    private String realm;
+
+    private URI serverUrl;
+
+    public String getVersion() {
+        return this.version;
+    }
+
+    void setVersion(final String version) {
+        this.version = version;
+    }
+
+    public URI getIssuer() {
+        return this.issuer;
+    }
+
+    void setIssuer(final URI issuer) {
+        this.issuer = issuer;
+    }
+
+    public List<String> getPatProfiles() {
+        return this.patProfiles;
+    }
+
+    void setPatProfiles(final List<String> patProfiles) {
+        this.patProfiles = patProfiles;
+    }
+
+    public List<String> getPatGrantTypes() {
+        return this.patGrantTypes;
+    }
+
+    void setPatGrantTypes(final List<String> patGrantTypes) {
+        this.patGrantTypes = patGrantTypes;
+    }
+
+    public List<String> getAatProfiles() {
+        return this.aatProfiles;
+    }
+
+    void setAatProfiles(final List<String> aatProfiles) {
+        this.aatProfiles = aatProfiles;
+    }
+
+    public List<String> getAatGrantTypes() {
+        return this.aatGrantTypes;
+    }
+
+    void setAatGrantTypes(final List<String> aatGrantTypes) {
+        this.aatGrantTypes = aatGrantTypes;
+    }
+
+    public List<String> getRptProfiles() {
+        return this.rptProfiles;
+    }
+
+    void setRptProfiles(final List<String> rptProfiles) {
+        this.rptProfiles = rptProfiles;
+    }
+
+    public List<String> getClaimTokenProfiles() {
+        return this.claimTokenProfiles;
+    }
+
+    void setClaimTokenProfiles(final List<String> claimTokenProfiles) {
+        this.claimTokenProfiles = claimTokenProfiles;
+    }
+
+    public URI getDynamicClientEndpoint() {
+        return this.dynamicClientEndpoint;
+    }
+
+    void setDynamicClientEndpoint(final URI dynamicClientEndpoint) {
+        this.dynamicClientEndpoint = dynamicClientEndpoint;
+    }
+
+    public URI getTokenEndpoint() {
+        return this.tokenEndpoint;
+    }
+
+    void setTokenEndpoint(final URI tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    public URI getAuthorizationEndpoint() {
+        return this.authorizationEndpoint;
+    }
+
+    void setAuthorizationEndpoint(final URI authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    public URI getRequestingPartyClaimsEndpoint() {
+        return this.requestingPartyClaimsEndpoint;
+    }
+
+    void setRequestingPartyClaimsEndpoint(final URI requestingPartyClaimsEndpoint) {
+        this.requestingPartyClaimsEndpoint = requestingPartyClaimsEndpoint;
+    }
+
+    public URI getResourceSetRegistrationEndpoint() {
+        return this.resourceSetRegistrationEndpoint;
+    }
+
+    void setResourceSetRegistrationEndpoint(final URI resourceSetRegistrationEndpoint) {
+        this.resourceSetRegistrationEndpoint = resourceSetRegistrationEndpoint;
+    }
+
+    public URI getIntrospectionEndpoint() {
+        return this.introspectionEndpoint;
+    }
+
+    void setIntrospectionEndpoint(final URI introspectionEndpoint) {
+        this.introspectionEndpoint = introspectionEndpoint;
+    }
+
+    public URI getPermissionRegistrationEndpoint() {
+        return this.permissionRegistrationEndpoint;
+    }
+
+    void setPermissionRegistrationEndpoint(final URI permissionRegistrationEndpoint) {
+        this.permissionRegistrationEndpoint = permissionRegistrationEndpoint;
+    }
+
+    public URI getRptEndpoint() {
+        return this.rptEndpoint;
+    }
+
+    void setRptEndpoint(final URI rptEndpoint) {
+        this.rptEndpoint = rptEndpoint;
+    }
+
+    public String getRealm() {
+        return this.realm;
+    }
+
+    public void setRealm(final String realm) {
+        this.realm = realm;
+    }
+
+    public URI getServerUrl() {
+        return this.serverUrl;
+    }
+
+    public void setServerUrl(URI serverUrl) {
+        this.serverUrl = serverUrl;
+    }
+
+    public void setRealmPublicKey(String realmPublicKey) {
+        this.realmPublicKey = realmPublicKey;
+    }
+
+    public String getRealmPublicKey() {
+        return realmPublicKey;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java
new file mode 100644
index 0000000..07e5908
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java
@@ -0,0 +1,56 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.config;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.wellknown.WellKnownProvider;
+
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaWellKnownProvider implements WellKnownProvider {
+
+    private final KeycloakSession session;
+
+    public UmaWellKnownProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @Override
+    public Object getConfig() {
+        RealmModel realm = this.session.getContext().getRealm();
+        UriInfo uriInfo = this.session.getContext().getUri();
+
+        return Configuration.fromDefault(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(), realm.getName(),
+                URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "auth").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
+                URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "token").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
+                realm.getPublicKeyPem());
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java
new file mode 100644
index 0000000..7776720
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.config;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.wellknown.WellKnownProvider;
+import org.keycloak.wellknown.WellKnownProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaWellKnownProviderFactory implements WellKnownProviderFactory {
+    @Override
+    public WellKnownProvider create(KeycloakSession session) {
+        return new UmaWellKnownProvider(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "uma-configuration";
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
new file mode 100644
index 0000000..5df5a0b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.keycloak.Config;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
+
+    private Executor scheduler;
+
+    @Override
+    public AuthorizationProvider create(KeycloakSession session) {
+        StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class);
+
+        if (storeFactory == null) {
+            storeFactory = session.getProvider(StoreFactory.class);
+        }
+
+        return new AuthorizationProvider(session, storeFactory);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+        //TODO: user-defined configuration
+//        Executor executor = Executors.newWorkStealingPool();
+//        this.scheduler = command -> {
+//            Map<Class<?>, Object> contextDataMap = ResteasyProviderFactory.getContextDataMap();
+//            executor.execute(() -> {
+//                ResteasyProviderFactory.pushContextDataMap(contextDataMap);
+//                command.run();
+//            });
+//        };
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "authorization";
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
new file mode 100644
index 0000000..df6f54d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
@@ -0,0 +1,264 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.entitlement;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.OAuthErrorException;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.common.KeycloakEvaluationContext;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.entitlement.representation.EntitlementRequest;
+import org.keycloak.authorization.entitlement.representation.EntitlementResponse;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.authorization.util.Permissions;
+import org.keycloak.authorization.util.Tokens;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.authorization.Permission;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.Cors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementService {
+
+    private final AuthorizationProvider authorization;
+
+    @Context
+    private HttpRequest request;
+
+    public EntitlementService(AuthorizationProvider authorization) {
+        this.authorization = authorization;
+    }
+
+    @Path("{resource_server_id}")
+    @OPTIONS
+    public Response authorizePreFlight(@PathParam("resource_server_id") String resourceServerId) {
+        return Cors.add(this.request, Response.ok()).auth().preflight().build();
+    }
+
+    @Path("{resource_server_id}")
+    @GET()
+    @Produces("application/json")
+    @Consumes("application/json")
+    public void getAll(@PathParam("resource_server_id") String resourceServerId, @Suspended AsyncResponse asyncResponse) {
+        KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
+
+        if (resourceServerId == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Requires resource_server_id request parameter.", Status.BAD_REQUEST);
+        }
+
+        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
+        ClientModel client = realm.getClientByClientId(resourceServerId);
+
+        if (client == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Identifier is not associated with any client and resource server.", Status.BAD_REQUEST);
+        }
+
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(client.getId());
+
+        authorization.evaluators().from(Permissions.all(resourceServer, identity, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
+
+            @Override
+            public void onError(Throwable cause) {
+                asyncResponse.resume(cause);
+            }
+
+            @Override
+            protected void onComplete(List<Result> results) {
+                List<Permission> entitlements = Permissions.allPermits(results);
+
+                if (entitlements.isEmpty()) {
+                    asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
+                            .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
+                            .allowedOrigins(identity.getAccessToken())
+                            .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+                } else {
+                    asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+                }
+            }
+        });
+    }
+
+    @Path("{resource_server_id}")
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public void get(@PathParam("resource_server_id") String resourceServerId, EntitlementRequest entitlementRequest, @Suspended AsyncResponse asyncResponse) {
+        KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
+
+        if (entitlementRequest == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid entitlement request.", Status.BAD_REQUEST);
+        }
+
+        if (resourceServerId == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid resource_server_id.", Status.BAD_REQUEST);
+        }
+
+        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
+
+        ClientModel client = realm.getClientByClientId(resourceServerId);
+
+        if (client == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Identifier is not associated with any resource server.", Status.BAD_REQUEST);
+        }
+
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(client.getId());
+
+        authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
+
+            @Override
+            public void onError(Throwable cause) {
+                asyncResponse.resume(cause);
+            }
+
+            @Override
+            protected void onComplete(List<Result> results) {
+                List<Permission> entitlements = Permissions.allPermits(results);
+
+                if (entitlements.isEmpty()) {
+                    asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN));
+                } else {
+                    asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+                }
+            }
+        });
+    }
+
+    private String createRequestingPartyToken(List<Permission> permissions) {
+        AccessToken accessToken = Tokens.getAccessToken(this.authorization.getKeycloakSession());
+        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
+        AccessToken.Authorization authorization = new AccessToken.Authorization();
+
+        authorization.setPermissions(permissions);
+
+        accessToken.setAuthorization(authorization);
+        ;
+        return new TokenManager().encodeToken(realm, accessToken);
+    }
+
+    private List<ResourcePermission> createPermissions(EntitlementRequest entitlementRequest, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Map<String, Set<String>> permissionsToEvaluate = new HashMap<>();
+
+        entitlementRequest.getPermissions().forEach(requestedResource -> {
+            Resource resource;
+
+            if (requestedResource.getResourceSetId() != null) {
+                resource = storeFactory.getResourceStore().findById(requestedResource.getResourceSetId());
+            } else {
+                resource = storeFactory.getResourceStore().findByName(requestedResource.getResourceSetName(), resourceServer.getId());
+            }
+
+            if (resource == null) {
+                throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getResourceSetId() + "] or name [" + requestedResource.getResourceSetName() + "] does not exist.", Status.FORBIDDEN);
+            }
+
+            permissionsToEvaluate.put(resource.getId(), requestedResource.getScopes());
+        });
+
+        String rpt = entitlementRequest.getRpt();
+
+        if (rpt != null && !"".equals(rpt)) {
+            KeycloakContext context = authorization.getKeycloakSession().getContext();
+
+            if (!Tokens.verifySignature(rpt, context.getRealm().getPublicKey())) {
+                throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
+            }
+
+            AccessToken requestingPartyToken;
+
+            try {
+                requestingPartyToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
+            } catch (JWSInputException e) {
+                throw new ErrorResponseException("invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
+            }
+
+            if (requestingPartyToken.isActive()) {
+                AccessToken.Authorization authorizationData = requestingPartyToken.getAuthorization();
+
+                if (authorizationData != null) {
+                    authorizationData.getPermissions().forEach(permission -> {
+                        Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
+
+                        if (resourcePermission != null) {
+                            Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
+
+                            if (scopes == null) {
+                                scopes = new HashSet<>();
+                                permissionsToEvaluate.put(resourcePermission.getId(), scopes);
+                            }
+
+                            scopes.addAll(permission.getScopes());
+                        }
+                    });
+                }
+            }
+        }
+
+        return permissionsToEvaluate.entrySet().stream()
+                .flatMap((Function<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
+                    Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
+
+                    if (entry.getValue().isEmpty()) {
+                        return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream();
+                    } else {
+                        return entry.getValue().stream()
+                                .map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId()))
+                                .filter(scope -> scope != null)
+                                .map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer()));
+                    }
+                }).collect(Collectors.toList());
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementRequest.java b/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementRequest.java
new file mode 100644
index 0000000..3afcc31
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementRequest.java
@@ -0,0 +1,25 @@
+package org.keycloak.authorization.entitlement.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementRequest {
+
+    private String rpt;
+
+    private List<PermissionRequest> permissions = new ArrayList<>();
+
+    public List<PermissionRequest> getPermissions() {
+        return permissions;
+    }
+
+    public String getRpt() {
+        return rpt;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementResponse.java b/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementResponse.java
new file mode 100644
index 0000000..8e883cd
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementResponse.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.entitlement.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EntitlementResponse {
+
+    private String rpt;
+
+    public EntitlementResponse(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public EntitlementResponse() {
+        this(null);
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+
+    public void setRpt(final String rpt) {
+        this.rpt = rpt;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/ErrorCode.java b/services/src/main/java/org/keycloak/authorization/ErrorCode.java
new file mode 100644
index 0000000..63ac38a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/ErrorCode.java
@@ -0,0 +1,28 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ErrorCode {
+
+    String INVALID_CLIENT_ID = "invalid_client_id";
+
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
new file mode 100644
index 0000000..a31e834
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.protection.introspect;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.oidc.AccessTokenIntrospectionProvider;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessToken.Authorization;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Introspects token accordingly with UMA Bearer Token Profile.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
+
+    protected static final Logger LOGGER = Logger.getLogger(RPTIntrospectionProvider.class);
+
+    public RPTIntrospectionProvider(KeycloakSession session) {
+        super(session);
+    }
+
+    @Override
+    public Response introspect(String token) {
+        LOGGER.debug("Introspecting requesting party token");
+        try {
+            AccessToken requestingPartyToken = toAccessToken(token);
+            boolean active = isActive(requestingPartyToken);
+            ObjectNode tokenMetadata;
+
+            if (active) {
+                LOGGER.debug("Token is active");
+                AccessToken introspect = new AccessToken();
+                introspect.type(requestingPartyToken.getType());
+                introspect.expiration(requestingPartyToken.getExpiration());
+                introspect.issuedAt(requestingPartyToken.getIssuedAt());
+                introspect.audience(requestingPartyToken.getAudience());
+                introspect.notBefore(requestingPartyToken.getNotBefore());
+                introspect.setRealmAccess(null);
+                introspect.setResourceAccess(null);
+                tokenMetadata = JsonSerialization.createObjectNode(introspect);
+                tokenMetadata.putPOJO("permissions", requestingPartyToken.getAuthorization().getPermissions());
+            } else {
+                LOGGER.debug("Token is not active");
+                tokenMetadata = JsonSerialization.createObjectNode();
+            }
+
+            tokenMetadata.put("active", active);
+
+            return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
+        } catch (Exception e) {
+            throw new RuntimeException("Error creating token introspection response.", e);
+        }
+    }
+
+    private boolean isActive(AccessToken requestingPartyToken) {
+        Authorization authorization = requestingPartyToken.getAuthorization();
+        return requestingPartyToken.isActive() && authorization != null && authorization.getPermissions() != null && !authorization.getPermissions().isEmpty();
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProviderFactory.java b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProviderFactory.java
new file mode 100644
index 0000000..1c4d894
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProviderFactory.java
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.authorization.protection.introspect;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.protocol.oidc.TokenIntrospectionProvider;
+import org.keycloak.protocol.oidc.TokenIntrospectionProviderFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RPTIntrospectionProviderFactory implements TokenIntrospectionProviderFactory {
+    @Override
+    public TokenIntrospectionProvider create(KeycloakSession session) {
+        return new RPTIntrospectionProvider(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "requesting_party_token";
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
new file mode 100644
index 0000000..cf2f9e0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
@@ -0,0 +1,102 @@
+package org.keycloak.authorization.protection.permission;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+import org.keycloak.authorization.protection.permission.representation.PermissionResponse;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.services.ErrorResponseException;
+
+import javax.ws.rs.core.Response;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AbstractPermissionService {
+
+    private final AuthorizationProvider authorization;
+    private final KeycloakIdentity identity;
+    private final ResourceServer resourceServer;
+
+    public AbstractPermissionService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        this.identity = identity;
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+    }
+
+    public Response create(List<PermissionRequest> request) {
+        if (request == null) {
+            throw new ErrorResponseException("invalid_permission_request", "Invalid permission request.", Response.Status.BAD_REQUEST);
+        }
+
+        List<ResourceRepresentation> resource = verifyRequestedResource(request);
+
+        return Response.status(Response.Status.CREATED).entity(new PermissionResponse(createPermissionTicket(resource))).build();
+    }
+
+    private List<ResourceRepresentation> verifyRequestedResource(List<PermissionRequest> request) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        return request.stream().map(request1 -> {
+            String resourceSetId = request1.getResourceSetId();
+            String resourceSetName = request1.getResourceSetName();
+
+            if (resourceSetId == null && resourceSetName == null) {
+                throw new ErrorResponseException("invalid_resource_set_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
+            }
+
+            Resource resource;
+
+            if (resourceSetId != null) {
+                resource = storeFactory.getResourceStore().findById(resourceSetId);
+            } else {
+                resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
+            }
+
+            if (resource == null) {
+                if (resourceSetId != null) {
+                    throw new ErrorResponseException("nonexistent_resource_set_id", "Resource set with id[" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
+                } else {
+                    throw new ErrorResponseException("nonexistent_resource_set_name", "Resource set with name[" + resourceSetName + "] does not exists in this server.", Response.Status.BAD_REQUEST);
+                }
+            }
+
+            return new ResourceRepresentation(resource.getName(), verifyRequestedScopes(request1, resource));
+        }).collect(Collectors.toList());
+    }
+
+    private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) {
+        return request.getScopes().stream().map(scopeName -> {
+            for (Scope scope : resource.getScopes()) {
+                if (scope.getName().equals(scopeName)) {
+                    return new ScopeRepresentation(scopeName);
+                }
+            }
+
+            for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType())) {
+                if (baseResource.getOwner().equals(resource.getResourceServer().getClientId())) {
+                    for (Scope baseScope : baseResource.getScopes()) {
+                        if (baseScope.getName().equals(scopeName)) {
+                            return new ScopeRepresentation(scopeName);
+                        }
+                    }
+                }
+            }
+
+            throw new ErrorResponseException("invalid_scope", "Scope [" + scopeName + " is not valid.", Response.Status.BAD_REQUEST);
+        }).collect(Collectors.toSet());
+    }
+
+    private String createPermissionTicket(List<ResourceRepresentation> resources) {
+        return new JWSBuilder().jsonContent(new PermissionTicket(resources, this.resourceServer.getId(), this.identity.getAccessToken()))
+                .rsa256(this.authorization.getKeycloakSession().getContext().getRealm().getPrivateKey());
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java
new file mode 100644
index 0000000..9d54730
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java
@@ -0,0 +1,59 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+import org.keycloak.authorization.protection.permission.representation.PermissionResponse;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.services.ErrorResponseException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionService extends AbstractPermissionService {
+
+    public PermissionService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        super(identity, resourceServer, authorization);
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(PermissionRequest request) {
+        return create(Arrays.asList(request));
+    }
+
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java
new file mode 100644
index 0000000..eea2108
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionsService extends AbstractPermissionService {
+
+    public PermissionsService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        super(identity, resourceServer, authorization);
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(List<PermissionRequest> request) {
+        return super.create(request);
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicket.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicket.java
new file mode 100644
index 0000000..9ee6368
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicket.java
@@ -0,0 +1,58 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission;
+
+import org.keycloak.TokenIdGenerator;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.JsonWebToken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionTicket extends JsonWebToken {
+
+    private final List<ResourceRepresentation> resources = new ArrayList<>();
+    private final String resourceServerId;
+
+    public PermissionTicket() {
+        this.resourceServerId = null;
+    }
+
+    public PermissionTicket(List<ResourceRepresentation> resources, String resourceServerId, AccessToken accessToken) {
+        id(TokenIdGenerator.generateId());
+        subject(accessToken.getSubject());
+        expiration(accessToken.getExpiration());
+        notBefore(accessToken.getNotBefore());
+        issuedAt(accessToken.getIssuedAt());
+        issuedFor(accessToken.getIssuedFor());
+        this.resources.addAll(resources);
+        this.resourceServerId = resourceServerId;
+    }
+
+    public List<ResourceRepresentation> getResources() {
+        return this.resources;
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionRequest.java b/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionRequest.java
new file mode 100644
index 0000000..31d6d55
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionRequest.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionRequest {
+
+    @JsonProperty("resource_set_id")
+    private final String resourceSetId;
+
+    @JsonProperty("resource_set_name")
+    private final String resourceSetName;
+
+    private final Set<String> scopes;
+
+    public PermissionRequest(String resourceSetId, String... scopes) {
+        this.resourceSetId = resourceSetId;
+
+        if (scopes != null) {
+            this.scopes = new HashSet(Arrays.asList(scopes));
+        } else {
+            this.scopes = new HashSet<>();
+        }
+
+        this.resourceSetName = null;
+    }
+
+    public PermissionRequest() {
+        this(null, null);
+    }
+
+    public String getResourceSetId() {
+        return this.resourceSetId;
+    }
+
+    public String getResourceSetName() {
+        return resourceSetName;
+    }
+
+    public Set<String> getScopes() {
+        return this.scopes;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionResponse.java b/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionResponse.java
new file mode 100644
index 0000000..24efefa
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/representation/PermissionResponse.java
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.permission.representation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionResponse {
+
+    private final String ticket;
+
+    public PermissionResponse(String ticket) {
+        this.ticket = ticket;
+    }
+
+    public PermissionResponse() {
+        this(null);
+    }
+
+    public String getTicket() {
+        return this.ticket;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
new file mode 100644
index 0000000..f3695d0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
@@ -0,0 +1,117 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.OAuthErrorException;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.ResourceSetService;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.protection.permission.PermissionService;
+import org.keycloak.authorization.protection.permission.PermissionsService;
+import org.keycloak.authorization.protection.resource.ResourceService;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.ErrorResponseException;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ProtectionService {
+
+    private final AuthorizationProvider authorization;
+
+    public ProtectionService(AuthorizationProvider authorization) {
+        this.authorization = authorization;
+    }
+
+    @Path("/resource_set")
+    public Object resource() {
+        KeycloakIdentity identity = createIdentity();
+
+        if (!identity.hasRole("uma_protection")) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
+        }
+
+        ResourceSetService resourceManager = new ResourceSetService(getResourceServer(identity), this.authorization, null);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
+
+        ResourceService resource = new ResourceService(getResourceServer(identity), identity, resourceManager, this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    @Path("/permission")
+    public Object permission() {
+        KeycloakIdentity identity = createIdentity();
+
+        if (!identity.hasRole("uma_protection")) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
+        }
+
+        PermissionService resource = new PermissionService(identity, getResourceServer(identity), this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    @Path("/permissions")
+    public Object permissions() {
+        KeycloakIdentity identity = createIdentity();
+
+        if (!identity.hasRole("uma_protection")) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
+        }
+
+        PermissionsService resource = new PermissionsService(identity, getResourceServer(identity), this.authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
+
+    private KeycloakIdentity createIdentity() {
+        return new KeycloakIdentity(this.authorization.getKeycloakSession());
+    }
+
+    private ResourceServer getResourceServer(Identity identity) {
+        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
+        ClientModel clientApplication = realm.getClientById(identity.getId());
+
+        if (clientApplication == null) {
+            throw new ErrorResponseException("invalid_clientId", "Client application with id [" + identity.getId() + "] does not exist in realm [" + realm.getName() + "]", Status.BAD_REQUEST);
+        }
+
+        ResourceServer resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(identity.getId());
+
+        if (resourceServer == null) {
+            throw new ErrorResponseException("invalid_clientId", "Client application [" + clientApplication.getClientId() + "] is not registered as resource server.", Status.FORBIDDEN);
+        }
+
+        return resourceServer;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/RegistrationResponse.java b/services/src/main/java/org/keycloak/authorization/protection/resource/RegistrationResponse.java
new file mode 100644
index 0000000..4f82242
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/RegistrationResponse.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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 com.fasterxml.jackson.annotation.JsonUnwrapped;
+import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RegistrationResponse {
+
+    private final UmaResourceRepresentation resourceDescription;
+
+    public RegistrationResponse(UmaResourceRepresentation resourceDescription) {
+        this.resourceDescription = resourceDescription;
+    }
+
+    public RegistrationResponse() {
+        this(null);
+    }
+
+    @JsonUnwrapped
+    public UmaResourceRepresentation getResourceDescription() {
+        return this.resourceDescription;
+    }
+
+    public String getId() {
+        if (this.resourceDescription != null) {
+            return this.resourceDescription.getId();
+        }
+
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/representation/RegistrationResponse.java b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/RegistrationResponse.java
new file mode 100644
index 0000000..6922d51
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/RegistrationResponse.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.representation;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RegistrationResponse {
+
+    private final UmaResourceRepresentation resourceDescription;
+
+    public RegistrationResponse(UmaResourceRepresentation resourceDescription) {
+        this.resourceDescription = resourceDescription;
+    }
+
+    public RegistrationResponse() {
+        this(null);
+    }
+
+    @JsonUnwrapped
+    public UmaResourceRepresentation getResourceDescription() {
+        return this.resourceDescription;
+    }
+
+    public String getId() {
+        if (this.resourceDescription != null) {
+            return this.resourceDescription.getId();
+        }
+
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java
new file mode 100644
index 0000000..0e9bafa
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.representation;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * <p>One or more resources that the resource server manages as a set of protected resources.
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.2">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaResourceRepresentation {
+
+    @JsonProperty("_id")
+    private String id;
+
+    private String name;
+    private String uri;
+    private String type;
+    private Set<UmaScopeRepresentation> scopes;
+
+    @JsonProperty("icon_uri")
+    private String iconUri;
+    private String owner;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     * @param iconUri a {@link URI} for a graphic icon representing the resource set
+     */
+    public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes, String uri, String type, String iconUri) {
+        this.name = name;
+        this.scopes = scopes;
+        this.uri = uri;
+        this.type = type;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param uri a {@link URI} that provides the network location for the resource set being registered
+     * @param type a string uniquely identifying the semantics of the resource set
+     * @param scopes the available scopes for this resource set
+     */
+    public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes, String uri, String type) {
+        this(name, scopes, uri, type, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name a human-readable string describing a set of one or more resources
+     * @param serverUri a {@link URI} that identifies this resource server
+     * @param scopes the available scopes for this resource set
+     */
+    public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes) {
+        this(name, scopes, null, null, null);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     */
+    public UmaResourceRepresentation() {
+        this(null, null, null, null, null);
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getUri() {
+        return this.uri;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public Set<UmaScopeRepresentation> getScopes() {
+        return Collections.unmodifiableSet(this.scopes);
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setScopes(Set<UmaScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaScopeRepresentation.java b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaScopeRepresentation.java
new file mode 100644
index 0000000..4a184d9
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaScopeRepresentation.java
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.representation;
+
+import java.net.URI;
+import java.util.Objects;
+
+/**
+ * <p>A bounded extent of access that is possible to perform on a resource set. In authorization policy terminology,
+ * a scope is one of the potentially many "verbs" that can logically apply to a resource set ("object").
+ *
+ * <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.1">OAuth-resource-reg</a>.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaScopeRepresentation {
+
+    private String id;
+    private String name;
+    private String iconUri;
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     * @param iconUri a {@link URI} for a graphic icon representing the scope
+     */
+    public UmaScopeRepresentation(String name, String iconUri) {
+        this.name = name;
+        this.iconUri = iconUri;
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param name the a human-readable string describing some scope (extent) of access
+     */
+    public UmaScopeRepresentation(String name) {
+        this(name, null);
+    }
+
+    /**
+     * Creates an instance.
+     */
+    public UmaScopeRepresentation() {
+        this(null, null);
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getIconUri() {
+        return this.iconUri;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        UmaScopeRepresentation scope = (UmaScopeRepresentation) o;
+        return Objects.equals(getName(), scope.getName());
+    }
+
+    public int hashCode() {
+        return Objects.hash(getName());
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setIconUri(String iconUri) {
+        this.iconUri = iconUri;
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..f4aaac5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -0,0 +1,228 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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 org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.ResourceSetService;
+import org.keycloak.authorization.admin.representation.ResourceOwnerRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.admin.util.Models;
+import org.keycloak.authorization.identity.Identity;
+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.authorization.store.StoreFactory;
+import org.keycloak.services.ErrorResponseException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+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) {
+        this.identity = identity;
+        this.resourceServer = resourceServer;
+        this.resourceManager = resourceManager;
+        this.authorization = authorization;
+    }
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(UmaResourceRepresentation umaResource) {
+        checkResourceServerSettings();
+        ResourceRepresentation resource = toResourceRepresentation(umaResource);
+        Response response = this.resourceManager.create(resource);
+
+        if (response.getEntity() instanceof ResourceRepresentation) {
+            return Response.status(Status.CREATED).entity(toUmaRepresentation((ResourceRepresentation) response.getEntity())).build();
+        }
+
+        return response;
+    }
+
+    @Path("/{id}")
+    @DELETE
+    public Response delete(@PathParam("id") String id) {
+        checkResourceServerSettings();
+        return this.resourceManager.delete(id);
+    }
+
+    @Path("/{id}")
+    @GET
+    @Produces("application/json")
+    public RegistrationResponse findById(@PathParam("id") String id) {
+        Response response = this.resourceManager.findById(id);
+        UmaResourceRepresentation resource = toUmaRepresentation((ResourceRepresentation) response.getEntity());
+
+        if (resource == null) {
+            throw new ErrorResponseException("not_found", "Resource with id [" + id + "] not found.", Status.NOT_FOUND);
+        }
+
+        return new RegistrationResponse(resource);
+    }
+
+    @GET
+    @Produces("application/json")
+    public Set<String> find(@QueryParam("filter") String filter) {
+        if (filter == null) {
+            return findAll();
+        } else {
+            return findByFilter(filter);
+        }
+    }
+
+    private Set<String> findAll() {
+        Response response = this.resourceManager.findAll();
+        List<ResourceRepresentation> resources = (List<ResourceRepresentation>) response.getEntity();
+        return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet());
+    }
+
+    private Set<String> findByFilter(String filter) {
+        Set<ResourceRepresentation> resources = new HashSet<>();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+
+        if (filter != null) {
+            for (String currentFilter : filter.split("&")) {
+                String[] parts = currentFilter.split("=");
+                String filterType = parts[0];
+                final String filterValue;
+
+                if (parts.length > 1) {
+                    filterValue = parts[1];
+                } else {
+                    filterValue = null;
+                }
+
+
+                if ("name".equals(filterType)) {
+                    resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getName())).collect(Collectors.toSet()).stream()
+                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .collect(Collectors.toList()));
+                } else if ("type".equals(filterType)) {
+                    resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getType())).collect(Collectors.toSet()).stream()
+                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .collect(Collectors.toList()));
+                } else if ("uri".equals(filterType)) {
+                    resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getUri())).collect(Collectors.toSet()).stream()
+                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .collect(Collectors.toList()));
+                } else if ("owner".equals(filterType)) {
+                    resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getOwner())).collect(Collectors.toSet()).stream()
+                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .collect(Collectors.toList()));
+                }
+            }
+        } else {
+            resources = storeFactory.getResourceStore().findByOwner(identity.getId()).stream()
+                    .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                    .collect(Collectors.toSet());
+        }
+
+        return resources.stream()
+                .map(ResourceRepresentation::getId)
+                .collect(Collectors.toSet());
+    }
+
+    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());
+
+        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()));
+
+        return resource;
+    }
+
+    private UmaResourceRepresentation toUmaRepresentation(ResourceRepresentation representation) {
+        if (representation == null) {
+            return null;
+        }
+
+        UmaResourceRepresentation resource = new UmaResourceRepresentation();
+
+        resource.setId(representation.getId());
+        resource.setIconUri(representation.getIconUri());
+        resource.setName(representation.getName());
+        resource.setUri(representation.getUri());
+        resource.setType(representation.getType());
+
+        if (representation.getOwner() != null) {
+            resource.setOwner(representation.getOwner().getId());
+        }
+
+        resource.setScopes(representation.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()));
+
+        return resource;
+    }
+
+    private void checkResourceServerSettings() {
+        if (!this.resourceServer.isAllowRemoteResourceManagement()) {
+            throw new ErrorResponseException("not_supported", "Remote management is disabled.", Status.BAD_REQUEST);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
new file mode 100644
index 0000000..43204b8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -0,0 +1,140 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.util;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.Decision.Effect;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.representations.authorization.Permission;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public final class Permissions {
+
+    /**
+     * Returns a list of permissions for all resources and scopes that belong to the given <code>resourceServer</code> and
+     * <code>identity</code>.
+     *
+     * TODO: review once we support caches
+     *
+     * @param resourceServer
+     * @param identity
+     * @param authorization
+     * @return
+     */
+    public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) {
+        List<ResourcePermission> permissions = new ArrayList<>();
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceStore resourceStore = storeFactory.getResourceStore();
+
+        resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
+        resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
+
+        return permissions;
+    }
+
+    public static List<ResourcePermission> createResourcePermissions(Resource resource) {
+        List<ResourcePermission> permissions = new ArrayList<>();
+        List<Scope> scopes = resource.getScopes();
+
+        permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
+
+        for (Scope scope : scopes) {
+            permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
+        }
+
+        return permissions;
+    }
+
+    public static List<Permission> allPermits(List<Result> evaluation) {
+        List<Permission> permissions = evaluation.stream()
+                .filter(evaluationResult -> evaluationResult.getEffect().equals(Effect.PERMIT))
+                .map(evaluationResult -> {
+                    ResourcePermission permission = evaluationResult.getPermission();
+                    String resourceId = null;
+                    String resourceName = null;
+
+                    Resource resource = permission.getResource();
+
+                    if (resource != null) {
+                        resourceId = resource.getId();
+                        resourceName = resource.getName();
+                    }
+
+                    Set<String> scopes = null;
+
+                    if (!permission.getScopes().isEmpty()) {
+                        scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
+                    }
+
+                    return new Permission(resourceId, resourceName, scopes);
+                }).collect(Collectors.toList());
+
+        Map<String, Permission> perms = new HashMap<>();
+
+        permissions.forEach(permission -> {
+            Permission evalPermission = perms.get(permission.getResourceSetId());
+
+            if (evalPermission == null) {
+                evalPermission = permission;
+                if (evalPermission.getScopes() != null && evalPermission.getScopes().isEmpty()) {
+                    evalPermission.setScopes(null);
+                }
+                perms.put(permission.getResourceSetId(), evalPermission);
+            }
+
+            Set<String> permissionScopes = permission.getScopes();
+
+            if (permissionScopes != null && !permissionScopes.isEmpty()) {
+                Set<String> scopes = evalPermission.getScopes();
+
+                if (scopes == null) {
+                    scopes = new HashSet();
+                    evalPermission.setScopes(scopes);
+                }
+
+                for (String scopeName : permissionScopes) {
+                    if (!scopes.contains(scopeName)) {
+                        scopes.add(scopeName);
+                    }
+                }
+            }
+        });
+
+        return perms.values().stream().collect(Collectors.toList());
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/util/Tokens.java b/services/src/main/java/org/keycloak/authorization/util/Tokens.java
new file mode 100644
index 0000000..0deeef5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/util/Tokens.java
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.util;
+
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager.AuthResult;
+
+import javax.ws.rs.core.Response.Status;
+import java.security.PublicKey;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Tokens {
+
+    public static AccessToken getAccessToken(KeycloakSession keycloakSession) {
+        AppAuthManager authManager = new AppAuthManager();
+        KeycloakContext context = keycloakSession.getContext();
+        AuthResult authResult = authManager.authenticateBearerToken(keycloakSession, context.getRealm(), context.getUri(), context.getConnection(), context.getRequestHeaders());
+
+        if (authResult != null) {
+            return authResult.getToken();
+        }
+
+        return null;
+    }
+
+    public static String getAccessTokenAsString(KeycloakSession keycloakSession) {
+        AppAuthManager authManager = new AppAuthManager();
+
+        return authManager.extractAuthorizationHeaderToken(keycloakSession.getContext().getRequestHeaders());
+    }
+
+    public static boolean verifySignature(String token, PublicKey publicKey) {
+        try {
+            JWSInput jws = new JWSInput(token);
+
+            return RSAProvider.verify(jws, publicKey);
+        } catch (Exception e) {
+            throw new ErrorResponseException("invalid_signature", "Unexpected error while validating signature.", Status.INTERNAL_SERVER_ERROR);
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
index a2469ec..c19d1e8 100644
--- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
+++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
@@ -24,7 +24,6 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.services.ServicesLogger;
 
-import javax.mail.Message;
 import javax.mail.MessagingException;
 import javax.mail.Multipart;
 import javax.mail.Session;
@@ -108,10 +107,10 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider {
                 multipart.addBodyPart(htmlPart);
             }
 
-            Message msg = new MimeMessage(session);
+            MimeMessage msg = new MimeMessage(session);
             msg.setFrom(new InternetAddress(from));
             msg.setHeader("To", address);
-            msg.setSubject(subject);
+            msg.setSubject(subject, "utf-8");
             msg.setContent(multipart);
             msg.saveChanges();
             msg.setSentDate(new Date());
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java
new file mode 100644
index 0000000..0378765
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.common.VerificationException;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvider {
+
+    private final KeycloakSession session;
+    private final TokenManager tokenManager;
+    private final RealmModel realm;
+
+    public AccessTokenIntrospectionProvider(KeycloakSession session) {
+        this.session = session;
+        this.realm = session.getContext().getRealm();
+        this.tokenManager = new TokenManager();
+    }
+
+    public Response introspect(String token) {
+        try {
+            AccessToken toIntrospect = toAccessToken(token);
+            RealmModel realm = this.session.getContext().getRealm();
+            ObjectNode tokenMetadata;
+
+            boolean active = tokenManager.isTokenValid(session, realm, toIntrospect);
+
+            if (active) {
+                tokenMetadata = JsonSerialization.createObjectNode(toIntrospect);
+                tokenMetadata.put("client_id", toIntrospect.getIssuedFor());
+                tokenMetadata.put("username", toIntrospect.getPreferredUsername());
+            } else {
+                tokenMetadata = JsonSerialization.createObjectNode();
+            }
+
+            tokenMetadata.put("active", active);
+
+            return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
+        } catch (Exception e) {
+            throw new RuntimeException("Error creating token introspection response.", e);
+        }
+    }
+
+    protected AccessToken toAccessToken(String token) {
+        try {
+            return RSATokenVerifier.toAccessToken(token, realm.getPublicKey());
+        } catch (VerificationException e) {
+            throw new ErrorResponseException("invalid_request", "Invalid token.", Response.Status.UNAUTHORIZED);
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProviderFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProviderFactory.java
new file mode 100644
index 0000000..19c9f18
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProviderFactory.java
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AccessTokenIntrospectionProviderFactory implements TokenIntrospectionProviderFactory {
+
+    public static final String ACCESS_TOKEN_TYPE = "access_token";
+
+    @Override
+    public TokenIntrospectionProvider create(KeycloakSession session) {
+        return new AccessTokenIntrospectionProvider(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return ACCESS_TOKEN_TYPE;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index bf11650..f7d68dc 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -132,7 +132,7 @@ public class TokenEndpoint {
 
     @Path("introspect")
     public Object introspect() {
-        TokenIntrospectionEndpoint tokenIntrospectionEndpoint = new TokenIntrospectionEndpoint(this.realm, this.tokenManager, this.event);
+        TokenIntrospectionEndpoint tokenIntrospectionEndpoint = new TokenIntrospectionEndpoint(this.realm, this.event);
 
         ResteasyProviderFactory.getInstance().injectProperties(tokenIntrospectionEndpoint);
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
index 323a6ff..f6c59c8 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
@@ -16,26 +16,19 @@
  */
 package org.keycloak.protocol.oidc.endpoints;
 
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.OAuthErrorException;
-import org.keycloak.RSATokenVerifier;
 import org.keycloak.common.ClientConnection;
-import org.keycloak.common.VerificationException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
-import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.protocol.oidc.TokenManager;
-import org.keycloak.protocol.oidc.TokenManager.TokenValidation;
+import org.keycloak.protocol.oidc.AccessTokenIntrospectionProviderFactory;
+import org.keycloak.protocol.oidc.TokenIntrospectionProvider;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
-import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ErrorResponseException;
-import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.POST;
 import javax.ws.rs.core.Context;
@@ -52,8 +45,6 @@ import javax.ws.rs.core.UriInfo;
  */
 public class TokenIntrospectionEndpoint {
 
-    private static final String TOKEN_TYPE_ACCESS_TOKEN = "access_token";
-    private static final String TOKEN_TYPE_REFRESH_TOKEN = "refresh_token";
     private static final String PARAM_TOKEN_TYPE_HINT = "token_type_hint";
     private static final String PARAM_TOKEN = "token";
 
@@ -72,12 +63,10 @@ public class TokenIntrospectionEndpoint {
     private ClientConnection clientConnection;
 
     private final RealmModel realm;
-    private final TokenManager tokenManager;
     private final EventBuilder event;
 
-    public TokenIntrospectionEndpoint(RealmModel realm, TokenManager tokenManager, EventBuilder event) {
+    public TokenIntrospectionEndpoint(RealmModel realm, EventBuilder event) {
         this.realm = realm;
-        this.tokenManager = tokenManager;
         this.event = event;
     }
 
@@ -94,7 +83,7 @@ public class TokenIntrospectionEndpoint {
         String tokenTypeHint = formParams.getFirst(PARAM_TOKEN_TYPE_HINT);
 
         if (tokenTypeHint == null) {
-            tokenTypeHint = TOKEN_TYPE_ACCESS_TOKEN;
+            tokenTypeHint = AccessTokenIntrospectionProviderFactory.ACCESS_TOKEN_TYPE;
         }
 
         String token = formParams.getFirst(PARAM_TOKEN);
@@ -103,39 +92,26 @@ public class TokenIntrospectionEndpoint {
             throw throwErrorResponseException(Errors.INVALID_REQUEST, "Token not provided.", Status.BAD_REQUEST);
         }
 
+        TokenIntrospectionProvider provider = this.session.getProvider(TokenIntrospectionProvider.class, tokenTypeHint);
+
+        if (provider == null) {
+            throw throwErrorResponseException(Errors.INVALID_REQUEST, "Unsupported token type [" + tokenTypeHint + "].", Status.BAD_REQUEST);
+        }
+
         try {
-            AccessToken toIntrospect = toAccessToken(tokenTypeHint, token);
-            ObjectNode tokenMetadata;
-
-            boolean active = tokenManager.isTokenValid(session, realm, toIntrospect);
-            if (active) {
-                tokenMetadata = JsonSerialization.createObjectNode(toIntrospect);
-                tokenMetadata.put("client_id", toIntrospect.getIssuedFor());
-                tokenMetadata.put("username", toIntrospect.getPreferredUsername());
-            } else {
-                tokenMetadata = JsonSerialization.createObjectNode();
-            }
 
-            tokenMetadata.put("active", active);
+            Response response = provider.introspect(token);
 
             this.event.success();
 
-            return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).build();
+            return response;
+        } catch (ErrorResponseException ere) {
+            throw ere;
         } catch (Exception e) {
             throw throwErrorResponseException(Errors.INVALID_REQUEST, "Failed to introspect token.", Status.BAD_REQUEST);
         }
     }
 
-    private AccessToken toAccessToken(String tokenTypeHint, String token) throws JWSInputException, OAuthErrorException {
-        if (TOKEN_TYPE_ACCESS_TOKEN.equals(tokenTypeHint)) {
-            return toAccessToken(token);
-        } else if (TOKEN_TYPE_REFRESH_TOKEN.equals(tokenTypeHint)) {
-            return this.tokenManager.toRefreshToken(this.realm, token);
-        } else {
-            throw throwErrorResponseException(Errors.INVALID_REQUEST, "Unsupported token type [" + tokenTypeHint + "].", Status.BAD_REQUEST);
-        }
-    }
-
     private void authorizeClient() {
         try {
             ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
@@ -153,14 +129,6 @@ public class TokenIntrospectionEndpoint {
         }
     }
 
-    private AccessToken toAccessToken(String tokenString) {
-        try {
-            return RSATokenVerifier.toAccessToken(tokenString, realm.getPublicKey());
-        } catch (VerificationException e) {
-            throw new ErrorResponseException("invalid_request", "Invalid token.", Status.UNAUTHORIZED);
-        }
-    }
-
     private void checkSsl() {
         if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
             throw new ErrorResponseException("invalid_request", "HTTPS required", Status.FORBIDDEN);
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 f3f502d..a7781de 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
@@ -20,12 +20,16 @@ package org.keycloak.protocol.oidc.installation;
 import org.keycloak.Config;
 import org.keycloak.authentication.ClientAuthenticator;
 import org.keycloak.authentication.ClientAuthenticatorFactory;
+import org.keycloak.authorization.admin.AuthorizationService;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
 import org.keycloak.protocol.ClientInstallationProvider;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.services.managers.ClientManager;
 import org.keycloak.util.JsonSerialization;
 
@@ -34,6 +38,7 @@ import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.net.URI;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -59,6 +64,9 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
             Map<String, Object> adapterConfig = getClientCredentialsAdapterConfig(session, client);
             rep.setCredentials(adapterConfig);
         }
+
+        configureAuthorizationSettings(session, client, rep);
+
         String json = null;
         try {
             json = JsonSerialization.writeValueAsPrettyString(rep);
@@ -143,4 +151,22 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
         return MediaType.APPLICATION_JSON;
     }
 
+    private void configureAuthorizationSettings(KeycloakSession session, ClientModel client, ClientManager.InstallationAdapterConfig rep) {
+        if (new AuthorizationService(session, client, null).isEnabled()) {
+            PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
+
+            enforcerConfig.setEnforcementMode(null);
+            enforcerConfig.setPaths(null);
+
+            rep.setEnforcerConfig(enforcerConfig);
+
+            Set<RoleModel> clientRoles = client.getRoles();
+
+            if (clientRoles.size() == 1) {
+                if (clientRoles.iterator().next().getName().equals(Constants.AUTHZ_UMA_PROTECTION)) {
+                    rep.setUseResourceRoleMappings(null);
+                }
+            }
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProvider.java
new file mode 100644
index 0000000..23c675b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProvider.java
@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RefreshTokenIntrospectionProvider extends AccessTokenIntrospectionProvider {
+
+    public RefreshTokenIntrospectionProvider(KeycloakSession session) {
+        super(session);
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProviderFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProviderFactory.java
new file mode 100644
index 0000000..9f5ef76
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/RefreshTokenIntrospectionProviderFactory.java
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RefreshTokenIntrospectionProviderFactory extends AccessTokenIntrospectionProviderFactory {
+
+    private static final String REFRESH_TOKEN_TYPE = "refresh_token";
+
+    @Override
+    public TokenIntrospectionProvider create(KeycloakSession session) {
+        return new RefreshTokenIntrospectionProvider(session);
+    }
+
+    @Override
+    public String getId() {
+        return REFRESH_TOKEN_TYPE;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java b/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java
index 26aa19d..5f4b19d 100644
--- a/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java
+++ b/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java
@@ -33,6 +33,15 @@ public class DefaultProviderLoader implements ProviderLoader {
     }
 
     @Override
+    public List<Spi> loadSpis() {
+        LinkedList<Spi> list = new LinkedList<>();
+        for (Spi spi : ServiceLoader.load(Spi.class, classLoader)) {
+            list.add(spi);
+        }
+        return list;
+    }
+
+    @Override
     public List<ProviderFactory> load(Spi spi) {
         LinkedList<ProviderFactory> list = new LinkedList<ProviderFactory>();
         for (ProviderFactory f : ServiceLoader.load(spi.getProviderFactoryClass(), classLoader)) {
diff --git a/services/src/main/java/org/keycloak/provider/ProviderManager.java b/services/src/main/java/org/keycloak/provider/ProviderManager.java
index 997c68a..e906df9 100644
--- a/services/src/main/java/org/keycloak/provider/ProviderManager.java
+++ b/services/src/main/java/org/keycloak/provider/ProviderManager.java
@@ -65,6 +65,20 @@ public class ProviderManager {
         }
     }
 
+    public synchronized List<Spi> loadSpis() {
+        // Use a map to prevent duplicates, since the loaders may have overlapping classpaths.
+        Map<String, Spi> spiMap = new HashMap<>();
+        for (ProviderLoader loader : loaders) {
+            List<Spi> spis = loader.loadSpis();
+            if (spis != null) {
+                for (Spi spi : spis) {
+                    spiMap.put(spi.getName(), spi);
+                }
+            }
+        }
+        return new LinkedList<>(spiMap.values());
+    }
+
     public synchronized List<ProviderFactory> load(Spi spi) {
         List<ProviderFactory> factories = cache.get(spi.getName());
         if (factories == null) {
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index 90b495c..172de6e 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -70,8 +70,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
 
         ProviderManager pm = new ProviderManager(getClass().getClassLoader(), Config.scope().getArray("providers"));
 
-        ServiceLoader<Spi> load = ServiceLoader.load(Spi.class, getClass().getClassLoader());
-        loadSPIs(pm, load);
+        // Load the SPI classes through the provider manager, so both Keycloak internal SPI's and
+        // the ones defined in deployed modules will be found.
+        loadSPIs(pm, pm.loadSpis());
         for ( Map<String, ProviderFactory> factories : factoriesMap.values()) {
             for (ProviderFactory factory : factories.values()) {
                 factory.postInit(this);
@@ -79,8 +80,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
         }
     }
 
-    protected void loadSPIs(ProviderManager pm, ServiceLoader<Spi> load) {
-        for (Spi spi : load) {
+    protected void loadSPIs(ProviderManager pm, List<Spi> spiList) {
+        for (Spi spi : spiList) {
             spis.add(spi);
 
             Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
index 282fd31..40c1ac6 100755
--- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
+++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
@@ -23,6 +23,8 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.KeycloakTransaction;
 
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -87,15 +89,46 @@ public class KeycloakSessionServletFilter implements Filter {
         try {
             filterChain.doFilter(servletRequest, servletResponse);
         } finally {
-            // KeycloakTransactionCommitter is responsible for committing the transaction, but if an exception is thrown it's not invoked and transaction
-            // should be rolled back
-            if (session.getTransaction() != null && session.getTransaction().isActive()) {
-                session.getTransaction().rollback();
+            if (servletRequest.isAsyncStarted()) {
+                servletRequest.getAsyncContext().addListener(createAsyncLifeCycleListener(session));
+            } else {
+                closeSession(session);
+            }
+        }
+    }
+
+    private AsyncListener createAsyncLifeCycleListener(final KeycloakSession session) {
+        return new AsyncListener() {
+            @Override
+            public void onComplete(AsyncEvent event) {
+                closeSession(session);
             }
 
-            session.close();
-            ResteasyProviderFactory.clearContextData();
+            @Override
+            public void onTimeout(AsyncEvent event) {
+                closeSession(session);
+            }
+
+            @Override
+            public void onError(AsyncEvent event) {
+                closeSession(session);
+            }
+
+            @Override
+            public void onStartAsync(AsyncEvent event) {
+            }
+        };
+    }
+
+    private void closeSession(KeycloakSession session) {
+        // KeycloakTransactionCommitter is responsible for committing the transaction, but if an exception is thrown it's not invoked and transaction
+        // should be rolled back
+        if (session.getTransaction() != null && session.getTransaction().isActive()) {
+            session.getTransaction().rollback();
         }
+
+        session.close();
+        ResteasyProviderFactory.clearContextData();
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
index 7432e6f..7bdf5b1 100644
--- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
@@ -35,6 +35,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
 import org.keycloak.representations.adapters.config.BaseRealmConfig;
 import org.keycloak.common.util.Time;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.ServicesLogger;
 
@@ -207,6 +208,8 @@ public class ClientManager {
         protected Boolean publicClient;
         @JsonProperty("credentials")
         protected Map<String, Object> credentials;
+        @JsonProperty("policy-enforcer")
+        protected PolicyEnforcerConfig enforcerConfig;
 
         public Boolean isUseResourceRoleMappings() {
             return useResourceRoleMappings;
@@ -247,6 +250,14 @@ public class ClientManager {
         public void setBearerOnly(Boolean bearerOnly) {
             this.bearerOnly = bearerOnly;
         }
+
+        public PolicyEnforcerConfig getEnforcerConfig() {
+            return this.enforcerConfig;
+        }
+
+        public void setEnforcerConfig(PolicyEnforcerConfig enforcerConfig) {
+            this.enforcerConfig = enforcerConfig;
+        }
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
index 665dcd3..fa3b1d5 100644
--- a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
+++ b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
@@ -22,7 +22,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.services.ServicesLogger;
 
 import java.util.ArrayList;
@@ -55,18 +55,18 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
 
     protected abstract class LoginEvent implements Comparable<LoginEvent> {
         protected final String realmId;
-        protected final String username;
+        protected final String userId;
         protected final String ip;
 
-        protected LoginEvent(String realmId, String username, String ip) {
+        protected LoginEvent(String realmId, String userId, String ip) {
             this.realmId = realmId;
-            this.username = username;
+            this.userId = userId;
             this.ip = ip;
         }
 
         @Override
         public int compareTo(LoginEvent o) {
-            return username.compareTo(o.username);
+            return userId.compareTo(o.userId);
         }
     }
 
@@ -79,8 +79,8 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
     protected class FailedLogin extends LoginEvent {
         protected final CountDownLatch latch = new CountDownLatch(1);
 
-        public FailedLogin(String realmId, String username, String ip) {
-            super(realmId, username, ip);
+        public FailedLogin(String realmId, String userId, String ip) {
+            super(realmId, userId, ip);
         }
     }
 
@@ -92,11 +92,11 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
         logger.debug("failure");
         RealmModel realm = getRealmModel(session, event);
         logFailure(event);
-        UserModel user = session.users().getUserByUsername(event.username.toString(), realm);
-        UsernameLoginFailureModel userLoginFailure = getUserModel(session, event);
+        UserModel user = session.users().getUserById(event.userId, realm);
+        UserLoginFailureModel userLoginFailure = getUserModel(session, event);
         if (user != null) {
             if (userLoginFailure == null) {
-                userLoginFailure = session.sessions().addUserLoginFailure(realm, event.username.toLowerCase());
+                userLoginFailure = session.sessions().addUserLoginFailure(realm, event.userId);
             }
             userLoginFailure.setLastIPFailure(event.ip);
             long currentTime = System.currentTimeMillis();
@@ -135,10 +135,10 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
     }
 
 
-    protected UsernameLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
+    protected UserLoginFailureModel getUserModel(KeycloakSession session, LoginEvent event) {
         RealmModel realm = getRealmModel(session, event);
         if (realm == null) return null;
-        UsernameLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.username.toLowerCase());
+        UserLoginFailureModel user = session.sessions().getUserLoginFailure(realm, event.userId);
         if (user == null) return null;
         return user;
     }
@@ -212,7 +212,7 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
     }
 
     protected void logFailure(LoginEvent event) {
-        logger.loginFailure(event.username, event.ip);
+        logger.loginFailure(event.userId, event.ip);
         failures++;
         long delta = 0;
         if (lastFailure > 0) {
@@ -227,9 +227,9 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
     }
 
     @Override
-    public void failedLogin(RealmModel realm, String username, ClientConnection clientConnection) {
+    public void failedLogin(RealmModel realm, UserModel user, ClientConnection clientConnection) {
         try {
-            FailedLogin event = new FailedLogin(realm.getId(), username, clientConnection.getRemoteAddr());
+            FailedLogin event = new FailedLogin(realm.getId(), user.getId(), clientConnection.getRemoteAddr());
             queue.offer(event);
             // wait a minimum of seconds for type to process so that a hacker
             // cannot flood with failed logins and overwhelm the queue and not have notBefore updated to block next requests
@@ -241,17 +241,17 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
     }
 
     @Override
-    public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, String username) {
-        UsernameLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
-        if (failure == null) {
-            return false;
+    public boolean isTemporarilyDisabled(KeycloakSession session, RealmModel realm, UserModel user) {
+        UserLoginFailureModel failure = session.sessions().getUserLoginFailure(realm, user.getId());
+
+        if (failure != null) {
+            int currTime = (int) (System.currentTimeMillis() / 1000);
+            if (currTime < failure.getFailedLoginNotBefore()) {
+                logger.debugv("Current: {0} notBefore: {1}", currTime, failure.getFailedLoginNotBefore());
+                return true;
+            }
         }
 
-        int currTime = (int)(System.currentTimeMillis()/1000);
-        if (currTime < failure.getFailedLoginNotBefore()) {
-            logger.debugv("Current: {0} notBefore: {1}", currTime , failure.getFailedLoginNotBefore());
-            return true;
-        }
         return false;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 4ff5588..3926447 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -47,7 +47,6 @@ import org.keycloak.representations.idm.OAuthClientRepresentation;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.timer.TimerProvider;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -120,6 +119,7 @@ public class RealmManager implements RealmImporter {
         setupAuthenticationFlows(realm);
         setupRequiredActions(realm);
         setupOfflineTokens(realm);
+        setupAuthorizationServices(realm);
 
         return realm;
     }
@@ -489,6 +489,9 @@ public class RealmManager implements RealmImporter {
         for (final UserFederationProviderModel fedProvider : federationProviders) {
             usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
         }
+
+        setupAuthorizationServices(realm);
+
         return realm;
     }
 
@@ -581,4 +584,14 @@ public class RealmManager implements RealmImporter {
         return session.users().searchForUser(searchString.trim(), realmModel);
     }
 
+    private void setupAuthorizationServices(RealmModel realm) {
+        for (String roleName : Constants.AUTHZ_DEFAULT_AUTHORIZATION_ROLES) {
+            if (realm.getRole(roleName) == null) {
+                RoleModel role = realm.addRole(roleName);
+                role.setDescription("${role_" + roleName + "}");
+                role.setScopeParamRequired(false);
+                realm.addDefaultRole(roleName);
+            }
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
index 6ffa12c..18b6d43 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
@@ -21,7 +21,8 @@ import org.keycloak.common.ClientConnection;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.managers.BruteForceProtector;
 
@@ -72,14 +73,14 @@ public class AttackDetectionResource {
     /**
      * Get status of a username in brute force detection
      *
-     * @param username
+     * @param userId
      * @return
      */
     @GET
-    @Path("brute-force/usernames/{username}")
+    @Path("brute-force/users/{userId}")
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Map<String, Object> bruteForceUserStatus(@PathParam("username") String username) {
+    public Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId) {
         auth.requireView();
 
         Map<String, Object> data = new HashMap<>();
@@ -89,9 +90,11 @@ public class AttackDetectionResource {
         data.put("lastIPFailure", "n/a");
         if (!realm.isBruteForceProtected()) return data;
 
-        UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
+        UserModel user = session.users().getUserById(userId, realm);
+
+        UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
         if (model == null) return data;
-        if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, username)) {
+        if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
             data.put("disabled", true);
         }
         data.put("numFailures", model.getNumFailures());
@@ -105,16 +108,16 @@ public class AttackDetectionResource {
      *
      * This can release temporary disabled user
      *
-     * @param username
+     * @param userId
      */
-    @Path("brute-force/usernames/{username}")
+    @Path("brute-force/users/{userId}")
     @DELETE
-    public void clearBruteForceForUser(@PathParam("username") String username) {
+    public void clearBruteForceForUser(@PathParam("userId") String userId) {
         auth.requireManage();
 
-        UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
+        UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
         if (model != null) {
-            session.sessions().removeUserLoginFailure(realm, username);
+            session.sessions().removeUserLoginFailure(realm, userId);
             adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
         }
     }
@@ -125,7 +128,7 @@ public class AttackDetectionResource {
      * This can release temporary disabled users
      *
      */
-    @Path("brute-force/usernames")
+    @Path("brute-force/users")
     @DELETE
     public void clearAllBruteForce() {
         auth.requireManage();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index 9b5304e..94d4821 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -20,6 +20,7 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.admin.AuthorizationService;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
@@ -133,12 +134,18 @@ public class ClientResource {
         }
     }
 
-    public static void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
+    public void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
         if (TRUE.equals(rep.isServiceAccountsEnabled()) && !client.isServiceAccountsEnabled()) {
             new ClientManager(new RealmManager(session)).enableServiceAccount(client);
         }
 
         RepresentationToModel.updateClient(rep, client);
+
+        if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
+            authorization().enable();
+        } else {
+            authorization().disable();
+        }
     }
 
     /**
@@ -156,7 +163,11 @@ public class ClientResource {
             throw new NotFoundException("Could not find client");
         }
 
-        return ModelToRepresentation.toRepresentation(client);
+        ClientRepresentation representation = ModelToRepresentation.toRepresentation(client);
+
+        representation.setAuthorizationServicesEnabled(authorization().isEnabled());
+
+        return representation;
     }
 
     /**
@@ -537,4 +548,12 @@ public class ClientResource {
         return result;
     }
 
+    @Path("/authz")
+    public AuthorizationService authorization() {
+        AuthorizationService resource = new AuthorizationService(this.session, this.client, this.auth);
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index b26d95a..1eee46b 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -496,9 +496,9 @@ public class RealmAdminResource {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public List<EventRepresentation> getEvents(@QueryParam("type") List<String> types, @QueryParam("client") String client,
-            @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
-            @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
-            @QueryParam("max") Integer maxResults) {
+                                               @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
+                                               @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
+                                               @QueryParam("max") Integer maxResults) {
         auth.init(RealmAuth.Resource.EVENTS).requireView();
 
         EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
@@ -585,10 +585,10 @@ public class RealmAdminResource {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public List<AdminEventRepresentation> getEvents(@QueryParam("operationTypes") List<String> operationTypes, @QueryParam("authRealm") String authRealm, @QueryParam("authClient") String authClient,
-            @QueryParam("authUser") String authUser, @QueryParam("authIpAddress") String authIpAddress,
-            @QueryParam("resourcePath") String resourcePath, @QueryParam("dateFrom") String dateFrom,
-            @QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
-            @QueryParam("max") Integer maxResults) {
+                                                    @QueryParam("authUser") String authUser, @QueryParam("authIpAddress") String authIpAddress,
+                                                    @QueryParam("resourcePath") String resourcePath, @QueryParam("dateFrom") String dateFrom,
+                                                    @QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
+                                                    @QueryParam("max") Integer maxResults) {
         auth.init(RealmAuth.Resource.EVENTS).requireView();
 
         EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
index 440241d..176c480 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
@@ -31,7 +31,7 @@ public class RealmAuth {
     private Resource resource;
 
     public enum Resource {
-        CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION
+        CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION
     }
 
     private AdminAuth auth;
@@ -89,6 +89,8 @@ public class RealmAuth {
                 return AdminRoles.VIEW_EVENTS;
             case IDENTITY_PROVIDER:
                 return AdminRoles.VIEW_IDENTITY_PROVIDERS;
+            case AUTHORIZATION:
+                return AdminRoles.VIEW_AUTHORIZATION;
             default:
                 throw new IllegalStateException();
         }
@@ -108,6 +110,8 @@ public class RealmAuth {
                 return AdminRoles.MANAGE_IDENTITY_PROVIDERS;
             case IMPERSONATION:
                 return ImpersonationConstants.IMPERSONATION_ROLE;
+            case AUTHORIZATION:
+                return AdminRoles.MANAGE_AUTHORIZATION;
             default:
                 throw new IllegalStateException();
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index c6407c7..1a9a5c3 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -105,15 +105,16 @@ public class RealmsAdminResource {
             ClientModel adminApp = auth.getRealm().getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()));
             addRealmRep(reps, auth.getRealm(), adminApp);
         }
+
+        if (reps.isEmpty()) {
+            throw new ForbiddenException();
+        }
+
         logger.debug(("getRealms()"));
         return reps;
     }
 
     protected void addRealmRep(List<RealmRepresentation> reps, RealmModel realm, ClientModel realmManagementClient) {
-        if (!auth.hasOneOfAppRole(realmManagementClient, AdminRoles.ALL_REALM_ROLES)) {
-            throw new ForbiddenException();
-        }
-
         if (auth.hasAppRole(realmManagementClient, AdminRoles.VIEW_REALM)) {
             reps.add(ModelToRepresentation.toRepresentation(realm, false));
         } else if (auth.hasOneOfAppRole(realmManagementClient, AdminRoles.ALL_REALM_ROLES)) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 2eb39bd..5788b42 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -81,7 +81,6 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.WebApplicationException;
 
-import java.io.IOException;
 import java.net.URI;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -94,15 +93,12 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.managers.UserSessionManager;
 import org.keycloak.services.resources.AccountService;
 import org.keycloak.common.util.Time;
 import org.keycloak.services.validation.Validation;
-import org.keycloak.theme.Theme;
-import org.keycloak.theme.Theme.Type;
-import org.keycloak.theme.ThemeProvider;
 
 /**
  * Base resource for managing users
@@ -166,8 +162,8 @@ public class UsersResource {
                 attrsToRemove = Collections.emptySet();
             }
 
-            if (rep.isEnabled() != null && rep.isEnabled() && rep.getUsername() != null) {
-                UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase());
+            if (rep.isEnabled() != null && rep.isEnabled()) {
+                UserLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, id);
                 if (failureModel != null) {
                     failureModel.clearFailures();
                 }
@@ -300,7 +296,7 @@ public class UsersResource {
             rep.setFederatedIdentities(reps);
         }
 
-        if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, rep.getUsername())) {
+        if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
             rep.setEnabled(false);
         }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/JsResource.java b/services/src/main/java/org/keycloak/services/resources/JsResource.java
index 32161a4..c74abf0 100755
--- a/services/src/main/java/org/keycloak/services/resources/JsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/JsResource.java
@@ -44,55 +44,90 @@ public class JsResource {
     @GET
     @Path("/keycloak.js")
     @Produces("text/javascript")
-    public Response getJs() {
-        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.js");
-        if (inputStream != null) {
-            CacheControl cacheControl = new CacheControl();
-            cacheControl.setNoTransform(false);
-            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+    public Response getKeycloakJs() {
+        return getJs("keycloak.js");
+    }
 
-            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
-        } else {
+    @GET
+    @Path("/{version}/keycloak.js")
+    @Produces("text/javascript")
+    public Response getKeycloakJsWithVersion(@PathParam("version") String version) {
+        if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
+
+        return getKeycloakJs();
     }
 
     @GET
-    @Path("/{version}/keycloak.js")
+    @Path("/keycloak.min.js")
+    @Produces("text/javascript")
+    public Response getKeycloakMinJs() {
+        return getJs("keycloak.min.js");
+    }
+
+    @GET
+    @Path("/{version}/keycloak.min.js")
     @Produces("text/javascript")
-    public Response getJsWithVersion(@PathParam("version") String version) {
+    public Response getKeycloakMinJsWithVersion(@PathParam("version") String version) {
         if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
 
-        return getJs();
+        return getKeycloakMinJs();
     }
 
+    /**
+     * Get keycloak-authz.js file for javascript clients
+     *
+     * @return
+     */
     @GET
-    @Path("/keycloak.min.js")
+    @Path("/keycloak-authz.js")
     @Produces("text/javascript")
-    public Response getMinJs() {
-        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.min.js");
-        if (inputStream != null) {
-            CacheControl cacheControl = new CacheControl();
-            cacheControl.setNoTransform(false);
-            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+    public Response getKeycloakAuthzJs() {
+        return getJs("keycloak-authz.js");
+    }
 
-            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
-        } else {
+    @GET
+    @Path("/{version}/keycloak-authz.js")
+    @Produces("text/javascript")
+    public Response getKeycloakAuthzJsWithVersion(@PathParam("version") String version) {
+        if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
+
+        return getKeycloakAuthzJs();
     }
 
     @GET
-    @Path("/{version}/keycloak.min.js")
+    @Path("/keycloak-authz.min.js")
     @Produces("text/javascript")
-    public Response getMinJsWithVersion(@PathParam("version") String version) {
+    public Response getKeycloakAuthzMinJs() {
+        return getJs("keycloak-authz.min.js");
+    }
+
+    @GET
+    @Path("/{version}/keycloak-authz.min.js")
+    @Produces("text/javascript")
+    public Response getKeycloakAuthzMinJsWithVersion(@PathParam("version") String version) {
         if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
 
-        return getMinJs();
+        return getKeycloakAuthzMinJs();
     }
 
+    private Response getJs(String name) {
+        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name);
+        if (inputStream != null) {
+            CacheControl cacheControl = new CacheControl();
+            cacheControl.setNoTransform(false);
+            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+
+            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
+        } else {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index d6d44d4..0385281 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -26,7 +26,8 @@ import org.keycloak.exportimport.ExportImportManager;
 import org.keycloak.migration.MigrationModelManager;
 import org.keycloak.models.*;
 import org.keycloak.models.dblock.DBLockProvider;
-import org.keycloak.services.managers.DBLockManager;
+import org.keycloak.models.dblock.DBLockManager;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.PostMigrationEvent;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.RealmRepresentation;
@@ -91,53 +92,28 @@ public class KeycloakApplication extends Application {
 
         singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
 
-        ExportImportManager exportImportManager;
-
-        DBLockManager dbLockManager = new DBLockManager(sessionFactory.create());
-        dbLockManager.checkForcedUnlock();
-        DBLockProvider dbLock = dbLockManager.getDBLock();
-        dbLock.waitForLock();
-        try {
-            migrateModel();
-
-            KeycloakSession session = sessionFactory.create();
-            try {
-                session.getTransaction().begin();
+        ExportImportManager[] exportImportManager = new ExportImportManager[1];
 
-                ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
-                exportImportManager = new ExportImportManager(session);
+        KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
 
-                boolean createMasterRealm = applianceBootstrap.isNewInstall();
-                if (exportImportManager.isRunImport() && exportImportManager.isImportMasterIncluded()) {
-                    createMasterRealm = false;
-                }
-
-                if (createMasterRealm) {
-                    applianceBootstrap.createMasterRealm(contextPath);
-                }
-                session.getTransaction().commit();
-            } catch (RuntimeException re) {
-                if (session.getTransaction().isActive()) {
-                    session.getTransaction().rollback();
+            @Override
+            public void run(KeycloakSession lockSession) {
+                DBLockManager dbLockManager = new DBLockManager(lockSession);
+                dbLockManager.checkForcedUnlock();
+                DBLockProvider dbLock = dbLockManager.getDBLock();
+                dbLock.waitForLock();
+                try {
+                    exportImportManager[0] = migrateAndBootstrap();
+                } finally {
+                    dbLock.releaseLock();
                 }
-                throw re;
-            } finally {
-                session.close();
             }
 
-            if (exportImportManager.isRunImport()) {
-                exportImportManager.runImport();
-            } else {
-                importRealms();
-            }
+        });
 
-            importAddUser();
-        } finally {
-            dbLock.releaseLock();
-        }
 
-        if (exportImportManager.isRunExport()) {
-            exportImportManager.runExport();
+        if (exportImportManager[0].isRunExport()) {
+            exportImportManager[0].runExport();
         }
 
         boolean bootstrapAdminUser = false;
@@ -158,6 +134,49 @@ public class KeycloakApplication extends Application {
         setupScheduledTasks(sessionFactory);
     }
 
+
+    // Migrate model, bootstrap master realm, import realms and create admin user. This is done with acquired dbLock
+    protected ExportImportManager migrateAndBootstrap() {
+        ExportImportManager exportImportManager;
+        migrateModel();
+
+        KeycloakSession session = sessionFactory.create();
+        try {
+            session.getTransaction().begin();
+
+            ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
+            exportImportManager = new ExportImportManager(session);
+
+            boolean createMasterRealm = applianceBootstrap.isNewInstall();
+            if (exportImportManager.isRunImport() && exportImportManager.isImportMasterIncluded()) {
+                createMasterRealm = false;
+            }
+
+            if (createMasterRealm) {
+                applianceBootstrap.createMasterRealm(contextPath);
+            }
+            session.getTransaction().commit();
+        } catch (RuntimeException re) {
+            if (session.getTransaction().isActive()) {
+                session.getTransaction().rollback();
+            }
+            throw re;
+        } finally {
+            session.close();
+        }
+
+        if (exportImportManager.isRunImport()) {
+            exportImportManager.runImport();
+        } else {
+            importRealms();
+        }
+
+        importAddUser();
+
+        return exportImportManager;
+    }
+
+
     protected void migrateModel() {
         KeycloakSession session = sessionFactory.create();
         try {
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index ae743f3..53c9aaf 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -18,6 +18,8 @@ package org.keycloak.services.resources;
 
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationService;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.common.util.KeycloakUriBuilder;
 import org.keycloak.events.EventBuilder;
@@ -40,8 +42,12 @@ import javax.ws.rs.NotFoundException;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.net.URI;
 
 /**
@@ -95,12 +101,17 @@ public class RealmsResource {
 
     @Path("{realm}/protocol/{protocol}")
     public Object getProtocol(final @PathParam("realm") String name,
-                                            final @PathParam("protocol") String protocol) {
+                              final @PathParam("protocol") String protocol) {
         RealmModel realm = init(name);
 
+        LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol);
+        if(factory == null){
+            logger.debugf("protocol %s not found", protocol);
+            throw new NotFoundException("Protocol not found");
+        }
+
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
 
-        LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol);
         Object endpoint = factory.createProtocolEndpoint(realm, event);
 
         ResteasyProviderFactory.getInstance().injectProperties(endpoint);
@@ -228,7 +239,7 @@ public class RealmsResource {
     @Path("{realm}/.well-known/{provider}")
     @Produces(MediaType.APPLICATION_JSON)
     public Response getWellKnown(final @PathParam("realm") String name,
-                              final @PathParam("provider") String providerName) {
+                                 final @PathParam("provider") String providerName) {
         init(name);
 
         WellKnownProvider wellKnown = session.getProvider(WellKnownProvider.class, providerName);
@@ -237,6 +248,17 @@ public class RealmsResource {
         return Cors.add(request, responseBuilder).allowedOrigins("*").build();
     }
 
+    @Path("{realm}/authz")
+    public Object getAuthorizationService(@PathParam("realm") String name) {
+        init(name);
+        AuthorizationProvider authorization = this.session.getProvider(AuthorizationProvider.class);
+        AuthorizationService service = new AuthorizationService(authorization);
+
+        ResteasyProviderFactory.getInstance().injectProperties(service);
+
+        return service;
+    }
+
     /**
      * A JAX-RS sub-resource locator that uses the {@link org.keycloak.services.resource.RealmResourceSPI} to resolve sub-resources instances given an <code>unknownPath</code>.
      *
diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java
index c50191f..fb71a2e 100644
--- a/services/src/main/java/org/keycloak/services/ServicesLogger.java
+++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java
@@ -402,8 +402,4 @@ public interface ServicesLogger extends BasicLogger {
     @LogMessage(level = ERROR)
     @Message(id=90, value="Failed to close ProviderSession")
     void failedToCloseProviderSession(@Cause Throwable t);
-
-    @LogMessage(level = WARN)
-    @Message(id=91, value="Forced release of DB lock at startup requested by System property. Make sure to not use this in production environment! And especially when more cluster nodes are started concurrently.")
-    void forcedReleaseDBLock();
 }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authorization.AuthorizationProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.authorization.AuthorizationProviderFactory
new file mode 100644
index 0000000..3ffe34c
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authorization.AuthorizationProviderFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual 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.
+#
+
+org.keycloak.authorization.DefaultAuthorizationProviderFactory
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.oidc.TokenIntrospectionProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.protocol.oidc.TokenIntrospectionProviderFactory
new file mode 100644
index 0000000..ae45923
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.oidc.TokenIntrospectionProviderFactory
@@ -0,0 +1,21 @@
+#
+#  Copyright 2016 Red Hat, Inc. and/or its affiliates
+#  and other contributors as indicated by the @author tags.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+#
+
+org.keycloak.protocol.oidc.AccessTokenIntrospectionProviderFactory
+org.keycloak.protocol.oidc.RefreshTokenIntrospectionProviderFactory
+org.keycloak.authorization.protection.introspect.RPTIntrospectionProviderFactory
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 50bb346..55b31a0 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -18,3 +18,4 @@
 org.keycloak.exportimport.ClientDescriptionConverterSpi
 org.keycloak.wellknown.WellKnownSpi
 org.keycloak.services.clientregistration.ClientRegistrationSpi
+
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.wellknown.WellKnownProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.wellknown.WellKnownProviderFactory
index afc490e..df3dd7a 100644
--- a/services/src/main/resources/META-INF/services/org.keycloak.wellknown.WellKnownProviderFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.wellknown.WellKnownProviderFactory
@@ -15,4 +15,5 @@
 # limitations under the License.
 #
 
-org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory
\ No newline at end of file
+org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory
+org.keycloak.authorization.config.UmaWellKnownProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 362d749..c1d0412 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -29,6 +29,11 @@
     <name>Keycloak Integration TestSuite</name>
     <description />
 
+    <properties>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.bouncycastle</groupId>
@@ -135,6 +140,10 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-authz-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
         </dependency>
         <dependency>
@@ -400,6 +409,7 @@
                                 <keycloak.user.provider>jpa</keycloak.user.provider>
                                 <keycloak.userSessionPersister.provider>jpa</keycloak.userSessionPersister.provider>
                                 <keycloak.eventsStore.provider>jpa</keycloak.eventsStore.provider>
+                                <keycloak.authorization.provider>jpa</keycloak.authorization.provider>
 
                                 <keycloak.liquibase.logging.level>debug</keycloak.liquibase.logging.level>
                             </systemPropertyVariables>
@@ -439,6 +449,7 @@
                                         <keycloak.user.provider>mongo</keycloak.user.provider>
                                         <keycloak.userSessionPersister.provider>mongo</keycloak.userSessionPersister.provider>
                                         <keycloak.eventsStore.provider>mongo</keycloak.eventsStore.provider>
+                                        <keycloak.authorization.provider>mongo</keycloak.authorization.provider>
                                         <keycloak.connectionsMongo.host>${keycloak.connectionsMongo.host}</keycloak.connectionsMongo.host>
                                         <keycloak.connectionsMongo.port>${keycloak.connectionsMongo.port}</keycloak.connectionsMongo.port>
                                         <keycloak.connectionsMongo.db>${keycloak.connectionsMongo.db}</keycloak.connectionsMongo.db>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java
index 19da666..6c10322 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java
@@ -107,7 +107,7 @@ public class AddUserTest {
 
             List<RoleRepresentation> realmRoles = userResource.roles().realmLevel().listAll();
 
-            assertRoles(realmRoles, "admin", "offline_access");
+            assertRoles(realmRoles, "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
             List<ClientRepresentation> clients = realm.clients().findAll();
             String accountId = null;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java
new file mode 100644
index 0000000..301adca
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java
@@ -0,0 +1,127 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakTransactionManager;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.AccessTokenResponse;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Invocation;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MASTER;
+import static org.keycloak.models.AdminRoles.ADMIN;
+import static org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractAuthorizationTest {
+
+    protected static final String TEST_REALM_NAME = "photoz";
+
+    @Rule
+    public KeycloakAuthorizationServerRule keycloak = new KeycloakAuthorizationServerRule(TEST_REALM_NAME);
+
+    private Keycloak adminClient;
+
+    @Before
+    public void onBefore() {
+        adminClient = Keycloak.getInstance(AUTH_SERVER_ROOT, MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
+    }
+
+    protected <R> R onAuthorizationSession(Function<AuthorizationProvider, R> function) {
+        KeycloakSession keycloakSession = startKeycloakSession();
+        KeycloakTransactionManager transaction = keycloakSession.getTransaction();
+
+        try {
+            AuthorizationProvider authorizationProvider = keycloakSession.getProvider(AuthorizationProvider.class);
+
+            R result = function.apply(authorizationProvider);
+
+            transaction.commit();
+
+            return result;
+        } catch (Exception e) {
+            transaction.rollback();
+            throw new RuntimeException(e);
+        } finally {
+            if (keycloakSession != null) {
+                keycloakSession.close();
+            }
+        }
+    }
+
+    protected void onAuthorizationSession(Consumer<AuthorizationProvider> consumer) {
+        KeycloakSession keycloakSession = startKeycloakSession();
+        KeycloakTransactionManager transaction = keycloakSession.getTransaction();
+
+        try {
+            AuthorizationProvider authorizationProvider = keycloakSession.getProvider(AuthorizationProvider.class);
+
+            consumer.accept(authorizationProvider);
+
+            transaction.commit();
+        } catch (Exception e) {
+            transaction.rollback();
+            throw new RuntimeException(e);
+        } finally {
+            if (keycloakSession != null) {
+                keycloakSession.close();
+            }
+        }
+    }
+
+    protected Invocation.Builder newClient(ClientModel client, String authzRelativePath) {
+        return ClientBuilder.newClient()
+                .register((ClientRequestFilter) requestContext -> {
+                    AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
+                    requestContext.getHeaders().add("Authorization", "Bearer " + accessToken.getToken());
+                }).target(AUTH_SERVER_ROOT + "/admin/realms/" + TEST_REALM_NAME + "/clients/" + client.getId() + "/authz" + authzRelativePath).request();
+    }
+
+    protected ClientModel getClientByClientId(String clientId) {
+        KeycloakSession session = this.keycloak.startSession();
+
+        try {
+            RealmModel realm = session.realms().getRealmByName(TEST_REALM_NAME);
+            return realm.getClientByClientId(clientId);
+        } finally {
+            session.close();
+        }
+    }
+
+    private KeycloakSession startKeycloakSession() {
+        KeycloakSession keycloakSession = this.keycloak.startSession();
+
+        keycloakSession.getContext().setRealm(keycloakSession.realms().getRealmByName(TEST_REALM_NAME));
+
+        return keycloakSession;
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
new file mode 100644
index 0000000..0786eab
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
@@ -0,0 +1,370 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.apache.commons.collections.map.HashedMap;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.junit.Before;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.common.KeycloakEvaluationContext;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DefaultEvaluation;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest {
+
+    protected ResourceServer resourceServer;
+    protected Resource adminResource;
+    protected Policy anyAdminPolicy;
+    protected Policy onlyFromSpecificAddressPolicy;
+    protected Policy administrationPolicy;
+
+    protected Resource albumResource;
+    protected Policy anyUserPolicy;
+
+    @Before
+    public void onBefore() {
+        super.onBefore();
+        this.resourceServer = createResourceServer();
+        this.adminResource = createAdminAlbumResource();
+        this.anyAdminPolicy = createAnyAdminPolicy();
+        this.onlyFromSpecificAddressPolicy = createOnlyFromSpecificAddressPolicy();
+        this.administrationPolicy = createAdministrationPolicy();
+
+        this.albumResource = createAlbumResource();
+        this.anyUserPolicy = createAnyUserPolicy();
+    }
+
+    protected ResourceServer createResourceServer() {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+
+            return resourceServerStore.create(getClientByClientId("photoz-restful-api").getId());
+        });
+    }
+
+    protected Map<String, DefaultEvaluation> performEvaluation(List<ResourcePermission> permissions, AccessToken accessToken, ClientConnection clientConnection) {
+        Map<String, DefaultEvaluation> evaluations = new HashedMap();
+
+        onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+
+            // during tests we create resource instances, but we need to reload them to get their collections updated
+            List<ResourcePermission> updatedPermissions = permissions.stream().map(permission -> {
+                Resource resource = storeFactory.getResourceStore().findById(permission.getResource().getId());
+                return new ResourcePermission(resource, permission.getScopes(), permission.getResourceServer());
+            }).collect(Collectors.toList());
+
+            authorizationProvider.evaluators().from(updatedPermissions, createEvaluationContext(accessToken, clientConnection, authorizationProvider)).evaluate(new Decision<DefaultEvaluation>() {
+                @Override
+                public void onDecision(DefaultEvaluation evaluation) {
+                    evaluations.put(evaluation.getPolicy().getId(), evaluation);
+                }
+
+                @Override
+                public void onError(Throwable cause) {
+                    throw new RuntimeException("Permission evaluation failed.", cause);
+                }
+            });
+        });
+
+        return evaluations;
+    }
+
+    private KeycloakEvaluationContext createEvaluationContext(AccessToken accessToken, ClientConnection clientConnection, AuthorizationProvider authorizationProvider) {
+        KeycloakSession keycloakSession = authorizationProvider.getKeycloakSession();
+
+        keycloakSession.getContext().setConnection(clientConnection);
+
+        keycloakSession.getContext().setClient(getClientByClientId("photoz-html5-client"));
+
+        ResteasyProviderFactory.pushContext(HttpHeaders.class, createHttpHeaders());
+
+        KeycloakIdentity identity = new KeycloakIdentity(accessToken, keycloakSession);
+
+        return new KeycloakEvaluationContext(identity, keycloakSession);
+    }
+
+    protected AccessToken createAccessToken(Set<String> roles) {
+        AccessToken accessToken = new AccessToken();
+
+        accessToken.setRealmAccess(new AccessToken.Access());
+        accessToken.getRealmAccess().roles(roles);
+
+        return accessToken;
+    }
+
+
+    private HttpHeaders createHttpHeaders() {
+        return new HttpHeaders() {
+            @Override
+            public List<String> getRequestHeader(String name) {
+                return null;
+            }
+
+            @Override
+            public String getHeaderString(String name) {
+                return null;
+            }
+
+            @Override
+            public MultivaluedMap<String, String> getRequestHeaders() {
+                return null;
+            }
+
+            @Override
+            public List<MediaType> getAcceptableMediaTypes() {
+                return null;
+            }
+
+            @Override
+            public List<Locale> getAcceptableLanguages() {
+                return null;
+            }
+
+            @Override
+            public MediaType getMediaType() {
+                return null;
+            }
+
+            @Override
+            public Locale getLanguage() {
+                return null;
+            }
+
+            @Override
+            public Map<String, Cookie> getCookies() {
+                return null;
+            }
+
+            @Override
+            public Date getDate() {
+                return null;
+            }
+
+            @Override
+            public int getLength() {
+                return 0;
+            }
+        };
+    }
+
+    protected ClientConnection createClientConnection(String remoteAddr) {
+        return new ClientConnection() {
+            @Override
+            public String getRemoteAddr() {
+                return remoteAddr;
+            }
+
+            @Override
+            public String getRemoteHost() {
+                return "localhost";
+            }
+
+            @Override
+            public int getRemotePort() {
+                return 0;
+            }
+
+            @Override
+            public String getLocalAddr() {
+                return null;
+            }
+
+            @Override
+            public int getLocalPort() {
+                return 0;
+            }
+        };
+    }
+
+    protected Invocation.Builder newPermissionRequest(String... id) {
+        String idPathParam = "";
+
+        if (id.length != 0) {
+            idPathParam = "/" + id[0];
+        }
+
+        return newClient(getClientByClientId("photoz-restful-api"), "/resource-server/policy" + idPathParam);
+    }
+
+    private Policy createAdministrationPolicy() {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+            Policy policy = policyStore.create("Administration Policy", "aggregate", resourceServer);
+
+            policy.addAssociatedPolicy(anyAdminPolicy);
+            policy.addAssociatedPolicy(onlyFromSpecificAddressPolicy);
+
+            return policy;
+        });
+    }
+
+    private Policy createOnlyFromSpecificAddressPolicy() {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+            Policy policy = policyStore.create("Only From a Specific Client Address", "js", resourceServer);
+            HashedMap config = new HashedMap();
+
+            config.put("code",
+                    "var contextAttributes = $evaluation.getContext().getAttributes();" +
+                    "var networkAddress = contextAttributes.getValue('kc.client.network.ip_address');" +
+                    "if ('127.0.0.1'.equals(networkAddress.asInetAddress(0).getHostAddress())) {" +
+                        "$evaluation.grant();" +
+                    "}");
+
+            policy.setConfig(config);
+
+            return policy;
+        });
+    }
+
+    private Policy createAnyAdminPolicy() {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+            Policy policy = policyStore.create("Any Admin Policy", "role", resourceServer);
+            HashedMap config = new HashedMap();
+            RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
+            RoleModel adminRole = realm.getRole("admin");
+
+            try {
+                config.put("roles", JsonSerialization.writeValueAsString(new String[] {adminRole.getId()}));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            policy.setConfig(config);
+
+            return policy;
+        });
+    }
+
+    private Resource createAdminAlbumResource() {
+        ResourceRepresentation representation = new ResourceRepresentation();
+
+        representation.setName("Admin Resources");
+        representation.setType("http://photoz.com/admin");
+        representation.setUri("/admin/*");
+
+        HashSet<ScopeRepresentation> scopes = new HashSet<>();
+
+        scopes.add(new ScopeRepresentation("urn:photoz.com:scopes:album:admin:manage"));
+
+        representation.setScopes(scopes);
+
+        return createResource(representation);
+    }
+
+    private Resource createAlbumResource() {
+        ResourceRepresentation representation = new ResourceRepresentation();
+
+        representation.setName("Album Resource");
+        representation.setType("http://photoz.com/album");
+        representation.setUri("/album/*");
+
+        HashSet<ScopeRepresentation> scopes = new HashSet<>();
+
+        scopes.add(new ScopeRepresentation("urn:photoz.com:scopes:album:view"));
+        scopes.add(new ScopeRepresentation("urn:photoz.com:scopes:album:create"));
+        scopes.add(new ScopeRepresentation("urn:photoz.com:scopes:album:delete"));
+
+        representation.setScopes(scopes);
+
+        return createResource(representation);
+    }
+
+    protected Resource createResource(ResourceRepresentation representation) {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            ScopeStore scopeStore = storeFactory.getScopeStore();
+
+            representation.getScopes().forEach(scopeRepresentation -> {
+                scopeStore.create(scopeRepresentation.getName(), resourceServer);
+            });
+
+            ResourceStore resourceStore = storeFactory.getResourceStore();
+            Resource albumResource = resourceStore.create(representation.getName(), resourceServer, resourceServer.getId());
+
+            albumResource.setType(representation.getType());
+            albumResource.setUri(representation.getUri());
+            albumResource.setIconUri(representation.getIconUri());
+
+            return albumResource;
+        });
+    }
+
+    private Policy createAnyUserPolicy() {
+        return onAuthorizationSession(authorizationProvider -> {
+            StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+            Policy policy = policyStore.create("Any User Policy", "role", resourceServer);
+            HashedMap config = new HashedMap();
+            RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
+            RoleModel adminRole = realm.getRole("user");
+
+            try {
+                config.put("roles", JsonSerialization.writeValueAsString(new String[] {adminRole.getId()}));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            policy.setConfig(config);
+
+            return policy;
+        });
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AttributeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AttributeTest.java
new file mode 100644
index 0000000..86e1ba0
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AttributeTest.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.apache.commons.collections.map.HashedMap;
+import org.junit.Test;
+import org.keycloak.authorization.attribute.Attributes;
+
+import java.net.InetAddress;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AttributeTest {
+
+    @Test
+    public void testManageAttributes() throws ParseException {
+        Map<String, Collection<String>> map = new HashedMap();
+
+        map.put("integer", asList("1"));
+        map.put("long", asList("" + Long.MAX_VALUE));
+        map.put("string", asList("some string"));
+        map.put("date", asList("12/12/2016"));
+        map.put("ip_network_address", asList("127.0.0.1"));
+        map.put("host_network_address", asList("localhost"));
+        map.put("multi_valued", asList("1", "2", "3", "4"));
+
+        Attributes attributes = Attributes.from(map);
+
+        map.keySet().forEach(new Consumer<String>() {
+            @Override
+            public void accept(String name) {
+                assertTrue(attributes.exists(name));
+            }
+        });
+
+        assertFalse(attributes.exists("not_found"));
+        assertTrue(attributes.containsValue("integer", "1"));
+        assertTrue(attributes.containsValue("multi_valued", "3"));
+
+        assertEquals(1, attributes.getValue("multi_valued").asInt(0));
+        assertEquals(4, attributes.getValue("multi_valued").asInt(3));
+
+        assertEquals(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2016"), attributes.getValue("date").asDate(0, "dd/MM/yyyy"));
+
+        assertEquals(InetAddress.getLoopbackAddress(), attributes.getValue("ip_network_address").asInetAddress(0));
+        assertEquals(InetAddress.getLoopbackAddress(), attributes.getValue("host_network_address").asInetAddress(0));
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/KeycloakAuthorizationServerRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/KeycloakAuthorizationServerRule.java
new file mode 100644
index 0000000..1df0b61
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/KeycloakAuthorizationServerRule.java
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakAuthorizationServerRule extends AbstractKeycloakRule {
+
+    private final String realmName;
+
+    KeycloakAuthorizationServerRule(String realmName) {
+        this.realmName = realmName;
+    }
+
+    @Override
+    protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+        server.importRealm(getClass().getResourceAsStream("/authorization-test/test-" + realmName + "-realm.json"));
+    }
+
+    @Override
+    protected String[] getTestRealms() {
+        return new String[] {this.realmName};
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java
new file mode 100644
index 0000000..f323265
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourceManagementTest.java
@@ -0,0 +1,163 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.junit.Test;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.model.Resource;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceManagementTest extends AbstractPhotozAdminTest {
+
+    @Test
+    public void testCreate() throws Exception {
+        ResourceRepresentation newResource = new ResourceRepresentation();
+
+        newResource.setName("New Resource");
+        newResource.setType("Resource Type");
+        newResource.setIconUri("Resource Icon URI");
+        newResource.setUri("Resource URI");
+
+        Response response = newResourceRequest().post(Entity.entity(newResource, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+
+            assertNotNull(resourceModel);
+            assertEquals(resource.getId(), resourceModel.getId());
+            assertEquals("New Resource", resourceModel.getName());
+            assertEquals("Resource Type", resourceModel.getType());
+            assertEquals("Resource Icon URI", resourceModel.getIconUri());
+            assertEquals("Resource URI", resourceModel.getUri());
+            assertEquals(resourceServer.getClientId(), resourceModel.getOwner());
+            assertEquals(resourceServer.getId(), resourceModel.getResourceServer().getId());
+        });
+    }
+
+    @Test
+    public void testUpdate() throws Exception {
+        ResourceRepresentation newResource = new ResourceRepresentation();
+
+        newResource.setName("New Resource");
+        newResource.setType("Resource Type");
+        newResource.setIconUri("Resource Icon URI");
+        newResource.setUri("Resource URI");
+
+        Response response = newResourceRequest().post(Entity.entity(newResource, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        newResource.setName("New Resource Changed");
+        newResource.setType("Resource Type Changed");
+        newResource.setIconUri("Resource Icon URI Changed");
+        newResource.setUri("Resource URI Changed");
+
+        response = newResourceRequest().post(Entity.entity(newResource, MediaType.APPLICATION_JSON_TYPE));
+
+        ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+
+            assertNotNull(resourceModel);
+            assertEquals(resource.getId(), resourceModel.getId());
+            assertEquals("New Resource Changed", resourceModel.getName());
+            assertEquals("Resource Type Changed", resourceModel.getType());
+            assertEquals("Resource Icon URI Changed", resourceModel.getIconUri());
+            assertEquals("Resource URI Changed", resourceModel.getUri());
+            assertEquals(resourceServer.getId(), resourceModel.getResourceServer().getId());
+        });
+    }
+
+    @Test
+    public void testFindById() throws Exception {
+        ResourceRepresentation newResource = new ResourceRepresentation();
+
+        newResource.setName("New Resource");
+        newResource.setType("Resource Type");
+        newResource.setIconUri("Resource Icon URI");
+        newResource.setUri("Resource URI");
+
+        Response response = newResourceRequest().post(Entity.entity(newResource, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
+
+        response = newResourceRequest(resource.getId()).get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        resource = response.readEntity(ResourceRepresentation.class);
+
+        assertEquals("New Resource", resource.getName());
+        assertEquals("Resource Type", resource.getType());
+        assertEquals("Resource Icon URI", resource.getIconUri());
+        assertEquals("Resource URI", resource.getUri());
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        ResourceRepresentation newResource = new ResourceRepresentation();
+
+        newResource.setName("New Resource");
+
+        Response response = newResourceRequest().post(Entity.entity(newResource, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
+
+        assertNotNull(resource.getId());
+
+        response = newResourceRequest(resource.getId()).delete();
+
+        assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+        onAuthorizationSession(authorizationProvider -> {
+            Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
+
+            assertNull(resourceModel);
+        });
+    }
+
+    private Builder newResourceRequest(String... id) {
+        String idPathParam = "";
+
+        if (id.length != 0) {
+            idPathParam = "/" + id[0];
+        }
+
+        return newClient(getClientByClientId("photoz-restful-api"), "/resource-server/resource" + idPathParam);
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
new file mode 100644
index 0000000..a4cc551
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
@@ -0,0 +1,364 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.apache.commons.collections.map.HashedMap;
+import org.junit.Test;
+import org.keycloak.authorization.Decision.Effect;
+import org.keycloak.authorization.admin.representation.PolicyRepresentation;
+import org.keycloak.authorization.admin.representation.ResourceRepresentation;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.DefaultEvaluation;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
+
+    @Test
+    public void testCreateForTypeWithSinglePolicy() throws Exception {
+        PolicyRepresentation newPermission = new PolicyRepresentation();
+
+        newPermission.setName("Admin Resource Policy");
+        newPermission.setType("resource");
+
+        HashedMap config = new HashedMap();
+
+        config.put("defaultResourceType", "http://photoz.com/admin");
+        config.put("applyPolicies", JsonSerialization.writeValueAsString(new String[] {this.administrationPolicy.getId()}));
+
+        newPermission.setConfig(config);
+
+        Response response = newPermissionRequest().post(Entity.entity(newPermission, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
+
+            assertNotNull(policyModel);
+            assertEquals(permission.getId(), policyModel.getId());
+            assertEquals(newPermission.getName(), policyModel.getName());
+            assertEquals(newPermission.getType(), policyModel.getType());
+            assertEquals(resourceServer.getId(), policyModel.getResourceServer().getId());
+        });
+
+        Set<String> roles = new HashSet<>();
+
+        roles.add("admin");
+
+        Map<String, DefaultEvaluation> evaluationsAdminRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(adminResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluationsAdminRole.size());
+        assertTrue(evaluationsAdminRole.containsKey(this.administrationPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluationsAdminRole.get(this.administrationPolicy.getId()).getEffect());
+
+        evaluationsAdminRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(adminResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.10"));
+
+        assertEquals(1, evaluationsAdminRole.size());
+        assertTrue(evaluationsAdminRole.containsKey(this.administrationPolicy.getId()));
+        assertEquals(Effect.DENY, evaluationsAdminRole.get(this.administrationPolicy.getId()).getEffect());
+
+        roles.clear();
+        roles.add("user");
+
+        Map<String, DefaultEvaluation> evaluationsUserRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(adminResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluationsUserRole.size());
+        assertTrue(evaluationsUserRole.containsKey(this.administrationPolicy.getId()));
+        assertEquals(Effect.DENY, evaluationsUserRole.get(this.administrationPolicy.getId()).getEffect());
+    }
+
+    @Test
+    public void testCreateForTypeWithMultiplePolicies() throws Exception {
+        createAlbumResourceTypePermission();
+
+        HashSet<String> roles = new HashSet<>();
+
+        roles.add("admin");
+
+        Map<String, DefaultEvaluation> evaluationsAdminRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(albumResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(2, evaluationsAdminRole.size());
+        assertTrue(evaluationsAdminRole.containsKey(this.administrationPolicy.getId()));
+        assertTrue(evaluationsAdminRole.containsKey(this.anyUserPolicy.getId()));
+        assertEquals(Effect.DENY, evaluationsAdminRole.get(this.anyUserPolicy.getId()).getEffect());
+        assertEquals(Effect.PERMIT, evaluationsAdminRole.get(this.administrationPolicy.getId()).getEffect());
+
+        evaluationsAdminRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(albumResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.10"));
+
+        assertEquals(2, evaluationsAdminRole.size());
+        assertTrue(evaluationsAdminRole.containsKey(this.administrationPolicy.getId()));
+        assertTrue(evaluationsAdminRole.containsKey(this.anyUserPolicy.getId()));
+        assertEquals(Effect.DENY, evaluationsAdminRole.get(this.anyUserPolicy.getId()).getEffect());
+        assertEquals(Effect.DENY, evaluationsAdminRole.get(this.administrationPolicy.getId()).getEffect());
+
+        roles.clear();
+        roles.add("user");
+
+        Map<String, DefaultEvaluation> evaluationsUserRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(albumResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(2, evaluationsUserRole.size());
+        assertTrue(evaluationsUserRole.containsKey(this.administrationPolicy.getId()));
+        assertTrue(evaluationsUserRole.containsKey(this.anyUserPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluationsUserRole.get(this.anyUserPolicy.getId()).getEffect());
+        assertEquals(Effect.DENY, evaluationsUserRole.get(this.administrationPolicy.getId()).getEffect());
+    }
+
+    @Test
+    public void testUpdate() throws Exception {
+        PolicyRepresentation permission = createAlbumResourceTypePermission();
+        Map<String, String> config = permission.getConfig();
+
+        config.put("applyPolicies", JsonSerialization.writeValueAsString(new String[] {this.anyUserPolicy.getId()}));
+
+        permission.setConfig(config);
+
+        newPermissionRequest(permission.getId()).put(Entity.entity(permission, MediaType.APPLICATION_JSON_TYPE));
+
+        HashSet<String> roles = new HashSet<>();
+
+        roles.add("admin");
+
+        Map<String, DefaultEvaluation> evaluationsAdminRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(albumResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluationsAdminRole.size());
+        assertTrue(evaluationsAdminRole.containsKey(this.anyUserPolicy.getId()));
+        assertEquals(Effect.DENY, evaluationsAdminRole.get(this.anyUserPolicy.getId()).getEffect());
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        PolicyRepresentation newPermission = createAlbumResourceTypePermission();
+
+        Response delete = newPermissionRequest(newPermission.getId()).delete();
+
+        assertEquals(Status.NO_CONTENT.getStatusCode(), delete.getStatus());
+    }
+
+    @Test
+    public void testFindById() throws Exception {
+        PolicyRepresentation newPermission = createAlbumResourceTypePermission();
+
+        Response response = newPermissionRequest(newPermission.getId()).get();
+
+        PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
+
+        assertEquals(newPermission.getId(), permission.getId());
+        assertEquals(newPermission.getName(), permission.getName());
+        assertEquals(newPermission.getType(), permission.getType());
+    }
+
+    @Test
+    public void testCreatePolicyForResource() throws Exception {
+        PolicyRepresentation newPermission = new PolicyRepresentation();
+
+        newPermission.setName("Multiple Resource Policy");
+        newPermission.setType("resource");
+
+        HashedMap config = new HashedMap();
+
+        config.put("resources", JsonSerialization.writeValueAsString(new String[] {this.albumResource.getId(), this.adminResource.getId()}));
+        config.put("applyPolicies", JsonSerialization.writeValueAsString(new String[] {this.onlyFromSpecificAddressPolicy.getId()}));
+
+        newPermission.setConfig(config);
+
+        Response response = newPermissionRequest().post(Entity.entity(newPermission, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        List<ResourcePermission> permissions = new ArrayList<>();
+
+        permissions.add(new ResourcePermission(this.albumResource, Collections.emptyList(), this.resourceServer));
+
+        Map<String, DefaultEvaluation> evaluations = performEvaluation(
+                permissions,
+                createAccessToken(Collections.emptySet()),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluations.size());
+        assertTrue(evaluations.containsKey(this.onlyFromSpecificAddressPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluations.get(this.onlyFromSpecificAddressPolicy.getId()).getEffect());
+
+        permissions = new ArrayList<>();
+
+        permissions.add(new ResourcePermission(this.adminResource, Collections.emptyList(), this.resourceServer));
+
+        evaluations = performEvaluation(
+                permissions,
+                createAccessToken(Collections.emptySet()),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluations.size());
+        assertTrue(evaluations.containsKey(this.onlyFromSpecificAddressPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluations.get(this.onlyFromSpecificAddressPolicy.getId()).getEffect());
+
+        permissions = new ArrayList<>();
+
+        permissions.add(new ResourcePermission(this.adminResource, Collections.emptyList(), this.resourceServer));
+        permissions.add(new ResourcePermission(this.albumResource, Collections.emptyList(), this.resourceServer));
+
+        evaluations = performEvaluation(
+                permissions,
+                createAccessToken(Collections.emptySet()),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(1, evaluations.size());
+        assertTrue(evaluations.containsKey(this.onlyFromSpecificAddressPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluations.get(this.onlyFromSpecificAddressPolicy.getId()).getEffect());
+
+        permissions = new ArrayList<>();
+
+        permissions.add(new ResourcePermission(this.adminResource, Collections.emptyList(), this.resourceServer));
+        permissions.add(new ResourcePermission(this.albumResource, Collections.emptyList(), this.resourceServer));
+
+        evaluations = performEvaluation(
+                permissions,
+                createAccessToken(Collections.emptySet()),
+                createClientConnection("127.0.0.10"));
+
+        assertEquals(1, evaluations.size());
+        assertTrue(evaluations.containsKey(this.onlyFromSpecificAddressPolicy.getId()));
+        assertEquals(Effect.DENY, evaluations.get(this.onlyFromSpecificAddressPolicy.getId()).getEffect());
+    }
+
+    /**
+     * Tests if a resource can inherit the policies defined for another resource based on its type
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testInheritPoliciesBasedOnResourceType() throws Exception {
+        createAlbumResourceTypePermission();
+        ResourceRepresentation representation = new ResourceRepresentation();
+
+        representation.setName("Alice Family Album");
+        representation.setType(this.albumResource.getType());
+
+        Resource resource = createResource(representation);
+
+        Set<String> roles = new HashSet<>();
+
+        roles.add("user");
+
+        Map<String, DefaultEvaluation> evaluationsUserRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(resource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        assertEquals(2, evaluationsUserRole.size());
+        assertTrue(evaluationsUserRole.containsKey(this.administrationPolicy.getId()));
+        assertTrue(evaluationsUserRole.containsKey(this.anyUserPolicy.getId()));
+        assertEquals(Effect.PERMIT, evaluationsUserRole.get(this.anyUserPolicy.getId()).getEffect());
+        assertEquals(Effect.DENY, evaluationsUserRole.get(this.administrationPolicy.getId()).getEffect());
+
+        ResourceRepresentation someResourceRep = new ResourceRepresentation();
+
+        someResourceRep.setName("Some Resource");
+        someResourceRep.setType("Some non-existent type");
+
+        Resource someResource = createResource(someResourceRep);
+
+        evaluationsUserRole = performEvaluation(
+                Arrays.asList(new ResourcePermission(someResource, Collections.emptyList(), resourceServer)),
+                createAccessToken(roles),
+                createClientConnection("127.0.0.1"));
+
+        // no policies can be applied given that there is no policy defined for this resource or its type
+        assertEquals(0, evaluationsUserRole.size());
+    }
+
+    private PolicyRepresentation createAlbumResourceTypePermission() throws Exception {
+        PolicyRepresentation newPermission = new PolicyRepresentation();
+
+        newPermission.setName("Album Resource Policy");
+        newPermission.setType("resource");
+        newPermission.setDecisionStrategy(Policy.DecisionStrategy.AFFIRMATIVE);
+
+        HashedMap config = new HashedMap();
+
+        config.put("defaultResourceType",  albumResource.getType());
+
+        String applyPolicies = JsonSerialization.writeValueAsString(new String[]{this.anyUserPolicy.getId(), this.administrationPolicy.getId()});
+
+        config.put("applyPolicies", applyPolicies);
+
+        newPermission.setConfig(config);
+
+        Response response = newPermissionRequest().post(Entity.entity(newPermission, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
+
+            assertNotNull(policyModel);
+            assertEquals(permission.getId(), policyModel.getId());
+            assertEquals(permission.getName(), policyModel.getName());
+            assertEquals(permission.getType(), policyModel.getType());
+            assertTrue(permission.getConfig().containsValue(albumResource.getType()));
+            assertTrue(permission.getConfig().containsValue(applyPolicies));
+            assertEquals(resourceServer.getId(), policyModel.getResourceServer().getId());
+        });
+
+        return permission;
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java
new file mode 100644
index 0000000..839a813
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ScopeManagementTest.java
@@ -0,0 +1,148 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization;
+
+import org.junit.Test;
+import org.keycloak.authorization.admin.representation.ScopeRepresentation;
+import org.keycloak.authorization.model.Scope;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeManagementTest extends AbstractPhotozAdminTest {
+
+    @Test
+    public void testCreate() throws Exception {
+        ScopeRepresentation newScope = new ScopeRepresentation();
+
+        newScope.setName("New Scope");
+        newScope.setIconUri("Icon URI");
+
+        Response response = newScopeRequest().post(Entity.entity(newScope, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+
+            assertNotNull(scopeModel);
+            assertEquals(scope.getId(), scopeModel.getId());
+            assertEquals("New Scope", scopeModel.getName());
+            assertEquals("Icon URI", scopeModel.getIconUri());
+            assertEquals(resourceServer.getId(), scopeModel.getResourceServer().getId());
+        });
+    }
+
+    @Test
+    public void testUpdate() throws Exception {
+        ScopeRepresentation newScope = new ScopeRepresentation();
+
+        newScope.setName("New Scope");
+        newScope.setIconUri("Icon URI");
+
+        Response response = newScopeRequest().post(Entity.entity(newScope, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        newScope.setName("New Scope Changed");
+        newScope.setIconUri("Icon URI Changed");
+
+        response = newScopeRequest().post(Entity.entity(newScope, MediaType.APPLICATION_JSON_TYPE));
+
+        ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
+
+        onAuthorizationSession(authorizationProvider -> {
+            Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+
+            assertNotNull(scopeModel);
+            assertEquals(scope.getId(), scopeModel.getId());
+            assertEquals("New Scope Changed", scopeModel.getName());
+            assertEquals("Icon URI Changed", scopeModel.getIconUri());
+            assertEquals(resourceServer.getId(), scopeModel.getResourceServer().getId());
+        });
+    }
+
+    @Test
+    public void testFindById() throws Exception {
+        ScopeRepresentation newScope = new ScopeRepresentation();
+
+        newScope.setName("New Scope");
+        newScope.setIconUri("Icon URI");
+
+        Response response = newScopeRequest().post(Entity.entity(newScope, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
+
+        response = newScopeRequest(scope.getId()).get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        scope = response.readEntity(ScopeRepresentation.class);
+
+        assertEquals("New Scope", scope.getName());
+        assertEquals("Icon URI", scope.getIconUri());
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        ScopeRepresentation newScope = new ScopeRepresentation();
+
+        newScope.setName("New Scope");
+
+        Response response = newScopeRequest().post(Entity.entity(newScope, MediaType.APPLICATION_JSON_TYPE));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
+
+        assertNotNull(scope.getId());
+
+        response = newScopeRequest(scope.getId()).delete();
+
+        assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+        onAuthorizationSession(authorizationProvider -> {
+            Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
+
+            assertNull(scopeModel);
+        });
+    }
+
+    private Builder newScopeRequest(String... id) {
+        String idPathParam = "";
+
+        if (id.length != 0) {
+            idPathParam = "/" + id[0];
+        }
+
+        return newClient(getClientByClientId("photoz-restful-api"), "/resource-server/scope" + idPathParam);
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
index 6bbfeab..9589fe9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
@@ -44,6 +44,7 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.Retry;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.ErrorServlet;
@@ -503,7 +504,12 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-enc/");
         assertAtLoginPagePostBinding();
         loginPage.login("bburke", "password");
-        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-enc/");
+        Retry.execute(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-enc/");
+            }
+        }, 10, 100);
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-enc?GLO=true");
         checkLoggedOut(APP_SERVER_BASE_URL + "/sales-post-enc/", true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
index 6cb3f7c..7791c91 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
@@ -22,7 +22,9 @@ import io.undertow.servlet.Servlets;
 import io.undertow.servlet.api.DefaultServletConfig;
 import io.undertow.servlet.api.DeploymentInfo;
 import io.undertow.servlet.api.FilterInfo;
+import io.undertow.servlet.api.ServletInfo;
 import org.jboss.logging.Logger;
+import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher;
 import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
 import org.jboss.resteasy.spi.ResteasyDeployment;
 import org.keycloak.models.KeycloakSession;
@@ -37,6 +39,7 @@ import org.keycloak.testsuite.util.cli.TestsuiteCLI;
 import org.keycloak.util.JsonSerialization;
 
 import javax.servlet.DispatcherType;
+import javax.ws.rs.core.Application;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -309,7 +312,17 @@ public class KeycloakServer {
 
             di.setDefaultServletConfig(new DefaultServletConfig(true));
 
+            ServletInfo restEasyDispatcher = Servlets.servlet("Keycloak REST Interface", HttpServlet30Dispatcher.class);
+
+            restEasyDispatcher.addInitParam("resteasy.servlet.mapping.prefix", "/");
+            restEasyDispatcher.setAsyncSupported(true);
+
+            di.addServlet(restEasyDispatcher);
+
             FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
+
+            filter.setAsyncSupported(true);
+
             di.addFilter(filter);
             di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index 27b550a..f340379 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -86,7 +86,7 @@ public class AdapterTest extends AbstractModelTest {
         Assert.assertEquals(realmModel.getName(), "JUGGLER");
         Assert.assertArrayEquals(realmModel.getPrivateKey().getEncoded(), keyPair.getPrivate().getEncoded());
         Assert.assertArrayEquals(realmModel.getPublicKey().getEncoded(), keyPair.getPublic().getEncoded());
-        Assert.assertEquals(2, realmModel.getDefaultRoles().size());
+        Assert.assertEquals(3, realmModel.getDefaultRoles().size());
         Assert.assertTrue(realmModel.getDefaultRoles().contains("foo"));
     }
 
@@ -112,7 +112,7 @@ public class AdapterTest extends AbstractModelTest {
         Assert.assertEquals(realmModel.getName(), "JUGGLER");
         Assert.assertArrayEquals(realmModel.getPrivateKey().getEncoded(), keyPair.getPrivate().getEncoded());
         Assert.assertArrayEquals(realmModel.getPublicKey().getEncoded(), keyPair.getPublic().getEncoded());
-        Assert.assertEquals(2, realmModel.getDefaultRoles().size());
+        Assert.assertEquals(3, realmModel.getDefaultRoles().size());
         Assert.assertTrue(realmModel.getDefaultRoles().contains("foo"));
 
         realmModel.getId();
@@ -462,7 +462,7 @@ public class AdapterTest extends AbstractModelTest {
         realmModel.addRole("admin");
         realmModel.addRole("user");
         Set<RoleModel> roles = realmModel.getRoles();
-        Assert.assertEquals(4, roles.size());
+        Assert.assertEquals(5, roles.size());
         UserModel user = realmManager.getSession().users().addUser(realmModel, "bburke");
         RoleModel realmUserRole = realmModel.getRole("user");
         user.grantRole(realmUserRole);
@@ -488,7 +488,7 @@ public class AdapterTest extends AbstractModelTest {
         user.grantRole(application.getRole("user"));
 
         roles = user.getRealmRoleMappings();
-        Assert.assertEquals(roles.size(), 3);
+        Assert.assertEquals(4, roles.size());
         assertRolesContains(realmUserRole, roles);
         Assert.assertTrue(user.hasRole(realmUserRole));
         // Role "foo" is default realm role
@@ -503,13 +503,13 @@ public class AdapterTest extends AbstractModelTest {
         // Test that application role 'user' don't clash with realm role 'user'
         Assert.assertNotEquals(realmModel.getRole("user").getId(), application.getRole("user").getId());
 
-        Assert.assertEquals(7, user.getRoleMappings().size());
+        Assert.assertEquals(8, user.getRoleMappings().size());
 
         // Revoke some roles
         user.deleteRoleMapping(realmModel.getRole("foo"));
         user.deleteRoleMapping(appBarRole);
         roles = user.getRoleMappings();
-        Assert.assertEquals(5, roles.size());
+        Assert.assertEquals(6, roles.size());
         assertRolesContains(realmUserRole, roles);
         assertRolesContains(application.getRole("user"), roles);
         Assert.assertFalse(user.hasRole(appBarRole));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
index 14fde53..2537332 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
@@ -24,12 +24,11 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.jboss.logging.Logger;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.KeycloakSessionTask;
-import org.keycloak.services.managers.DBLockManager;
+import org.keycloak.models.dblock.DBLockManager;
 import org.keycloak.models.dblock.DBLockProvider;
 import org.keycloak.models.dblock.DBLockProviderFactory;
 import org.keycloak.models.utils.KeycloakModelUtils;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index bb7cbaf..dedc8d1 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -90,6 +90,12 @@ public class ImportTest extends AbstractModelTest {
     }
 
     // Moved to static method, so it's possible to test this from other places too (for example export-import tests)
+    /*
+    BIG HELPFUL HINT!!!!
+    WHEN YOU MIGRATE THIS CLASS YOU DO NOT NEED TO MIGRATE THIS METHOD.
+    IT HAS ALREADY BEEN IMPLEMENTED IN THE NEW ARQUILLIAN TESTSUTE.
+    SEE org.keycloak.testsuite.exportimport.ExportImportUtil
+    */
     public static void assertDataImportedInRealm(KeycloakSession session, RealmModel realm) {
         Assert.assertTrue(realm.isVerifyEmail());
         Assert.assertEquals(3600000, realm.getOfflineSessionIdleTimeout());
@@ -99,7 +105,7 @@ public class ImportTest extends AbstractModelTest {
         Assert.assertEquals(1, creds.size());
         RequiredCredentialModel cred = creds.get(0);
         Assert.assertEquals("password", cred.getFormLabel());
-        Assert.assertEquals(3, realm.getDefaultRoles().size());
+        Assert.assertEquals(4, realm.getDefaultRoles().size());
 
         Assert.assertNotNull(realm.getRole("foo"));
         Assert.assertNotNull(realm.getRole("bar"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 3da7d23..0a5842c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -28,7 +28,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.services.managers.UserManager;
 import org.keycloak.testsuite.rule.KeycloakRule;
@@ -471,10 +471,10 @@ public class UserSessionProviderTest {
 
     @Test
     public void loginFailures() {
-        UsernameLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1");
+        UserLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1");
         failure1.incrementFailures();
 
-        UsernameLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2");
+        UserLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2");
         failure2.incrementFailures();
         failure2.incrementFailures();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/Retry.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/Retry.java
index 441dcac..673d4fb 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/Retry.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/Retry.java
@@ -22,7 +22,7 @@ package org.keycloak.testsuite;
  */
 public class Retry {
 
-    public static void execute(Runnable runnable, int retry, long interval) throws InterruptedException {
+    public static void execute(Runnable runnable, int retry, long interval) {
         while (true) {
             try {
                 runnable.run();
@@ -30,7 +30,11 @@ public class Retry {
             } catch (RuntimeException e) {
                 retry--;
                 if (retry > 0) {
-                   Thread.sleep(interval);
+                    try {
+                        Thread.sleep(interval);
+                    } catch (InterruptedException ie) {
+                        throw new RuntimeException(ie);
+                    }
                 } else {
                     throw e;
                 }
diff --git a/testsuite/integration/src/test/resources/authorization-test/test-photoz-realm.json b/testsuite/integration/src/test/resources/authorization-test/test-photoz-realm.json
new file mode 100644
index 0000000..340576c
--- /dev/null
+++ b/testsuite/integration/src/test/resources/authorization-test/test-photoz-realm.json
@@ -0,0 +1,158 @@
+{
+  "realm": "photoz",
+  "enabled": true,
+  "accessTokenLifespan": 60,
+  "accessCodeLifespan": 60,
+  "accessCodeLifespanUserAction": 300,
+  "ssoSessionIdleTimeout": 600,
+  "ssoSessionMaxLifespan": 36000,
+  "sslRequired": "external",
+  "registrationAllowed": false,
+  "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+  "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "requiredCredentials": [
+    "password"
+  ],
+  "users": [
+    {
+      "username": "alice",
+      "enabled": true,
+      "email": "alice@keycloak.org",
+      "firstName": "Alice",
+      "lastName": "In Chains",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "alice"
+        }
+      ],
+      "realmRoles": [
+        "user"
+      ],
+      "clientRoles": {
+        "photoz-html5-client": [
+          "uma_authorization",
+          "kc_entitlement"
+        ]
+      }
+    },
+    {
+      "username": "jdoe",
+      "enabled": true,
+      "email": "jdoe@keycloak.org",
+      "firstName": "John",
+      "lastName": "Doe",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "jdoe"
+        }
+      ],
+      "realmRoles": [
+        "user"
+      ],
+      "clientRoles": {
+        "photoz-html5-client": [
+          "uma_authorization",
+          "kc_entitlement"
+        ]
+      }
+    },
+    {
+      "username": "admin",
+      "enabled": true,
+      "email": "admin@admin.com",
+      "firstName": "Admin",
+      "lastName": "Istrator",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "admin"
+        }
+      ],
+      "realmRoles": [
+        "user",
+        "admin"
+      ],
+      "clientRoles": {
+        "realm-management": [
+          "realm-admin"
+        ],
+        "photoz-html5-client": [
+          "uma_authorization",
+          "kc_entitlement"
+        ]
+      }
+    },
+    {
+      "username": "service-account-photoz-restful-api",
+      "enabled": true,
+      "email": "service-account-photoz-restful-api@placeholder.org",
+      "serviceAccountClientId": "photoz-restful-api",
+      "realmRoles": [
+        "uma_protection"
+      ]
+    }
+  ],
+  "roles": {
+    "realm": [
+      {
+        "name": "user",
+        "description": "User privileges"
+      },
+      {
+        "name": "admin",
+        "description": "Administrator privileges"
+      },
+      {
+        "name": "uma_protection",
+        "description": "Allows access to the Protection API"
+      }
+    ]
+  },
+  "clients": [
+    {
+      "clientId": "photoz-html5-client",
+      "enabled": true,
+      "adminUrl": "/photoz-html5-client",
+      "baseUrl": "/photoz-html5-client",
+      "publicClient": true,
+      "redirectUris": [
+        "/photoz-html5-client/*"
+      ]
+    },
+    {
+      "clientId": "photoz-restful-api",
+      "enabled": true,
+      "publicClient": false,
+      "baseUrl": "/photoz-restful-api",
+      "bearerOnly": false,
+      "serviceAccountsEnabled": true,
+      "redirectUris": [
+        "/photoz-restful-api/*"
+      ],
+      "secret": "secret"
+    },
+    {
+      "clientId": "public-client-01",
+      "enabled": true,
+      "publicClient": true,
+      "baseUrl": "public-client-01",
+      "redirectUris": [
+        "/public-client-01/*"
+      ]
+    },
+    {
+      "clientId": "confidential-no-service-account",
+      "enabled": true,
+      "publicClient": false,
+      "baseUrl": "/confidential-no-service-account",
+      "bearerOnly": false,
+      "serviceAccountsEnabled": false,
+      "redirectUris": [
+        "/confidential-no-service-account/*"
+      ],
+      "secret": "secret"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties
index f093692..c2bc22c 100755
--- a/testsuite/integration/src/test/resources/log4j.properties
+++ b/testsuite/integration/src/test/resources/log4j.properties
@@ -49,7 +49,7 @@ log4j.logger.org.keycloak.connections.jpa.updater.liquibase=${keycloak.liquibase
 
 # Enable to view database updates
 # log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug
-# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
+log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=${keycloak.liquibase.logging.level}
 # log4j.logger.org.keycloak.migration.MigrationModelManager=debug
 
 # Enable to view hibernate statistics
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 1c63a35..16ded24 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -26,6 +26,10 @@
         "provider": "${keycloak.userSessionPersister.provider:jpa}"
     },
 
+    "authorizationPersister": {
+        "provider": "${keycloak.authorization.provider:jpa}"
+    },
+
     "userCache": {
         "default" : {
             "enabled": true
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/security-eap6.xsl b/testsuite/integration-arquillian/servers/app-server/jboss/common/security-eap6.xsl
index b389cd1..a902fcb 100644
--- a/testsuite/integration-arquillian/servers/app-server/jboss/common/security-eap6.xsl
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/security-eap6.xsl
@@ -17,7 +17,7 @@
 
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:xalan="http://xml.apache.org/xalan"
-                xmlns:j="urn:jboss:domain:1.7"
+                xmlns:j="urn:jboss:domain:1.8"
                 xmlns:w="urn:jboss:domain:web:2.2"
                 version="2.0"
                 exclude-result-prefixes="xalan j ds k sec">
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index ef8a64d..50ec539 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -161,6 +161,20 @@
                                     </target>
                                 </configuration>
                             </execution>
+                            <execution>
+                                <id>inject-truststore-into-keycloak-server-json</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                                <configuration>
+                                    <target>
+                                        <ant antfile="../build-truststore.xml" inheritRefs="true">
+                                            <target name="inject-truststore"/>
+                                        </ant>
+                                    </target>
+                                </configuration>
+                            </execution>
                         </executions>
                         <dependencies>
                             <dependency>
@@ -257,6 +271,25 @@
                                     <overwrite>true</overwrite>
                                 </configuration>
                             </execution>
+                            <execution>
+                                <id>copy-keystore</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${auth.server.home}/standalone/configuration</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${common.resources}/keystore</directory>
+                                            <includes>
+                                                <include>keycloak.jks</include>
+                                                <include>keycloak.truststore</include>
+                                            </includes>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
                         </executions>
                     </plugin>
                     <plugin>
@@ -615,12 +648,12 @@
                 <session.cache.owners>1</session.cache.owners>
                 <offline.session.cache.owners>1</offline.session.cache.owners>
                 <login.failure.cache.owners>1</login.failure.cache.owners>
-                
+
                 <load.metric>simple</load.metric>
                 <!-- The default value 'simple' configures mod-cluster with simple-load-provider.
                 Any other value configures it with dynamic-load-provider using the particular `load.metric`.
                 Supported metrics: https://docs.jboss.org/mod_cluster/1.2.0/html/java.AS7config.html#LoadMetric -->
-                
+
             </properties>
             <build>
                 <pluginManagement>
@@ -723,7 +756,7 @@
                 </pluginManagement>
             </build>
         </profile>
-        
+
         <profile>
             <id>admin</id>
             <build>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java
new file mode 100644
index 0000000..117b928
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/CompanyRepresentation.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension;
+
+
+import org.keycloak.testsuite.domainextension.jpa.Company;
+
+public class CompanyRepresentation {
+
+    private String id;
+    private String name;
+
+    public CompanyRepresentation() {
+    }
+
+    public CompanyRepresentation(Company company) {
+        id = company.getId();
+        name = company.getName();
+    }
+    
+    public String getId() {
+		return id;
+	}
+    
+    public String getName() {
+		return name;
+	}
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/Company.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/Company.java
new file mode 100644
index 0000000..d5f95ae
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/Company.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.jpa;
+
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "EXAMPLE_COMPANY")
+@NamedQueries({ @NamedQuery(name = "findByRealm", query = "from Company where realmId = :realmId") })
+public class Company {
+
+    @Id
+    @Column(name = "ID")
+    private String id;
+
+    @Column(name = "NAME", nullable = false)
+    private String name;
+
+    @Column(name = "REALM_ID", nullable = false)
+    private String realmId;
+
+    public String getId() {
+		return id;
+	}
+    
+    public String getRealmId() {
+        return realmId;
+    }
+    
+    public String getName() {
+		return name;
+	}
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProvider.java
new file mode 100644
index 0000000..2f87632
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.jpa;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Example JpaEntityProvider.
+ */
+public class ExampleJpaEntityProvider implements JpaEntityProvider {
+
+    @Override
+    public List<Class<?>> getEntities() {
+        return Collections.<Class<?>>singletonList(Company.class);
+    }
+
+    @Override
+    public String getChangelogLocation() {
+    	return "META-INF/example-changelog.xml";
+    }
+    
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getFactoryId() {
+        return ExampleJpaEntityProviderFactory.ID;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProviderFactory.java
new file mode 100644
index 0000000..83dc0a7
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/jpa/ExampleJpaEntityProviderFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.jpa;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
+import org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
+ * 
+ * Example JpaEntityProviderFactory.
+ */
+public class ExampleJpaEntityProviderFactory implements JpaEntityProviderFactory {
+
+	protected static final String ID = "example-entity-provider";
+	
+    @Override
+    public JpaEntityProvider create(KeycloakSession session) {
+        return new ExampleJpaEntityProvider();
+    }
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public void init(Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/CompanyResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/CompanyResource.java
new file mode 100644
index 0000000..197cbce
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/CompanyResource.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.rest;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.testsuite.domainextension.CompanyRepresentation;
+import org.keycloak.testsuite.domainextension.spi.ExampleService;
+
+public class CompanyResource {
+
+	private final KeycloakSession session;
+	
+	public CompanyResource(KeycloakSession session) {
+		this.session = session;
+	}
+
+    @GET
+    @Path("")
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<CompanyRepresentation> getCompanies() {
+        return session.getProvider(ExampleService.class).listCompanies();
+    }
+
+    @DELETE
+    @Path("")
+    @NoCache
+    public void deleteAllCompanies() {
+        session.getProvider(ExampleService.class).deleteAllCompanies();
+    }
+
+    @POST
+    @Path("")
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createCompany(CompanyRepresentation rep) {
+        session.getProvider(ExampleService.class).addCompany(rep);
+        return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(rep.getId()).build()).build();
+    }
+
+    @GET
+    @NoCache
+    @Path("{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public CompanyRepresentation getCompany(@PathParam("id") final String id) {
+        return session.getProvider(ExampleService.class).findCompany(id);
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProvider.java
new file mode 100644
index 0000000..ff9ad02
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.rest;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.resource.RealmResourceProvider;
+
+public class ExampleRealmResourceProvider implements RealmResourceProvider {
+
+    private KeycloakSession session;
+
+    public ExampleRealmResourceProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @Override
+    public Object getResource() {
+        return new ExampleRestResource(session);
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProviderFactory.java
new file mode 100644
index 0000000..ae016b6
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRealmResourceProviderFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.rest;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.services.resource.RealmResourceProvider;
+import org.keycloak.services.resource.RealmResourceProviderFactory;
+
+public class ExampleRealmResourceProviderFactory implements RealmResourceProviderFactory {
+
+    public static final String ID = "example";
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public RealmResourceProvider create(KeycloakSession session) {
+        return new ExampleRealmResourceProvider(session);
+    }
+
+    @Override
+    public void init(Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRestResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRestResource.java
new file mode 100644
index 0000000..629acc8
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/rest/ExampleRestResource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.rest;
+
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.Path;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+
+public class ExampleRestResource {
+
+	private final KeycloakSession session;
+    private final AuthenticationManager.AuthResult auth;
+	
+	public ExampleRestResource(KeycloakSession session) {
+		this.session = session;
+        this.auth = new AppAuthManager().authenticateBearerToken(session, session.getContext().getRealm());
+	}
+	
+    @Path("companies")
+    public CompanyResource getCompanyResource() {
+        return new CompanyResource(session);
+    }
+
+    // Same like "companies" endpoint, but REST endpoint is authenticated with Bearer token and user must be in realm role "admin"
+    // Just for illustration purposes
+    @Path("companies-auth")
+    public CompanyResource getCompanyResourceAuthenticated() {
+        checkRealmAdmin();
+        return new CompanyResource(session);
+    }
+
+    private void checkRealmAdmin() {
+        if (auth == null) {
+            throw new NotAuthorizedException("Bearer");
+        } else if (auth.getToken().getRealmAccess() == null || !auth.getToken().getRealmAccess().isUserInRole("admin")) {
+            throw new ForbiddenException("Does not have realm admin role");
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleService.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleService.java
new file mode 100644
index 0000000..dd47d96
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.spi;
+
+import java.util.List;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.testsuite.domainextension.CompanyRepresentation;
+
+public interface ExampleService extends Provider {
+
+    List<CompanyRepresentation> listCompanies();
+
+    CompanyRepresentation findCompany(String id);
+
+    CompanyRepresentation addCompany(CompanyRepresentation company);
+
+    void deleteAllCompanies();
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleServiceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleServiceProviderFactory.java
new file mode 100644
index 0000000..ee802b0
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleServiceProviderFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.spi;
+
+import org.keycloak.provider.ProviderFactory;
+
+public interface ExampleServiceProviderFactory extends ProviderFactory<ExampleService> {
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleSpi.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleSpi.java
new file mode 100644
index 0000000..d85f043
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/ExampleSpi.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.spi;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+public class ExampleSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "example";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return ExampleService.class;
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return ExampleServiceProviderFactory.class;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceImpl.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceImpl.java
new file mode 100644
index 0000000..213f7e5
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceImpl.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.spi.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.testsuite.domainextension.CompanyRepresentation;
+import org.keycloak.testsuite.domainextension.jpa.Company;
+import org.keycloak.testsuite.domainextension.spi.ExampleService;
+
+public class ExampleServiceImpl implements ExampleService {
+
+    private final KeycloakSession session;
+
+    public ExampleServiceImpl(KeycloakSession session) {
+        this.session = session;
+        if (getRealm() == null) {
+            throw new IllegalStateException("The service cannot accept a session without a realm in it's context.");
+        }
+    }
+
+    private EntityManager getEntityManager() {
+        return session.getProvider(JpaConnectionProvider.class).getEntityManager();
+    }
+
+    protected RealmModel getRealm() {
+        return session.getContext().getRealm();
+    }
+    
+    @Override
+    public List<CompanyRepresentation> listCompanies() {
+    	List<Company> companyEntities = getEntityManager().createNamedQuery("findByRealm", Company.class)
+                .setParameter("realmId", getRealm().getId())
+                .getResultList();
+
+        List<CompanyRepresentation> result = new LinkedList<>();
+        for (Company entity : companyEntities) {
+            result.add(new CompanyRepresentation(entity));
+        }
+        return result;
+    }
+    
+    @Override
+    public CompanyRepresentation findCompany(String id) {
+    	Company entity = getEntityManager().find(Company.class, id);
+        return entity==null ? null : new CompanyRepresentation(entity);
+    }
+    
+    @Override
+    public CompanyRepresentation addCompany(CompanyRepresentation company) {
+        Company entity = new Company();
+        String id = company.getId()==null ?  KeycloakModelUtils.generateId() : company.getId();
+        entity.setId(id);
+        entity.setName(company.getName());
+        entity.setRealmId(getRealm().getId());
+        getEntityManager().persist(entity);
+
+        company.setId(id);
+        return company;
+    }
+
+    @Override
+    public void deleteAllCompanies() {
+        EntityManager em = getEntityManager();
+        List<Company> companyEntities = em.createNamedQuery("findByRealm", Company.class)
+                .setParameter("realmId", getRealm().getId())
+                .getResultList();
+
+        for (Company entity : companyEntities) {
+            em.remove(entity);
+        }
+    }
+
+    public void close() {
+        // Nothing to do.
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java
new file mode 100644
index 0000000..c772827
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/domainextension/spi/impl/ExampleServiceProviderFactoryImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension.spi.impl;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.testsuite.domainextension.spi.ExampleService;
+import org.keycloak.testsuite.domainextension.spi.ExampleServiceProviderFactory;
+
+public class ExampleServiceProviderFactoryImpl implements ExampleServiceProviderFactory {
+
+    @Override
+    public ExampleService create(KeycloakSession session) {
+        return new ExampleServiceImpl(session);
+    }
+
+    @Override
+    public void init(Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "exampleServiceImpl";
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
index 4bdf05f..e484645 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
@@ -61,7 +61,19 @@ import org.keycloak.events.EventType;
 import org.keycloak.events.admin.AdminEventQuery;
 import org.keycloak.events.admin.AuthDetails;
 import org.keycloak.events.admin.OperationType;
+import org.keycloak.exportimport.ExportImportManager;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.RealmProvider;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
 import org.keycloak.representations.idm.AuthDetailsRepresentation;
+import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -253,7 +265,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public List<EventRepresentation> queryEvents(@QueryParam("realmId") String realmId, @QueryParam("type") List<String> types, @QueryParam("client") String client,
-            @QueryParam("user") String user, @QueryParam("dateFrom") Date dateFrom, @QueryParam("dateTo") Date dateTo,
+            @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
             @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
             @QueryParam("max") Integer maxResults) {
 
@@ -282,11 +294,13 @@ public class TestingResourceProvider implements RealmResourceProvider {
         }
 
         if(dateFrom != null) {
-            query.fromDate(dateFrom);
+            Date from = formatDate(dateFrom, "Date(From)");
+            query.fromDate(from);
         }
 
         if(dateTo != null) {
-            query.toDate(dateTo);
+            Date to = formatDate(dateTo, "Date(To)");
+            query.toDate(to);
         }
 
         if (ipAddress != null) {
@@ -424,26 +438,12 @@ public class TestingResourceProvider implements RealmResourceProvider {
         }
 
         if(dateFrom != null) {
-            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-            Date from;
-            try {
-                from = df.parse(dateFrom);
-            } catch (ParseException e) {
-                throw new BadRequestException("Invalid value for 'Date(From)', expected format is yyyy-MM-dd");
-            }
-
+            Date from = formatDate(dateFrom, "Date(From)");
             query.fromTime(from);
         }
 
         if(dateTo != null) {
-            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-            Date to;
-            try {
-                to = df.parse(dateTo);
-            } catch (ParseException e) {
-                throw new BadRequestException("Invalid value for 'Date(To)', expected format is yyyy-MM-dd");
-            }
-
+            Date to = formatDate(dateTo, "Date(To)");
             query.toTime(to);
         }
 
@@ -461,6 +461,15 @@ public class TestingResourceProvider implements RealmResourceProvider {
         return toAdminEventRep(query.getResultList());
     }
 
+    private Date formatDate(String date, String paramName) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            return df.parse(date);
+        } catch (ParseException e) {
+            throw new BadRequestException("Invalid value for '" + paramName + "', expected format is yyyy-MM-dd");
+        }
+    }
+
     private List<AdminEventRepresentation> toAdminEventRep(List<AdminEvent> events) {
         List<AdminEventRepresentation> reps = new ArrayList<>();
         for (AdminEvent event : events) {
@@ -547,4 +556,93 @@ public class TestingResourceProvider implements RealmResourceProvider {
         result.setUsername(PassThroughAuthenticator.username);
         return result;
     }
+
+    @GET
+    @Path("/run-import")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runImport() {
+        new ExportImportManager(session).runImport();
+        return Response.ok().build();
+    }
+
+    @GET
+    @Path("/run-export")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runExport() {
+        new ExportImportManager(session).runExport();
+        return Response.ok().build();
+    }
+
+    @GET
+    @Path("/valid-credentials")
+    @Produces(MediaType.APPLICATION_JSON)
+    public boolean validCredentials(@QueryParam("realmName") String realmName, @QueryParam("userName") String userName, @QueryParam("password") String password) {
+        RealmModel realm = session.realms().getRealm(realmName);
+        if (realm == null) return false;
+        UserProvider userProvider = session.getProvider(UserProvider.class);
+        UserModel user = userProvider.getUserByUsername(userName, realm);
+        return userProvider.validCredentials(session, realm, user, UserCredentialModel.password(password));
+    }
+
+    @GET
+    @Path("/user-by-federated-identity")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByFederatedIdentity(@QueryParam("realmName") String realmName,
+                                                         @QueryParam("identityProvider") String identityProvider,
+                                                         @QueryParam("userId") String userId,
+                                                         @QueryParam("userName") String userName) {
+        RealmModel realm = getRealmByName(realmName);
+        UserModel foundFederatedUser = session.users().getUserByFederatedIdentity(new FederatedIdentityModel(identityProvider, userId, userName), realm);
+        if (foundFederatedUser == null) return null;
+        return ModelToRepresentation.toRepresentation(foundFederatedUser);
+    }
+
+    @GET
+    @Path("/user-by-username-from-fed-factory")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByUsernameFromFedProviderFactory(@QueryParam("realmName") String realmName,
+                                                                      @QueryParam("userName") String userName) {
+        RealmModel realm = getRealmByName(realmName);
+        UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, "dummy");
+        UserModel user = factory.getInstance(session, null).getUserByUsername(realm, userName);
+        if (user == null) return null;
+        return ModelToRepresentation.toRepresentation(user);
+    }
+
+    @GET
+    @Path("/get-client-auth-flow")
+    @Produces(MediaType.APPLICATION_JSON)
+    public AuthenticationFlowRepresentation getClientAuthFlow(@QueryParam("realmName") String realmName) {
+        RealmModel realm = getRealmByName(realmName);
+        AuthenticationFlowModel flow = realm.getClientAuthenticationFlow();
+        if (flow == null) return null;
+        return ModelToRepresentation.toRepresentation(realm, flow);
+    }
+
+    @GET
+    @Path("/get-reset-cred-flow")
+    @Produces(MediaType.APPLICATION_JSON)
+    public AuthenticationFlowRepresentation getResetCredFlow(@QueryParam("realmName") String realmName) {
+        RealmModel realm = getRealmByName(realmName);
+        AuthenticationFlowModel flow = realm.getResetCredentialsFlow();
+        if (flow == null) return null;
+        return ModelToRepresentation.toRepresentation(realm, flow);
+    }
+
+    @GET
+    @Path("/get-user-by-service-account-client")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByServiceAccountClient(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId) {
+        RealmModel realm = getRealmByName(realmName);
+        ClientModel client =  realm.getClientByClientId(clientId);
+        UserModel user = session.users().getUserByServiceAccountClient(client);
+        if (user == null) return null;
+        return ModelToRepresentation.toRepresentation(user);
+    }
+
+    private RealmModel getRealmByName(String realmName) {
+        RealmProvider realmProvider = session.getProvider(RealmProvider.class);
+        return realmProvider.getRealmByName(realmName);
+    }
+
 }
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/example-changelog.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/example-changelog.xml
new file mode 100644
index 0000000..5edd719
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/example-changelog.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+    <changeSet author="erik.mulder@docdatapayments.com" id="example-1.0">
+
+        <createTable tableName="EXAMPLE_COMPANY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey
+            constraintName="PK_COMPANY"
+            tableName="EXAMPLE_COMPANY"
+            columnNames="ID"
+        />
+
+    </changeSet>
+    
+</databaseChangeLog>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory
new file mode 100644
index 0000000..67464bd
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.testsuite.domainextension.jpa.ExampleJpaEntityProviderFactory
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..1c31c00
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.testsuite.domainextension.spi.ExampleSpi
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
index 8823682..1958ff0 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
@@ -16,4 +16,5 @@
 #
 
 org.keycloak.testsuite.rest.TestingResourceProviderFactory
-org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory
+org.keycloak.testsuite.domainextension.rest.ExampleRealmResourceProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.testsuite.domainextension.spi.ExampleServiceProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.testsuite.domainextension.spi.ExampleServiceProviderFactory
new file mode 100644
index 0000000..d58236f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.testsuite.domainextension.spi.ExampleServiceProviderFactory
@@ -0,0 +1,18 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.testsuite.domainextension.spi.impl.ExampleServiceProviderFactoryImpl
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
index 92b57f2..28e4789 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml
@@ -30,7 +30,12 @@
         <module name="org.keycloak.keycloak-server-spi"/>
         <module name="org.keycloak.keycloak-services"/>
         <module name="org.keycloak.keycloak-model-infinispan"/>
+        <module name="org.keycloak.keycloak-model-jpa"/>
         <module name="org.infinispan"/>
         <module name="org.jboss.logging"/>
+        <module name="org.jboss.resteasy.resteasy-jaxrs"/>
+        <module name="javax.persistence.api"/>
+        <module name="org.hibernate"/>
+        <module name="org.javassist"/>
     </dependencies>
-</module>
\ No newline at end of file
+</module>
diff --git a/testsuite/integration-arquillian/test-apps/js-console/example-realm.json b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
index 3bc609e..4e3adca 100755
--- a/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
+++ b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
@@ -21,6 +21,20 @@
             "clientRoles": {
                 "account": ["view-profile", "manage-account"]
             }
+        },{
+            "username" : "unauthorized",
+            "enabled": true,
+            "email" : "sample-user2@example",
+            "firstName": "Sample",
+            "lastName": "User",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ],
+            "realmRoles": [],
+            "clientRoles": {
+                "account": ["view-profile", "manage-account"]
+            }
         }
     ],
     "roles" : {
@@ -54,6 +68,12 @@
                 "http://localhost:8280",
                 "https://localhost:8643"
             ]
+        },{
+            "clientId": "js-database",
+            "enabled": true,
+            "adminUrl": "/database",
+            "baseUrl": "/database",
+            "bearerOnly": true
         }
     ],
     "clientScopeMappings": {
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
index 04d7a8e..cff5862 100755
--- a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
@@ -39,6 +39,7 @@
     <button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
     <button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
     <button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
+    <button onclick="createBearerRequest()">Create Bearer Request</button>
     <select id="flowSelect">
         <option value="standard">standard</option>
         <option value="implicit">implicit</option>
@@ -48,6 +49,10 @@
         <option value="fragment">fragment</option>
         <option value="query">query</option>
     </select>
+    <select id="onLoad">
+        <option value="check-sso">check-sso</option>
+        <option value="login-required">login-required</option>
+    </select>
 </div>
 
 <h2>Result</h2>
@@ -115,6 +120,34 @@
         document.getElementById('events').innerHTML = new Date().toLocaleString() + "\t" + event + "\n" + e;
     }
 
+    function createBearerRequest() {
+
+        var url = 'http://localhost:8280/js-database/customers';
+        if (window.location.href.indexOf("8643") > -1) {
+            url = url.replace("8280","8643");
+            url = url.replace("http","https");
+        }
+
+        var req = new XMLHttpRequest();
+        req.open('GET', url, true);
+        req.setRequestHeader('Accept', 'application/json');
+        req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
+
+        req.onreadystatechange = function () {
+            if (req.readyState == 4) {
+                if (req.status == 200) {
+                    output(req.responseText);
+                } else if (req.status == 403) {
+                    output('Forbidden');
+                } else if (req.status == 401) {
+                    output('Unauthorized');
+                }
+            }
+        };
+
+        req.send();
+    }
+
     var keycloak;
 
     function keycloakInit() {
@@ -144,7 +177,7 @@
             event('Access token expired.');
         };
 
-        var initOptions = {flow: document.getElementById("flowSelect").value, responseMode:  document.getElementById("responseModeSelect").value}
+        var initOptions = {onLoad: document.getElementById("onLoad").value, flow: document.getElementById("flowSelect").value, responseMode:  document.getElementById("responseModeSelect").value};
         keycloak.init(initOptions).success(function (authenticated) {
             output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
         }).error(function () {
diff --git a/testsuite/integration-arquillian/test-apps/js-database/pom.xml b/testsuite/integration-arquillian/test-apps/js-database/pom.xml
new file mode 100644
index 0000000..24e20c7
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-database/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>integration-arquillian-test-apps</artifactId>
+        <groupId>org.keycloak.testsuite</groupId>
+        <version>2.0.0.CR1-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>integration-arquillian-test-apps-js-database</artifactId>
+    <name>JAX-RS Database Service Using OAuth Bearer Tokens</name>
+    <packaging>war</packaging>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>js-database</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/CustomerService.java b/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/CustomerService.java
new file mode 100755
index 0000000..d2b193e
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/CustomerService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.example.oauth;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.representations.AccessToken;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("customers")
+public class CustomerService {
+
+    @Context
+    private HttpRequest httpRequest;
+
+    @GET
+    @Produces("application/json")
+    @NoCache
+    public List<String> getCustomers() {
+        // Just to show how to user info from access token in REST endpoint
+        KeycloakSecurityContext securityContext = (KeycloakSecurityContext) httpRequest.getAttribute(KeycloakSecurityContext.class.getName());
+        AccessToken accessToken = securityContext.getToken();
+        System.out.println(String.format("User '%s' with email '%s' made request to CustomerService REST endpoint", accessToken.getPreferredUsername(), accessToken.getEmail()));
+
+        ArrayList<String> rtn = new ArrayList<String>();
+        rtn.add("Bill Burke");
+        rtn.add("Stian Thorgersen");
+        rtn.add("Stan Silvert");
+        rtn.add("Gabriel Cardoso");
+        rtn.add("Viliam Rockai");
+        rtn.add("Marek Posolda");
+        rtn.add("Boleslaw Dawidowicz");
+        return rtn;
+    }
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/DataApplication.java b/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/DataApplication.java
new file mode 100755
index 0000000..6456904
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-database/src/main/java/org/keycloak/example/oauth/DataApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.example.oauth;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@ApplicationPath("/")
+public class DataApplication extends Application
+{
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/keycloak.json
new file mode 100755
index 0000000..9f94148
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm" : "example",
+  "resource" : "js-database",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8180/auth",
+  "bearer-only" : true,
+  "ssl-required" : "external"
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..599f667
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-database/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+	<module-name>js-database</module-name>
+	
+    <security-constraint>
+        <web-resource-collection>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+<!--        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>  -->
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK</auth-method>
+        <realm-name>example</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml
index 12c89a0..5f13130 100644
--- a/testsuite/integration-arquillian/test-apps/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/pom.xml
@@ -17,5 +17,6 @@
     <modules>
         <module>js-console</module>
         <module>test-apps-dist</module>
+        <module>js-database</module>
     </modules>
 </project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index ee6ad1b..11cf4d6 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -68,6 +68,11 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>org.subethamail</groupId>
+            <artifactId>subethasmtp</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
             <groupId>com.icegreen</groupId>
             <artifactId>greenmail</artifactId>
             <scope>compile</scope>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java
new file mode 100644
index 0000000..c282dff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.filter;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.keycloak.common.util.Time;
+
+/**
+ * Filter to handle "special" requests to perform actions on adapter side (for example setting time offset )
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AdapterActionsFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletResponse servletResp = (HttpServletResponse) response;
+
+        //Accept timeOffset as argument to enforce timeouts
+        String timeOffsetParam = request.getParameter("timeOffset");
+        if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) {
+            Time.setOffset(Integer.parseInt(timeOffsetParam));
+        }
+
+        // Continue request
+        chain.doFilter(request, response);
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+
+    private void writeResponse(HttpServletResponse response, String responseText) throws IOException {
+        PrintWriter writer = response.getWriter();
+        writer.println(responseText);
+        writer.flush();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java
new file mode 100644
index 0000000..d370dd0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import java.io.IOException;
+
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.keycloak.util.JsonSerialization;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl {
+
+    @FindBy(id = "accessToken")
+    private WebElement accessToken;
+
+    @FindBy(id = "refreshToken")
+    private WebElement refreshToken;
+
+
+    public AccessToken getAccessToken() {
+        try {
+            return JsonSerialization.readValue(accessToken.getText(), AccessToken.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    public RefreshToken getRefreshToken() {
+        try {
+            return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSDatabaseTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSDatabaseTestApp.java
new file mode 100644
index 0000000..83afdb2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSDatabaseTestApp.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+import java.net.URL;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class JSDatabaseTestApp extends AbstractPageWithInjectedUrl {
+
+    public static final String DEPLOYMENT_NAME = "js-database-example";
+    public static final String CLIENT_ID = "integration-arquillian-test-apps-js-database";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        //EAP6 URL fix
+        URL fixedUrl = createInjectedURL("js-database");
+        return fixedUrl != null ? fixedUrl : url;
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/OfflineToken.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/OfflineToken.java
index 9df0ab9..248225e 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/OfflineToken.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/OfflineToken.java
@@ -19,7 +19,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 /**
  * @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>.
  */
-public class OfflineToken extends AbstractPageWithInjectedUrl {
+public class OfflineToken extends AbstractShowTokensPage {
 
     public static final String DEPLOYMENT_NAME = "offline-client";
 
@@ -32,35 +32,6 @@ public class OfflineToken extends AbstractPageWithInjectedUrl {
         return url;
     }
 
-    @FindBy(id = "accessToken")
-    private WebElement accessToken;
-
-    @FindBy(id = "refreshToken")
-    private WebElement refreshToken;
-
-    @FindBy(id = "prettyToken")
-    private WebElement prettyToken;
-
-
-    public AccessToken getAccessToken() {
-        try {
-            return JsonSerialization.readValue(accessToken.getText(), AccessToken.class);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-        return null;
-    }
-
-    public RefreshToken getRefreshToken() {
-        try {
-            return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
     public void logout() {
         log.info("Logging out, navigating to: " + getUriBuilder().path("/logout").build().toASCIIString());
         driver.navigate().to(getUriBuilder().path("/logout").build().toASCIIString());
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/TokenMinTTLPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/TokenMinTTLPage.java
new file mode 100644
index 0000000..797ab6c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/TokenMinTTLPage.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TokenMinTTLPage extends AbstractShowTokensPage {
+
+    public static final String DEPLOYMENT_NAME = "token-min-ttl";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/AbstractShowTokensServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/AbstractShowTokensServlet.java
new file mode 100644
index 0000000..403c70e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/AbstractShowTokensServlet.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractShowTokensServlet extends HttpServlet {
+
+    private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+    protected String renderTokens(HttpServletRequest req)  throws ServletException, IOException {
+        RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
+        String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
+        RefreshToken refreshToken;
+        try {
+            refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
+        } catch (JWSInputException e) {
+            throw new IOException(e);
+        }
+        String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
+
+        return new StringBuilder("<span id=\"accessToken\">" + accessTokenPretty + "</span>")
+                .append("<span id=\"refreshToken\">" + refreshTokenPretty + "</span>")
+                .toString();
+    }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/OfflineTokenServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/OfflineTokenServlet.java
index 030bee3..c36b30e 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/OfflineTokenServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/OfflineTokenServlet.java
@@ -3,14 +3,12 @@ package org.keycloak.testsuite.adapter.servlet;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
-import org.keycloak.common.util.Time;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.representations.RefreshToken;
 import org.keycloak.util.JsonSerialization;
 
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.UriBuilder;
@@ -19,7 +17,7 @@ import java.io.IOException;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class OfflineTokenServlet extends HttpServlet {
+public class OfflineTokenServlet extends AbstractShowTokensServlet {
 
     private static final String OFFLINE_CLIENT_APP_URI = (System.getProperty("app.server.ssl.required", "false").equals("true")) ?
             System.getProperty("app.server.ssl.base.url", "https://localhost:8643") + "/offline-client" :
@@ -31,12 +29,6 @@ public class OfflineTokenServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
-        //Accept timeOffset as argument to enforce timeouts
-        String timeOffsetParam = req.getParameter("timeOffset");
-        if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) {
-            Time.setOffset(Integer.parseInt(timeOffsetParam));
-        }
-
         if (req.getRequestURI().endsWith("logout")) {
 
             UriBuilder redirectUriBuilder = UriBuilder.fromUri(OFFLINE_CLIENT_APP_URI);
@@ -54,19 +46,11 @@ public class OfflineTokenServlet extends HttpServlet {
         }
 
         StringBuilder response = new StringBuilder("<html><head><title>Offline token servlet</title></head><body><pre>");
-        RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
-        String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
-        RefreshToken refreshToken;
-        try {
-            refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
-        } catch (JWSInputException e) {
-            throw new IOException(e);
-        }
-        String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
 
-        response = response.append("<span id=\"accessToken\">" + accessTokenPretty + "</span>")
-                .append("<span id=\"refreshToken\">" + refreshTokenPretty + "</span>")
-                .append("</pre></body></html>");
+        String tokens = renderTokens(req);
+        response = response.append(tokens);
+
+        response.append("</pre></body></html>");
         resp.getWriter().println(response.toString());
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
index e4a8ab2..8ea9a49 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -26,6 +26,7 @@ import org.jboss.logging.Logger.Level;
 import org.jboss.shrinkwrap.api.Archive;
 import org.jboss.shrinkwrap.api.asset.StringAsset;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.representations.adapters.config.BaseAdapterConfig;
 import org.keycloak.testsuite.adapter.AdapterLibsMode;
 import org.keycloak.testsuite.util.IOUtil;
@@ -118,8 +119,8 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
                 }
             } else { // OIDC adapter config
                 try {
-                    BaseAdapterConfig adapterConfig = loadJson(archive.get(adapterConfigPath)
-                            .getAsset().openStream(), BaseAdapterConfig.class);
+                    AdapterConfig adapterConfig = loadJson(archive.get(adapterConfigPath)
+                            .getAsset().openStream(), AdapterConfig.class);
 
                     log.info(" setting " + (relative ? "" : "non-") + "relative auth-server-url");
                     if (relative) {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
index 424705c..5631b6a 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
@@ -24,6 +24,7 @@ import org.jboss.arquillian.core.api.annotation.Inject;
 import org.jboss.arquillian.test.api.ArquillianResource;
 import org.jboss.logging.Logger;
 import org.jboss.logging.Logger.Level;
+import org.keycloak.testsuite.arquillian.SuiteContext;
 import org.keycloak.testsuite.arquillian.TestContext;
 import org.keycloak.testsuite.arquillian.annotation.AppServerContext;
 import org.keycloak.testsuite.arquillian.annotation.AuthServerContext;
@@ -33,7 +34,6 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashSet;
 import java.util.Set;
-import org.keycloak.testsuite.arquillian.SuiteContext;
 
 public class URLProvider extends URLResourceProvider {
 
@@ -83,7 +83,8 @@ public class URLProvider extends URLResourceProvider {
                     for (Annotation a : qualifiers) {
                         if (OperateOnDeployment.class.isAssignableFrom(a.annotationType())) {
                             String port = appServerSslRequired ?  System.getProperty("app.server.https.port", "8643"):System.getProperty("app.server.http.port", "8280");
-                            url = new URL(fixedUrl.toExternalForm().replace("8080", port) + "/" + ((OperateOnDeployment) a).value());
+                            String protocol = appServerSslRequired ? "https" : "http";
+                            url = new URL(fixedUrl.toExternalForm().replace("8080", port).replace("http", protocol) + ((OperateOnDeployment) a).value());
                         }
                     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
index e566b41..848a9fa 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
@@ -28,9 +28,17 @@ public class VerifyEmail extends Authenticate {
 
     @FindBy(xpath = "//div[@id='kc-form-wrapper']/p")
     private WebElement instruction;
-    
+
+    @FindBy(id = "kc-error-message")
+    private WebElement error;
+
     public String getInstructionMessage() {
         waitUntilElement(instruction).is().present();
         return instruction.getText();
     }
+
+    public String getErrorMessage() {
+        waitUntilElement(error).is().present();
+        return error.getText();
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
index 46f7b23..dedc08d 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
@@ -21,6 +21,7 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient;
 import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
 import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
 import org.keycloak.testsuite.client.resources.TestApplicationResource;
+import org.keycloak.testsuite.client.resources.TestExampleCompanyResource;
 import org.keycloak.testsuite.client.resources.TestingResource;
 
 /**
@@ -45,6 +46,8 @@ public class KeycloakTestingClient {
 
     public TestApplicationResource testApp() { return target.proxy(TestApplicationResource.class); }
 
+    public TestExampleCompanyResource testExampleCompany() { return target.proxy(TestExampleCompanyResource.class); }
+
     public void close() {
         client.close();
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java
new file mode 100644
index 0000000..00d6e28
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestExampleCompanyResource.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.client.resources;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.keycloak.testsuite.domainextension.CompanyRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Path("/realms/{realmName}/example/companies")
+@Consumes(MediaType.APPLICATION_JSON)
+public interface TestExampleCompanyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    List<CompanyRepresentation> getCompanies(@PathParam("realmName") String realmName);
+
+    @GET
+    @Path("/{companyId}")
+    @Produces(MediaType.APPLICATION_JSON)
+    CompanyRepresentation getCompany(@PathParam("realmName") String realmName, @PathParam("companyId") String companyId);
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    Response createCompany(@PathParam("realmName") String realmName, CompanyRepresentation rep);
+
+    @DELETE
+    void deleteAllCompanies(@PathParam("realmName") String realmName);
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
index 81e4add..8bd36f3 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
@@ -20,7 +20,9 @@ package org.keycloak.testsuite.client.resources;
 import java.util.Date;
 import java.util.List;
 import org.keycloak.representations.idm.AdminEventRepresentation;
+import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.rest.representation.AuthenticatorState;
 
 import javax.ws.rs.Consumes;
@@ -35,6 +37,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.Map;
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.exportimport.ExportImportManager;
 
 /**
  * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -111,7 +114,7 @@ public interface TestingResource {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public List<EventRepresentation> queryEvents(@QueryParam("realmId") String realmId, @QueryParam("type") List<String> types, @QueryParam("client") String client,
-            @QueryParam("user") String user, @QueryParam("dateFrom") Date dateFrom, @QueryParam("dateTo") Date dateTo,
+            @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
             @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
             @QueryParam("max") Integer maxResults);
 
@@ -202,4 +205,48 @@ public interface TestingResource {
     @Path("/update-pass-through-auth-state")
     @Produces(MediaType.APPLICATION_JSON)
     AuthenticatorState updateAuthenticator(AuthenticatorState state);
+
+    @GET
+    @Path("/run-import")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runImport();
+
+    @GET
+    @Path("/run-export")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runExport();
+    
+    @GET
+    @Path("/valid-credentials")
+    @Produces(MediaType.APPLICATION_JSON)
+    public boolean validCredentials(@QueryParam("realmName") String realmName, @QueryParam("userName") String userName, @QueryParam("password") String password);
+
+    @GET
+    @Path("/user-by-federated-identity")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByFederatedIdentity(@QueryParam("realmName") String realmName,
+                                                         @QueryParam("identityProvider") String identityProvider,
+                                                         @QueryParam("userId") String userId,
+                                                         @QueryParam("userName") String userName);
+
+    @GET
+    @Path("/user-by-username-from-fed-factory")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByUsernameFromFedProviderFactory(@QueryParam("realmName") String realmName,
+                                                                      @QueryParam("userName") String userName);
+
+    @GET
+    @Path("/get-client-auth-flow")
+    @Produces(MediaType.APPLICATION_JSON)
+    public AuthenticationFlowRepresentation getClientAuthFlow(@QueryParam("realmName") String realmName);
+
+    @GET
+    @Path("/get-reset-cred-flow")
+    @Produces(MediaType.APPLICATION_JSON)
+    public AuthenticationFlowRepresentation getResetCredFlow(@QueryParam("realmName") String realmName);
+
+    @GET
+    @Path("/get-user-by-service-account-client")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserRepresentation getUserByServiceAccountClient(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId);
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
index 5110e4c..50c8ed3 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
@@ -25,4 +25,6 @@ public class MailServerConfiguration {
     public static final String FROM = "server@mail.test";
     public static final String HOST = "localhost";
     public static final String PORT = "3025";
+    public static final String PORT_SSL = "3465";
+    public static final String STARTTLS = "true";
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerFactoryImpl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerFactoryImpl.java
new file mode 100644
index 0000000..67a2e43
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerFactoryImpl.java
@@ -0,0 +1,86 @@
+package org.keycloak.testsuite.util;
+
+import org.subethamail.smtp.MessageContext;
+import org.subethamail.smtp.MessageHandler;
+import org.subethamail.smtp.MessageHandlerFactory;
+import org.subethamail.smtp.RejectException;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+import java.io.*;
+import java.util.Properties;
+
+
+public class MessageHandlerFactoryImpl implements MessageHandlerFactory {
+
+    MimeMessage message;
+
+    public MessageHandler create(MessageContext ctx) {
+        return new Handler(ctx);
+    }
+
+    class Handler implements MessageHandler {
+        MessageContext ctx;
+
+
+
+        public Handler(MessageContext ctx) {
+            this.ctx = ctx;
+        }
+
+        public void from(String from) throws RejectException {
+            System.out.println("FROM:" + from);
+        }
+
+        public void recipient(String recipient) throws RejectException {
+            System.out.println("RECIPIENT:" + recipient);
+        }
+
+        public void data(InputStream data) throws IOException {
+            String rawMail = this.convertStreamToString(data);
+
+            Session session = Session.getDefaultInstance(new Properties());
+            InputStream is = new ByteArrayInputStream(rawMail.getBytes());
+            try
+            {
+                message = new MimeMessage(session, is);
+                setMessage(message);
+            }
+            catch (MessagingException e)
+            {
+                e.printStackTrace();
+            }
+        }
+
+        public void done() {
+            System.out.println("Finished");
+        }
+
+        public String convertStreamToString(InputStream is) {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder sb = new StringBuilder();
+
+            String line = null;
+            try {
+                while ((line = reader.readLine()) != null) {
+                    sb.append(line + "\n");
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            return sb.toString();
+        }
+
+
+
+    }
+
+    public MimeMessage getMessage(){
+        return message;
+    }
+
+    public  void setMessage(MimeMessage msg){
+        this.message = msg;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerImpl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerImpl.java
new file mode 100644
index 0000000..ec6d430
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MessageHandlerImpl.java
@@ -0,0 +1,37 @@
+package org.keycloak.testsuite.util;
+
+import org.jboss.logging.Logger;
+import org.subethamail.smtp.MessageContext;
+import org.subethamail.smtp.MessageHandler;
+
+import java.io.InputStream;
+
+public class MessageHandlerImpl implements MessageHandler {
+    MessageContext context;
+
+    private static final Logger log = Logger.getLogger(MessageHandlerImpl.class);
+
+    MessageHandlerImpl(MessageContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public void from(String from) {
+        log.info("FROM: ${from}");
+    }
+
+    @Override
+    public void recipient(String recipient) {
+        log.info("RECIPIENT: ${recipient}");
+    }
+
+    @Override
+    public void data(InputStream data) {
+        log.info("DATA");
+    }
+
+    @Override
+    public void done() {
+        log.info("DONE");
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
index 670e340..a1a1813 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
@@ -53,6 +53,7 @@ import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RealmRepUtil;
 import org.keycloak.testsuite.util.UserBuilder;
 import twitter4j.JSONArray;
 import twitter4j.JSONObject;
@@ -68,7 +69,7 @@ public class ProfileTest extends TestRealmKeycloakTest {
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
-        UserRepresentation user = findUserInRealmRep(testRealm, "test-user@localhost");
+        UserRepresentation user = RealmRepUtil.findUser(testRealm, "test-user@localhost");
         user.setFirstName("First");
         user.setLastName("Last");
         Map<String, Object> attributes = user.getAttributes();
@@ -87,7 +88,7 @@ public class ProfileTest extends TestRealmKeycloakTest {
         RealmBuilder.edit(testRealm)
                     .user(user2);
 
-        ClientBuilder.edit(findClientInRealmRep(testRealm, "test-app"))
+        ClientBuilder.edit(RealmRepUtil.findClientByClientId(testRealm, "test-app"))
                      .addWebOrigin("http://localtest.me:8180");
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/TrustStoreEmailTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/TrustStoreEmailTest.java
new file mode 100644
index 0000000..75ba24e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/TrustStoreEmailTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import org.keycloak.testsuite.TestRealmKeycloakTest;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import org.keycloak.testsuite.auth.page.account.AccountManagement;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.auth.page.login.VerifyEmail;
+import org.keycloak.testsuite.util.*;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+
+/**
+ *
+ * @author fkiss
+ */
+public class TrustStoreEmailTest extends TestRealmKeycloakTest {
+
+    @Page
+    protected OIDCLogin testRealmLoginPage;
+
+    @Page
+    protected AuthRealm testRealmPage;
+
+    @Page
+    protected AccountManagement accountManagement;
+
+    @Page
+    private VerifyEmail testRealmVerifyEmailPage;
+
+    private UserRepresentation user;
+
+    @Override
+    public void configureTestRealm(RealmRepresentation testRealm) {
+        log.info("enable verify email and configure smtp server to run with ssl in test realm");
+
+        user = RealmRepUtil.findUser(testRealm, "test-user@localhost");
+        testRealm.setSmtpServer(SslMailServer.getServerConfiguration());
+        testRealm.setVerifyEmail(true);
+    }
+
+
+    @Override
+    public void setDefaultPageUriParameters() {
+        super.setDefaultPageUriParameters();
+        testRealmPage.setAuthRealm("test");
+        testRealmVerifyEmailPage.setAuthRealm(testRealmPage);
+        accountManagement.setAuthRealm(testRealmPage);
+        testRealmLoginPage.setAuthRealm(testRealmPage);
+    }
+
+    @After
+    public void afterTrustStoreEmailTest() {
+        SslMailServer.stop();
+    }
+
+
+    @Test
+    public void verifyEmailWithSslEnabled() {
+        SslMailServer.startWithSsl(this.getClass().getClassLoader().getResource(SslMailServer.PRIVATE_KEY).getFile());
+        accountManagement.navigateTo();
+        testRealmLoginPage.form().login(user.getUsername(), "password");
+
+        assertEquals("You need to verify your email address to activate your account.",
+                testRealmVerifyEmailPage.getFeedbackText());
+
+        String verifyEmailUrl = assertEmailAndGetUrl(MailServerConfiguration.FROM, user.getEmail(),
+                "Someone has created a Test account with this email address.", true);
+
+        log.info("navigating to url from email: " + verifyEmailUrl);
+
+        driver.navigate().to(verifyEmailUrl);
+
+        assertCurrentUrlStartsWith(accountManagement);
+        accountManagement.signOut();
+        testRealmLoginPage.form().login(user);
+        assertCurrentUrlStartsWith(accountManagement);
+    }
+
+    @Test
+    public void verifyEmailWithSslWrongCertificate() {
+        SslMailServer.startWithSsl(this.getClass().getClassLoader().getResource(SslMailServer.INVALID_KEY).getFile());
+        accountManagement.navigateTo();
+        loginPage.form().login(user);
+
+        assertEquals("Failed to send email, please try again later.\n" +
+                        "« Back to Application",
+                testRealmVerifyEmailPage.getErrorMessage());
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
index a2d0355..badf51c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -22,11 +22,15 @@ import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.asset.StringAsset;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
 
 import java.io.IOException;
 import java.net.URL;
 import java.util.List;
 
+import javax.ws.rs.core.UriBuilder;
+
 import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
 import static org.keycloak.testsuite.util.IOUtil.loadRealm;
 
@@ -107,4 +111,14 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
         testRealmPage.setAuthRealm(DEMO);
     }
 
+    protected void setAdapterAndServerTimeOffset(int timeOffset, String servletUri) {
+        setTimeOffset(timeOffset);
+        String timeOffsetUri = UriBuilder.fromUri(servletUri)
+                .queryParam("timeOffset", timeOffset)
+                .build().toString();
+
+        driver.navigate().to(timeOffsetUri);
+        WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
index 6100b13..7be3cbd 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -25,7 +25,8 @@ import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
-import org.keycloak.testsuite.adapter.page.JSConsoleExample;
+import org.keycloak.testsuite.adapter.page.JSConsoleTestApp;
+import org.keycloak.testsuite.adapter.page.JSDatabaseTestApp;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.auth.page.account.Applications;
 import org.keycloak.testsuite.auth.page.login.OAuthGrant;
@@ -51,7 +52,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampleAdapterTest {
 
     @Page
-    private JSConsoleExample jsConsoleExamplePage;
+    private JSConsoleTestApp jsConsoleTestAppPage;
 
     @Page
     private Config configPage;
@@ -67,9 +68,14 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
     public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
 
-    @Deployment(name = JSConsoleExample.DEPLOYMENT_NAME)
-    private static WebArchive jsConsoleExample() throws IOException {
-        return exampleDeployment(JSConsoleExample.CLIENT_ID);
+    @Deployment(name = JSConsoleTestApp.DEPLOYMENT_NAME)
+    private static WebArchive jsConsoleTestApp() throws IOException {
+        return exampleDeployment(JSConsoleTestApp.CLIENT_ID);
+    }
+
+    @Deployment(name = JSDatabaseTestApp.DEPLOYMENT_NAME)
+    private static WebArchive jsDbApp() throws IOException {
+        return exampleDeployment(JSDatabaseTestApp.CLIENT_ID);
     }
 
     @Override
@@ -77,7 +83,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         RealmRepresentation jsConsoleRealm = loadRealm(new File(TEST_APPS_HOME_DIR + "/js-console/example-realm.json"));
 
         fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
-                JSConsoleExample.CLIENT_ID, jsConsoleExamplePage.buildUri().toASCIIString());
+                JSConsoleTestApp.CLIENT_ID, jsConsoleTestAppPage.buildUri().toASCIIString());
 
         jsConsoleRealm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
 
@@ -92,92 +98,93 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
     @Test
     public void testJSConsoleAuth() {
-        jsConsoleExamplePage.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
+        jsConsoleTestAppPage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
 
-        waitUntilElement(jsConsoleExamplePage.getInitButtonElement()).is().present();
+        waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
 
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.logIn();
         testRealmLoginPage.form().login("user", "invalid-password");
-        assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
+        assertCurrentUrlDoesntStartWith(jsConsoleTestAppPage);
 
         testRealmLoginPage.form().login("invalid-user", "password");
-        assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
+        assertCurrentUrlDoesntStartWith(jsConsoleTestAppPage);
 
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        jsConsoleTestAppPage.init();
 
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Init Success (Authenticated)");
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Success");
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
 
-        jsConsoleExamplePage.logOut();
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
+        jsConsoleTestAppPage.logOut();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
+        jsConsoleTestAppPage.init();
 
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
     }
 
     @Test
     public void testRefreshToken() {
-        jsConsoleExamplePage.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
+        jsConsoleTestAppPage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
 
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.refreshToken();
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Failed to refresh token");
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.refreshToken();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
 
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Success");
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        jsConsoleTestAppPage.init();
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
 
-        jsConsoleExamplePage.refreshToken();
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Refresh Success");
+        jsConsoleTestAppPage.refreshToken();
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
     }
 
     @Test
     public void testRefreshTokenIfUnder30s() {
-        jsConsoleExamplePage.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.refreshToken();
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Failed to refresh token");
+        jsConsoleTestAppPage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.refreshToken();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
 
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Success");
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        jsConsoleTestAppPage.init();
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
 
-        jsConsoleExamplePage.refreshTokenIfUnder30s();
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Token not refreshed, valid for");
+        jsConsoleTestAppPage.refreshTokenIfUnder30s();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Token not refreshed, valid for");
 
         pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
 
-        jsConsoleExamplePage.refreshTokenIfUnder30s();
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Refresh Success");
+        jsConsoleTestAppPage.refreshTokenIfUnder30s();
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
     }
 
     @Test
     public void testGetProfile() {
-        jsConsoleExamplePage.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
+        jsConsoleTestAppPage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
 
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.getProfile();
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Failed to load profile");
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.getProfile();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to load profile");
 
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        jsConsoleExamplePage.init();
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Auth Success");
+        assertCurrentUrlStartsWith(jsConsoleTestAppPage);
+        jsConsoleTestAppPage.init();
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
 
-        jsConsoleExamplePage.getProfile();
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("\"username\": \"user\"");
+        jsConsoleTestAppPage.getProfile();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("\"username\": \"user\"");
     }
 
     @Test
@@ -188,7 +195,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         loginEventsPage.setConsoleRealm(EXAMPLE);
         applicationsPage.setAuthRealm(EXAMPLE);
 
-        jsConsoleExamplePage.navigateTo();
+        jsConsoleTestAppPage.navigateTo();
         driver.manage().deleteAllCookies();
 
         ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "js-console");
@@ -201,26 +208,27 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         realm.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT", "LOGIN"));
         testRealmResource().update(realm);
 
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.logIn();
 
         testRealmLoginPage.form().login("user", "password");
 
         assertTrue(oAuthGrantPage.isCurrent());
         oAuthGrantPage.accept();
 
-        jsConsoleExamplePage.init();
+        jsConsoleTestAppPage.init();
 
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Init Success (Authenticated)");
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
 
         applicationsPage.navigateTo();
         applicationsPage.revokeGrantForApplication("js-console");
 
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setOnLoad("login-required");
+        jsConsoleTestAppPage.init();
 
+        waitUntilElement(By.tagName("body")).is().visible();
         assertTrue(oAuthGrantPage.isCurrent());
 
         loginEventsPage.navigateTo();
@@ -255,34 +263,34 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
     @Test
     public void implicitFlowTest() {
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.setFlow("implicit");
-        jsConsoleExamplePage.init();
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setFlow("implicit");
+        jsConsoleTestAppPage.init();
 
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.logIn();
         assertTrue(driver.getPageSource().contains("Implicit flow is disabled for the client"));
 
         setImplicitFlowFroClient();
 
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.logIn();
         assertTrue(driver.getPageSource().contains("Standard flow is disabled for the client"));
 
         logInAndInit("implicit");
 
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Init Success (Authenticated)");
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
     }
 
     @Test
     public void implicitFlowQueryTest() {
         setImplicitFlowFroClient();
 
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.setFlow("implicit");
-        jsConsoleExamplePage.setResponseMode("query");
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setFlow("implicit");
+        jsConsoleTestAppPage.setResponseMode("query");
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.logIn();
         assertTrue(driver.getPageSource().contains("Invalid parameter: response_mode"));
     }
 
@@ -292,9 +300,9 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         logInAndInit("implicit");
 
-        jsConsoleExamplePage.refreshToken();
+        jsConsoleTestAppPage.refreshToken();
 
-        waitUntilElement(jsConsoleExamplePage.getOutputElement()).text().contains("Failed to refresh token");
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
     }
 
     @Test
@@ -309,7 +317,38 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         pause(6000);
 
-        waitUntilElement(jsConsoleExamplePage.getEventsElement()).text().contains("Access token expired");
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Access token expired");
+    }
+
+    @Test
+    public void testBearerRequest() {
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.createBearerRequest();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Unauthorized");
+
+        logInAndInit("standard", "unauthorized");
+        jsConsoleTestAppPage.createBearerRequest();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Forbidden");
+
+        jsConsoleTestAppPage.logOut();
+        logInAndInit("standard");
+        jsConsoleTestAppPage.createBearerRequest();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("[\"Bill Burke\",\"Stian Thorgersen\",\"Stan Silvert\",\"Gabriel Cardoso\",\"Viliam Rockai\",\"Marek Posolda\",\"Boleslaw Dawidowicz\"]");
+    }
+
+    @Test
+    public void loginRequiredAction() {
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setOnLoad("login-required");
+        jsConsoleTestAppPage.init();
+
+        assertCurrentUrlStartsWith(testRealmLoginPage);
+        testRealmLoginPage.form().login("user", "password");
+
+        waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
+        jsConsoleTestAppPage.init();
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
     }
 
     private void setImplicitFlowFroClient() {
@@ -320,14 +359,18 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         clientResource.update(client);
     }
 
+    private void logInAndInit(String flow, String user) {
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setFlow(flow);
+        jsConsoleTestAppPage.init();
+        jsConsoleTestAppPage.logIn();
+        testRealmLoginPage.form().login(user, "password");
+        jsConsoleTestAppPage.setFlow(flow);
+        jsConsoleTestAppPage.init();
+    }
+
     private void logInAndInit(String flow) {
-        jsConsoleExamplePage.navigateTo();
-        jsConsoleExamplePage.setFlow(flow);
-        jsConsoleExamplePage.init();
-        jsConsoleExamplePage.logIn();
-        testRealmLoginPage.form().login("user", "password");
-        jsConsoleExamplePage.setFlow(flow);
-        jsConsoleExamplePage.init();
+        logInAndInit(flow, "user");
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
index c754cd3..3b67027 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -27,9 +27,11 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.common.Version;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.VersionRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
+import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
 import org.keycloak.testsuite.adapter.page.*;
 import org.keycloak.util.BasicAuthHelper;
 
@@ -70,6 +72,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
     private ProductPortal productPortal;
     @Page
     private InputPortal inputPortal;
+    @Page
+    private TokenMinTTLPage tokenMinTTLPage;
 
     @Deployment(name = CustomerPortal.DEPLOYMENT_NAME)
     protected static WebArchive customerPortal() {
@@ -106,6 +110,11 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
         return servletDeployment(InputPortal.DEPLOYMENT_NAME, "keycloak.json", InputServlet.class);
     }
 
+    @Deployment(name = TokenMinTTLPage.DEPLOYMENT_NAME)
+    protected static WebArchive tokenMinTTLPage() {
+        return servletDeployment(TokenMinTTLPage.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, TokenMinTTLServlet.class, ErrorServlet.class);
+    }
+
     @Test
     public void testCustomerPortalWithSubsystemSettings() {
         customerPortalSubsystem.navigateTo();
@@ -408,4 +417,36 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
         assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
     }
 
+    // Tests "token-minimum-time-to-live" adapter configuration option
+    @Test
+    public void testTokenMinTTL() {
+        // Login
+        tokenMinTTLPage.navigateTo();
+        testRealmLoginPage.form().waitForUsernameInputPresent();
+        assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+        testRealmLoginPage.form().login("bburke@redhat.com", "password");
+        assertCurrentUrlEquals(tokenMinTTLPage);
+
+        // Get time of token
+        AccessToken token = tokenMinTTLPage.getAccessToken();
+        int tokenIssued1 = token.getIssuedAt();
+
+        // Sets 5 minutes offset and assert access token will be still the same
+        setAdapterAndServerTimeOffset(300, tokenMinTTLPage.toString());
+        token = tokenMinTTLPage.getAccessToken();
+        int tokenIssued2 = token.getIssuedAt();
+        Assert.assertEquals(tokenIssued1, tokenIssued2);
+        Assert.assertFalse(token.isExpired());
+
+        // Sets 9 minutes offset and assert access token will be refreshed (accessTokenTimeout is 10 minutes, token-min-ttl is 2 minutes. Hence 8 minutes or more should be sufficient)
+        setAdapterAndServerTimeOffset(540, tokenMinTTLPage.toString());
+        token = tokenMinTTLPage.getAccessToken();
+        int tokenIssued3 = token.getIssuedAt();
+        Assert.assertTrue(tokenIssued3 > tokenIssued1);
+
+        // Revert times
+        setAdapterAndServerTimeOffset(0, tokenMinTTLPage.toString());
+    }
+
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
index 9d8da7b..fa6d0a8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
@@ -12,6 +12,7 @@ import org.keycloak.events.EventType;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
+import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
 import org.keycloak.testsuite.adapter.page.OfflineToken;
 import org.keycloak.testsuite.pages.AccountApplicationsPage;
 import org.keycloak.testsuite.pages.LoginPage;
@@ -48,7 +49,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
 
     @Deployment(name = OfflineToken.DEPLOYMENT_NAME)
     protected static WebArchive offlineClient() {
-        return servletDeployment(OfflineToken.DEPLOYMENT_NAME, OfflineTokenServlet.class, ErrorServlet.class);
+        return servletDeployment(OfflineToken.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, OfflineTokenServlet.class, ErrorServlet.class);
     }
 
     @Override
@@ -186,14 +187,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
     }
 
     private void setAdapterAndServerTimeOffset(int timeOffset) {
-        setTimeOffset(timeOffset);
-        String timeOffsetUri = UriBuilder.fromUri(offlineTokenPage.toString())
-                .queryParam("timeOffset", timeOffset)
-                .build().toString();
-
-        driver.navigate().to(timeOffsetUri);
-        waitUntilElement(By.tagName("body")).is().visible();
-
+        super.setAdapterAndServerTimeOffset(timeOffset, offlineTokenPage.toString());
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
index 0034f36..23c365f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
@@ -244,7 +244,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
         salesPostPassiveServletPage.navigateTo();
         waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().contains("<body><pre></pre></body>"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
 
         salesPostSigEmailServletPage.navigateTo();
         assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
@@ -322,7 +322,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
         waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().contains("<body><pre></pre></body>"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
 
         assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage);
 
@@ -333,8 +333,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
         waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().contains("<body><pre></pre></body>"));
-
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
         assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
         assertForbidden(salesPostPassiveServletPage);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
index dca666f..d7946ce 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -60,6 +60,15 @@ public class ApiUtil {
         return path.substring(path.lastIndexOf('/') + 1);
     }
 
+    public static ClientResource findClientResourceById(RealmResource realm, String id) {
+        for (ClientRepresentation c : realm.clients().findAll()) {
+            if (c.getId().equals(id)) {
+                return realm.clients().get(c.getId());
+            }
+        }
+        return null;
+    }
+
     public static ClientResource findClientResourceByClientId(RealmResource realm, String clientId) {
         for (ClientRepresentation c : realm.clients().findAll()) {
             if (c.getClientId().equals(clientId)) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java
index d39f43c..0089192 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java
@@ -52,7 +52,7 @@ public class AttackDetectionResourceTest extends AbstractAdminTest {
     public void test() {
         AttackDetectionResource detection = adminClient.realm("test").attackDetection();
 
-        assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
 
         oauthClient.doLogin("test-user@localhost", "invalid");
         oauthClient.doLogin("test-user@localhost", "invalid");
@@ -62,21 +62,21 @@ public class AttackDetectionResourceTest extends AbstractAdminTest {
         oauthClient.doLogin("test-user2", "invalid");
         oauthClient.doLogin("nosuchuser", "invalid");
 
-        assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 3, true, true);
-        assertBruteForce(detection.bruteForceUserStatus("test-user2"), 2, true, true);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 3, true, true);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 2, true, true);
         assertBruteForce(detection.bruteForceUserStatus("nosuchuser"), 0, false, false);
 
-        detection.clearBruteForceForUser("test-user@localhost");
-        assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearBruteForceForUserPath("test-user@localhost"));
+        detection.clearBruteForceForUser(findUser("test-user@localhost").getId());
+        assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearBruteForceForUserPath(findUser("test-user@localhost").getId()));
 
-        assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
-        assertBruteForce(detection.bruteForceUserStatus("test-user2"), 2, true, true);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 2, true, true);
 
         detection.clearAllBruteForce();
         assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.attackDetectionClearAllBruteForcePath());
 
-        assertBruteForce(detection.bruteForceUserStatus("test-user@localhost"), 0, false, false);
-        assertBruteForce(detection.bruteForceUserStatus("test-user2"), 0, false, false);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user@localhost").getId()), 0, false, false);
+        assertBruteForce(detection.bruteForceUserStatus(findUser("test-user2").getId()), 0, false, false);
     }
 
     private void assertBruteForce(Map<String, Object> status, Integer expectedNumFailures, Boolean expectedFailure, Boolean expectedDisabled) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index 68a8f8d..2a9c643 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -359,7 +359,7 @@ public class ClientTest extends AbstractAdminTest {
 
         Assert.assertNames(scopesResource.realmLevel().listAll(), "role1");
         Assert.assertNames(scopesResource.realmLevel().listEffective(), "role1", "role2");
-        Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access");
+        Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
         Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll(), AccountRoles.VIEW_PROFILE);
         Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective(), AccountRoles.VIEW_PROFILE);
@@ -376,7 +376,7 @@ public class ClientTest extends AbstractAdminTest {
 
         Assert.assertNames(scopesResource.realmLevel().listAll());
         Assert.assertNames(scopesResource.realmLevel().listEffective());
-        Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", "role1", "role2");
+        Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "role1", "role2");
         Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll());
         Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT);
         Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
index 988ecaf..9ac45d0 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
@@ -23,6 +23,7 @@ import org.junit.Test;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.admin.client.resource.RoleMappingResource;
 import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.Constants;
 import org.keycloak.models.RoleModel;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientRepresentation;
@@ -430,7 +431,7 @@ public class GroupTest extends AbstractGroupTest {
 
         // List realm roles
         assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite");
-        assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", "user");
+        assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user");
         assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child");
 
         // List client roles
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
index 98bf9e6..81de894 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
@@ -182,6 +182,15 @@ public class PermissionsTest extends AbstractKeycloakTest {
         Assert.assertNames(realms, REALM_NAME);
         assertNotNull(realms.get(0).getAccessTokenLifespan());
 
+        // Check the same when access with users from 'master' realm
+        realms = clients.get("master-" + AdminRoles.VIEW_USERS).realms().findAll();
+        Assert.assertNames(realms, REALM_NAME);
+        assertGettersEmpty(realms.get(0));
+
+        realms = clients.get("master-" + AdminRoles.VIEW_REALM).realms().findAll();
+        Assert.assertNames(realms, REALM_NAME);
+        assertNotNull(realms.get(0).getAccessTokenLifespan());
+
         // Create realm
         invoke(new Invocation() {
             public void invoke(RealmResource realm) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index 19645a8..bb23116 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -29,6 +29,7 @@ import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.admin.client.resource.RoleMappingResource;
 import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.Constants;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.ErrorRepresentation;
@@ -789,7 +790,7 @@ public class UserTest extends AbstractAdminTest {
         assertAdminEvents.clear();
 
         RoleMappingResource roles = realm.users().get(userId).roles();
-        assertNames(roles.realmLevel().listAll(), "user", "offline_access");
+        assertNames(roles.realmLevel().listAll(), "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
         // Add realm roles
         List<RoleRepresentation> l = new LinkedList<>();
@@ -808,9 +809,9 @@ public class UserTest extends AbstractAdminTest {
         assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), list);
 
         // List realm roles
-        assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access");
+        assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
         assertNames(roles.realmLevel().listAvailable(), "admin");
-        assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access");
+        assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
         // List client roles
         assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite");
@@ -819,7 +820,7 @@ public class UserTest extends AbstractAdminTest {
 
         // Get mapping representation
         MappingsRepresentation all = roles.getAll();
-        assertNames(all.getRealmMappings(), "realm-role", "realm-composite", "user", "offline_access");
+        assertNames(all.getRealmMappings(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
         assertEquals(2, all.getClientMappings().size());
         assertNames(all.getClientMappings().get("myclient").getMappings(), "client-role", "client-composite");
         assertNames(all.getClientMappings().get("account").getMappings(), "manage-account", "view-profile");
@@ -829,7 +830,7 @@ public class UserTest extends AbstractAdminTest {
         roles.realmLevel().remove(Collections.singletonList(realmRoleRep));
         assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userRealmRoleMappingsPath(userId), Collections.singletonList(realmRoleRep));
 
-        assertNames(roles.realmLevel().listAll(), "realm-composite", "user", "offline_access");
+        assertNames(roles.realmLevel().listAll(), "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
         // Remove client role
         RoleRepresentation clientRoleRep = realm.clients().get(clientUuid).roles().get("client-role").toRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java
index 4831e29..28f0305 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTotpTest.java
@@ -88,7 +88,7 @@ public class UserTotpTest extends TestRealmKeycloakTest {
 
         List<UserRepresentation> users = adminClient.realms().realm("test").users().search("test-user@localhost", null, null, null, 0, 1);
         String userId = users.get(0).getId();
-
+        testingClient.testing().clearAdminEventQueue();
         adminClient.realms().realm("test").users().get(userId).removeTotp();
 
         totpPage.open();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CustomExtensionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CustomExtensionTest.java
new file mode 100644
index 0000000..36b5702
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/domainextension/CustomExtensionTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.domainextension;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.client.resources.TestExampleCompanyResource;
+import org.keycloak.testsuite.util.RealmBuilder;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CustomExtensionTest extends AbstractKeycloakTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation foo = RealmBuilder.create().name("foo").build();
+        testRealms.add(foo);
+    }
+
+    @Test
+    public void testDomainExtension() throws Exception {
+        companyResource().createCompany("foo", buildCompany("foo-company"));
+        companyResource().createCompany("foo", buildCompany("bar-company"));
+        companyResource().createCompany("master", buildCompany("master-company"));
+
+        List<CompanyRepresentation> fooCompanies = companyResource().getCompanies("foo");
+        List<CompanyRepresentation> masterCompanies = companyResource().getCompanies("master");
+
+        assertCompanyNames(fooCompanies, "foo-company", "bar-company");
+        assertCompanyNames(masterCompanies, "master-company");
+
+        companyResource().deleteAllCompanies("foo");
+        companyResource().deleteAllCompanies("master");
+    }
+
+    private TestExampleCompanyResource companyResource() {
+        return testingClient.testExampleCompany();
+    }
+
+    private CompanyRepresentation buildCompany(String companyName) {
+        CompanyRepresentation rep = new CompanyRepresentation();
+        rep.setName(companyName);
+        return rep;
+    }
+
+    private void assertCompanyNames(List<CompanyRepresentation> companies, String... expectedNames) {
+        Set<String> names = new HashSet<>();
+        for (CompanyRepresentation comp : companies) {
+            names.add(comp.getName());
+        }
+
+        Assert.assertEquals(expectedNames.length, names.size());
+        for (String expectedName : expectedNames) {
+            Assert.assertTrue(names.contains(expectedName));
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java
index 89fc07c..d8a08fc 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java
@@ -89,18 +89,12 @@ public class EventStoreProviderTest extends AbstractEventsTest {
 
         SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
         Date date1 = null, date2 = null, date3 = null, date4 = null;
-        Date date5 = null, date6 = null, date7 = null, date8 = null;
 
         try {
             date1 = formatter.parse(d1);
             date2 = formatter.parse(d2);
             date3 = formatter.parse(d3);
             date4 = formatter.parse(d4);
-
-            date5 = formatter.parse(d5);
-            date6 = formatter.parse(d6);
-            date7 = formatter.parse(d7);
-            date8 = formatter.parse(d8);
         } catch (ParseException e) {
             e.printStackTrace();
         }
@@ -131,22 +125,22 @@ public class EventStoreProviderTest extends AbstractEventsTest {
         Assert.assertEquals(1, testing().queryEvents(null, toList(EventType.UPDATE_PROFILE), null, null, null, null, null, null, null).size());
         Assert.assertEquals(1, testing().queryEvents(null, toList(EventType.UPDATE_EMAIL), null, null, null, null, null, null, null).size());
 
-        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, date1, null, null, null, null).size());
-        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, null, date4, null, null, null).size());
+        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, d1, null, null, null, null).size());
+        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, null, d4, null, null, null).size());
 
-        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, date3, null, null, null, null).size());
-        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, null, date2, null, null, null).size());
+        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, d3, null, null, null, null).size());
+        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, null, d2, null, null, null).size());
 
-        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, date7, null, null, null, null).size());
-        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, null, date6, null, null, null).size());
+        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, d7, null, null, null, null).size());
+        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, null, d6, null, null, null).size());
 
-        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, date1, date4, null, null, null).size());
-        Assert.assertEquals(6, testing().queryEvents(null, null, null, null, date2, date4, null, null, null).size());
-        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, date1, date2, null, null, null).size());
-        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, date3, date4, null, null, null).size());
+        Assert.assertEquals(8, testing().queryEvents(null, null, null, null, d1, d4, null, null, null).size());
+        Assert.assertEquals(6, testing().queryEvents(null, null, null, null, d2, d4, null, null, null).size());
+        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, d1, d2, null, null, null).size());
+        Assert.assertEquals(4, testing().queryEvents(null, null, null, null, d3, d4, null, null, null).size());
 
-        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, date5, date6, null, null, null).size());
-        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, date7, date8, null, null, null).size());
+        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, d5, d6, null, null, null).size());
+        Assert.assertEquals(0, testing().queryEvents(null, null, null, null, d7, d8, null, null, null).size());
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
new file mode 100755
index 0000000..3a6a1bb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.exportimport;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.dir.DirExportProvider;
+import org.keycloak.exportimport.dir.DirExportProviderFactory;
+import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+import java.util.regex.Matcher;
+import org.jboss.arquillian.container.spi.client.container.LifecycleException;
+import org.junit.After;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.util.UserBuilder;
+
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+
+/**
+ *
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ExportImportTest extends AbstractExportImportTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation testRealm1 = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+        testRealm1.getUsers().add(makeUser("user1"));
+        testRealm1.getUsers().add(makeUser("user2"));
+        testRealm1.getUsers().add(makeUser("user3"));
+        testRealms.add(testRealm1);
+
+        RealmRepresentation testRealm2 = loadJson(getClass().getResourceAsStream("/model/testrealm.json"), RealmRepresentation.class);
+        testRealm2.setId("test-realm");
+        testRealms.add(testRealm2);
+    }
+
+    private UserRepresentation makeUser(String userName) {
+        return UserBuilder.create()
+                .username(userName)
+                .email(userName + "@test.com")
+                .password("password")
+                .build();
+    }
+
+    @After
+    public void clearExportImportProps() throws LifecycleException {
+        clearExportImportProperties();
+    }
+
+    @Test
+    public void testDirFullExportImport() throws Throwable {
+        ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
+        String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport";
+        DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
+        ExportImportConfig.setDir(targetDirPath);
+        ExportImportConfig.setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE);
+
+        testFullExportImport();
+
+        // There should be 6 files in target directory (3 realm, 3 user)
+        Assert.assertEquals(6, new File(targetDirPath).listFiles().length);
+    }
+
+    @Test
+    public void testDirRealmExportImport() throws Throwable {
+        ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
+        String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport";
+        DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
+        ExportImportConfig.setDir(targetDirPath);
+        ExportImportConfig.setUsersPerFile(3);
+
+        testRealmExportImport();
+
+        // There should be 3 files in target directory (1 realm, 3 user)
+        File[] files = new File(targetDirPath).listFiles();
+        Assert.assertEquals(4, files.length);
+    }
+
+    @Test
+    public void testSingleFileFullExportImport() throws Throwable {
+        ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-full.json";
+        ExportImportConfig.setFile(targetFilePath);
+
+        testFullExportImport();
+    }
+
+    @Test
+    public void testSingleFileRealmExportImport() throws Throwable {
+        ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
+        ExportImportConfig.setFile(targetFilePath);
+
+        testRealmExportImport();
+    }
+
+    @Test
+    public void testSingleFileRealmWithoutBuiltinsImport() throws Throwable {
+        // Remove test realm
+        removeRealm("test-realm");
+
+        // Set the realm, which doesn't have builtin clients/roles inside JSON
+        ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        URL url = ExportImportTest.class.getResource("/model/testrealm.json");
+        String targetFilePath = new File(url.getFile()).getAbsolutePath();
+        ExportImportConfig.setFile(targetFilePath);
+
+        ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+
+        testingClient.testing().runImport();
+
+        RealmResource testRealmRealm = adminClient.realm("test-realm");
+
+        ExportImportUtil.assertDataImportedInRealm(adminClient, testingClient, testRealmRealm.toRepresentation());
+    }
+
+
+    private void removeRealm(String realmName) {
+        adminClient.realm(realmName).remove();
+    }
+
+    private void testFullExportImport() throws LifecycleException {
+        ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
+        ExportImportConfig.setRealmName(null);
+
+        testingClient.testing().runExport();
+
+        removeRealm("test");
+        removeRealm("test-realm");
+        Assert.assertEquals(1, adminClient.realms().findAll().size());
+
+        assertNotAuthenticated("test", "test-user@localhost", "password");
+        assertNotAuthenticated("test", "user1", "password");
+        assertNotAuthenticated("test", "user2", "password");
+        assertNotAuthenticated("test", "user3", "password");
+
+        // Configure import
+        ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+
+        testingClient.testing().runImport();
+
+        // Ensure data are imported back
+        Assert.assertEquals(3, adminClient.realms().findAll().size());
+
+        assertAuthenticated("test", "test-user@localhost", "password");
+        assertAuthenticated("test", "user1", "password");
+        assertAuthenticated("test", "user2", "password");
+        assertAuthenticated("test", "user3", "password");
+    }
+
+    private void testRealmExportImport() throws LifecycleException {
+        ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
+        ExportImportConfig.setRealmName("test");
+
+        testingClient.testing().runExport();
+
+        // Delete some realm (and some data in admin realm)
+        adminClient.realm("test").remove();
+
+        Assert.assertEquals(2, adminClient.realms().findAll().size());
+
+        assertNotAuthenticated("test", "test-user@localhost", "password");
+        assertNotAuthenticated("test", "user1", "password");
+        assertNotAuthenticated("test", "user2", "password");
+        assertNotAuthenticated("test", "user3", "password");
+
+        // Configure import
+        ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+
+        testingClient.testing().runImport();
+
+        // Ensure data are imported back, but just for "test" realm
+        Assert.assertEquals(3, adminClient.realms().findAll().size());
+
+        assertAuthenticated("test", "test-user@localhost", "password");
+        assertAuthenticated("test", "user1", "password");
+        assertAuthenticated("test", "user2", "password");
+        assertAuthenticated("test", "user3", "password");
+    }
+
+    private void assertAuthenticated(String realmName, String username, String password) {
+        assertAuth(true, realmName, username, password);
+    }
+
+    private void assertNotAuthenticated(String realmName, String username, String password) {
+        assertAuth(false, realmName, username, password);
+    }
+
+    private void assertAuth(boolean expectedResult, String realmName, String username, String password) {
+        Assert.assertEquals(expectedResult, testingClient.testing().validCredentials(realmName, username, password));
+    }
+
+    private static String getExportImportTestDirectory() {
+        String dirPath = null;
+        String relativeDirExportImportPath = "testsuite" + File.separator +
+                                             "integration-arquillian" + File.separator +
+                                             "tests" + File.separator +
+                                             "base" + File.separator +
+                                             "target" + File.separator +
+                                             "export-import";
+
+        if (System.getProperties().containsKey("maven.home")) {
+            dirPath = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", Matcher.quoteReplacement(relativeDirExportImportPath));
+        } else {
+            for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) {
+                if (c.contains(File.separator + "testsuite" + File.separator + "integration-arquillian" + File.separator)) {
+                    dirPath = c.replaceFirst("testsuite.integration-arquillian.*", Matcher.quoteReplacement(relativeDirExportImportPath));
+                }
+            }
+        }
+
+        String absolutePath = new File(dirPath).getAbsolutePath();
+        return absolutePath;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
new file mode 100644
index 0000000..95b327f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.exportimport;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Assert;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientTemplateResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.constants.KerberosConstants;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory;
+import org.keycloak.models.Constants;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
+import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
+import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
+import org.keycloak.representations.idm.ClientMappingsRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserFederationMapperRepresentation;
+import org.keycloak.representations.idm.UserFederationProviderRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.client.KeycloakTestingClient;
+import org.keycloak.testsuite.util.RealmRepUtil;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ExportImportUtil {
+
+    // In the old testsuite, this method exists as a public method of ImportTest from the model package.
+    // However, model package is not ready to be migrated yet.
+    public static void assertDataImportedInRealm(Keycloak adminClient, KeycloakTestingClient testingClient, RealmRepresentation realm) {
+        Assert.assertTrue(realm.isVerifyEmail());
+        Assert.assertEquals((Integer)3600000, realm.getOfflineSessionIdleTimeout());
+        Assert.assertEquals((Integer)1500, realm.getAccessTokenLifespanForImplicitFlow());
+
+        Set<String> creds = realm.getRequiredCredentials();
+        Assert.assertEquals(1, creds.size());
+        String cred = (String)creds.iterator().next();
+        Assert.assertEquals("password", cred);
+        Assert.assertEquals(4, realm.getDefaultRoles().size());
+
+        Assert.assertNotNull(RealmRepUtil.findDefaultRole(realm, "foo"));
+        Assert.assertNotNull(RealmRepUtil.findDefaultRole(realm, "bar"));
+
+        RealmResource realmRsc = adminClient.realm(realm.getRealm());
+
+        /* See KEYCLOAK-3104*/
+        UserRepresentation user = findByUsername(realmRsc, "loginclient");
+        Assert.assertNotNull(user);
+
+        UserResource userRsc = realmRsc.users().get(user.getId());
+        Assert.assertEquals(0, userRsc.getFederatedIdentity().size());
+
+        List<ClientRepresentation> resources = realmRsc.clients().findAll();
+        Assert.assertEquals(8, resources.size());
+
+        // Test applications imported
+        ClientRepresentation application = ApiUtil.findClientByClientId(realmRsc, "Application").toRepresentation();
+        ClientRepresentation otherApp = ApiUtil.findClientByClientId(realmRsc, "OtherApp").toRepresentation();
+        ClientRepresentation accountApp = ApiUtil.findClientByClientId(realmRsc, Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).toRepresentation();
+        ClientResource nonExisting = ApiUtil.findClientByClientId(realmRsc, "NonExisting");
+        Assert.assertNotNull(application);
+        Assert.assertNotNull(otherApp);
+        Assert.assertNull(nonExisting);
+        List<ClientRepresentation> clients = realmRsc.clients().findAll();
+        Assert.assertEquals(8, clients.size());
+        Assert.assertTrue(hasClient(clients, application));
+        Assert.assertTrue(hasClient(clients, otherApp));
+        Assert.assertTrue(hasClient(clients, accountApp));
+
+        Assert.assertEquals("Applicationn", application.getName());
+        Assert.assertEquals((Integer)50, application.getNodeReRegistrationTimeout());
+        Map<String, Integer> appRegisteredNodes = application.getRegisteredNodes();
+        Assert.assertEquals(2, appRegisteredNodes.size());
+        Assert.assertTrue(10 == appRegisteredNodes.get("node1"));
+        Assert.assertTrue(20 == appRegisteredNodes.get("172.10.15.20"));
+
+        // test clientAuthenticatorType
+        Assert.assertEquals("client-secret", application.getClientAuthenticatorType());
+        Assert.assertEquals("client-jwt", otherApp.getClientAuthenticatorType());
+
+        // Test finding applications by ID
+        Assert.assertNull(ApiUtil.findClientResourceById(realmRsc, "982734"));
+        Assert.assertEquals(application.getId(), ApiUtil.findClientResourceById(realmRsc, application.getId()).toRepresentation().getId());
+
+
+        // Test role mappings
+        UserRepresentation admin = findByUsername(realmRsc, "admin");
+        // user without creation timestamp in import
+        Assert.assertNull(admin.getCreatedTimestamp());
+        Set<RoleRepresentation> allRoles = allRoles(realmRsc, admin);
+        Assert.assertEquals(3, allRoles.size());
+        Assert.assertTrue(containsRole(allRoles, findRealmRole(realmRsc, "admin")));
+        Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, application.getId(), "app-admin")));
+        Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, otherApp.getId(), "otherapp-admin")));
+
+        Assert.assertTrue(findClientRole(realmRsc, application.getId(), "app-admin").isScopeParamRequired());
+        Assert.assertFalse(findClientRole(realmRsc, otherApp.getId(), "otherapp-admin").isScopeParamRequired());
+        Assert.assertFalse(findClientRole(realmRsc, otherApp.getId(), "otherapp-user").isScopeParamRequired());
+
+        UserRepresentation wburke = findByUsername(realmRsc, "wburke");
+        // user with creation timestamp in import
+        Assert.assertEquals(new Long(123654), wburke.getCreatedTimestamp());
+        allRoles = allRoles(realmRsc, wburke);
+        Assert.assertEquals(2, allRoles.size());
+        Assert.assertFalse(containsRole(allRoles, findRealmRole(realmRsc, "admin")));
+        Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, application.getId(), "app-user")));
+        Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, otherApp.getId(), "otherapp-user")));
+
+        Assert.assertNull(realmRsc.users().get(wburke.getId()).roles().getAll().getRealmMappings());
+
+        UserRepresentation loginclient = findByUsername(realmRsc, "loginclient");
+        // user with creation timestamp as string in import
+        Assert.assertEquals(new Long(123655), loginclient.getCreatedTimestamp());
+
+        List<RoleRepresentation> realmRoles = realmRolesForUser(realmRsc, admin);
+        Assert.assertEquals(1, realmRoles.size());
+        Assert.assertEquals("admin", realmRoles.iterator().next().getName());
+
+        List<RoleRepresentation> appRoles = clientRolesForUser(realmRsc, application, admin);
+        Assert.assertEquals(1, appRoles.size());
+        Assert.assertEquals("app-admin", appRoles.iterator().next().getName());
+
+        // Test attributes
+        Map<String, List<String>> attrs = wburke.getAttributesAsListValues();
+        Assert.assertEquals(1, attrs.size());
+        List<String> attrVals = attrs.get("email");
+        Assert.assertEquals(1, attrVals.size());
+        Assert.assertEquals("bburke@redhat.com", attrVals.get(0));
+
+        attrs = admin.getAttributesAsListValues();
+        Assert.assertEquals(2, attrs.size());
+        attrVals = attrs.get("key1");
+        Assert.assertEquals(1, attrVals.size());
+        Assert.assertEquals("val1", attrVals.get(0));
+        attrVals = attrs.get("key2");
+        Assert.assertEquals(2, attrVals.size());
+        Assert.assertTrue(attrVals.contains("val21") && attrVals.contains("val22"));
+
+        // Test client
+        ClientResource oauthClient = ApiUtil.findClientResourceByClientId(realmRsc, "oauthclient");
+        ClientRepresentation oauthClientRep = oauthClient.toRepresentation();
+        Assert.assertEquals("clientpassword", oauthClient.getSecret().getValue());
+        Assert.assertTrue(oauthClientRep.isEnabled());
+        Assert.assertNotNull(oauthClientRep);
+
+        // Test scope relationship
+        Set<RoleRepresentation> allScopes = allScopeMappings(oauthClient);
+        Assert.assertEquals(2, allScopes.size());
+        Assert.assertTrue(containsRole(allScopes, findRealmRole(realmRsc, "admin")));
+        Assert.assertTrue(containsRole(allScopes, findClientRole(realmRsc, application.getId(), "app-user")));
+
+        List<RoleRepresentation> realmScopes = realmScopeMappings(oauthClient);
+        Assert.assertTrue(containsRole(realmScopes, findRealmRole(realmRsc, "admin")));
+
+        List<RoleRepresentation> appScopes = clientScopeMappings(oauthClient);
+        Assert.assertTrue(containsRole(appScopes, findClientRole(realmRsc, application.getId(), "app-user")));
+
+        // Test social linking
+        UserResource socialUser = realmRsc.users().get(findByUsername(realmRsc, "mySocialUser").getId());
+        List<FederatedIdentityRepresentation> socialLinks = socialUser.getFederatedIdentity();
+        Assert.assertEquals(3, socialLinks.size());
+        boolean facebookFound = false;
+        boolean googleFound = false;
+        boolean twitterFound = false;
+        FederatedIdentityRepresentation facebookIdentityRep = null;
+        for (FederatedIdentityRepresentation federatedIdentityRep : socialLinks) {
+            if ("facebook1".equals(federatedIdentityRep.getIdentityProvider())) {
+                facebookFound = true;
+                facebookIdentityRep = federatedIdentityRep;
+                Assert.assertEquals("facebook1",federatedIdentityRep.getUserId());
+                Assert.assertEquals("fbuser1", federatedIdentityRep.getUserName());
+            } else if ("google1".equals(federatedIdentityRep.getIdentityProvider())) {
+                googleFound = true;
+                Assert.assertEquals("google1", federatedIdentityRep.getUserId());
+                Assert.assertEquals("mysocialuser@gmail.com", federatedIdentityRep.getUserName());
+            } else if ("twitter1".equals(federatedIdentityRep.getIdentityProvider())) {
+                twitterFound = true;
+                Assert.assertEquals("twitter1", federatedIdentityRep.getUserId());
+                Assert.assertEquals("twuser1", federatedIdentityRep.getUserName());
+            }
+        }
+        Assert.assertTrue(facebookFound && twitterFound && googleFound);
+
+        UserRepresentation foundSocialUser =  testingClient.testing().getUserByFederatedIdentity(realm.getRealm(), "facebook1", "facebook1", "fbuser1");
+        Assert.assertEquals(foundSocialUser.getUsername(), socialUser.toRepresentation().getUsername());
+        Assert.assertNull(testingClient.testing().getUserByFederatedIdentity(realm.getRealm(), "facebook", "not-existing", "not-existing"));
+
+        Assert.assertEquals("facebook1", facebookIdentityRep.getUserId());
+        Assert.assertEquals("fbuser1", facebookIdentityRep.getUserName());
+        Assert.assertEquals("facebook1", facebookIdentityRep.getIdentityProvider());
+
+        // Test remove/add social link
+        socialUser.removeFederatedIdentity("facebook1");
+        Assert.assertEquals(2, socialUser.getFederatedIdentity().size());
+        socialUser.addFederatedIdentity("facebook1", facebookIdentityRep);
+        Assert.assertEquals(3, socialUser.getFederatedIdentity().size());
+
+        // Test smtp config
+        Map<String, String> smtpConfig = realm.getSmtpServer();
+        Assert.assertTrue(smtpConfig.size() == 3);
+        Assert.assertEquals("auto@keycloak.org", smtpConfig.get("from"));
+        Assert.assertEquals("localhost", smtpConfig.get("host"));
+        Assert.assertEquals("3025", smtpConfig.get("port"));
+
+        // Test identity providers
+        List<IdentityProviderRepresentation> identityProviders = realm.getIdentityProviders();
+        Assert.assertEquals(3, identityProviders.size());
+        IdentityProviderRepresentation google = null;
+        for (IdentityProviderRepresentation idpRep : identityProviders) {
+            if (idpRep.getAlias().equals("google1")) google = idpRep;
+        }
+        Assert.assertNotNull(google);
+        Assert.assertEquals("google1", google.getAlias());
+        Assert.assertEquals("google", google.getProviderId());
+        Assert.assertTrue(google.isEnabled());
+        Assert.assertEquals("googleId", google.getConfig().get("clientId"));
+        Assert.assertEquals("googleSecret", google.getConfig().get("clientSecret"));
+
+        // Test federation providers
+        List<UserFederationProviderRepresentation> fedProviders = realm.getUserFederationProviders();
+        Assert.assertTrue(fedProviders.size() == 2);
+        UserFederationProviderRepresentation ldap1 = fedProviders.get(0);
+        Assert.assertEquals("MyLDAPProvider1", ldap1.getDisplayName());
+        Assert.assertEquals("ldap", ldap1.getProviderName());
+        Assert.assertEquals(1, ldap1.getPriority());
+        Assert.assertEquals("ldap://foo", ldap1.getConfig().get(LDAPConstants.CONNECTION_URL));
+
+        UserFederationProviderRepresentation ldap2 = fedProviders.get(1);
+        Assert.assertEquals("MyLDAPProvider2", ldap2.getDisplayName());
+        Assert.assertEquals("ldap://bar", ldap2.getConfig().get(LDAPConstants.CONNECTION_URL));
+
+        // Test federation mappers
+        List<UserFederationMapperRepresentation> fedMappers1 = realmRsc.userFederation().get(ldap1.getId()).getMappers();
+        Assert.assertTrue(fedMappers1.size() == 1);
+        UserFederationMapperRepresentation fullNameMapper = fedMappers1.iterator().next();
+        Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
+        Assert.assertEquals(FullNameLDAPFederationMapperFactory.PROVIDER_ID, fullNameMapper.getFederationMapperType());
+        //Assert.assertEquals(ldap1.getId(), fullNameMapper.getFederationProviderId());
+        Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE));
+
+        // All builtin LDAP mappers should be here
+        List<UserFederationMapperRepresentation> fedMappers2 = realmRsc.userFederation().get(ldap2.getId()).getMappers();
+        Assert.assertTrue(fedMappers2.size() > 3);
+        List<UserFederationMapperRepresentation> allMappers = realm.getUserFederationMappers();
+        Assert.assertEquals(allMappers.size(), fedMappers1.size() + fedMappers2.size());
+
+        // Assert that federation link wasn't created during import
+        Assert.assertNull(testingClient.testing().getUserByUsernameFromFedProviderFactory(realm.getRealm(), "wburke"));
+
+        // Test builtin authentication flows
+        AuthenticationFlowRepresentation clientFlow = testingClient.testing().getClientAuthFlow(realm.getRealm());
+        Assert.assertEquals(DefaultAuthenticationFlows.CLIENT_AUTHENTICATION_FLOW, clientFlow.getAlias());
+        Assert.assertNotNull(realmRsc.flows().getFlow(clientFlow.getId()));
+        Assert.assertTrue(realmRsc.flows().getExecutions(clientFlow.getAlias()).size() > 0);
+
+        AuthenticationFlowRepresentation resetFlow = testingClient.testing().getResetCredFlow(realm.getRealm());
+        Assert.assertEquals(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW, resetFlow.getAlias());
+        Assert.assertNotNull(realmRsc.flows().getFlow(resetFlow.getId()));
+        Assert.assertTrue(realmRsc.flows().getExecutions(resetFlow.getAlias()).size() > 0);
+
+        // Test protocol mappers. Default application has all the builtin protocol mappers. OtherApp just gss credential
+        List<ProtocolMapperRepresentation> applicationMappers = application.getProtocolMappers();
+        Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));//application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
+        Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "email"));
+        Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "given name"));
+        Assert.assertNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
+
+        Assert.assertEquals(1, otherApp.getProtocolMappers().size());
+        List<ProtocolMapperRepresentation> otherAppMappers = otherApp.getProtocolMappers();
+        Assert.assertNull(findMapperByName(otherAppMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
+        ProtocolMapperRepresentation gssCredentialMapper = findMapperByName(otherAppMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
+        assertGssProtocolMapper(gssCredentialMapper);
+
+        // Test clientTemplates
+        List<ClientTemplateRepresentation> clientTemplates = realmRsc.clientTemplates().findAll();
+        Assert.assertEquals(1, clientTemplates.size());
+        ClientTemplateRepresentation clientTemplate = clientTemplates.get(0);
+        Assert.assertEquals("foo-template", clientTemplate.getName());
+        Assert.assertEquals("foo-template-desc", clientTemplate.getDescription());
+        Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, clientTemplate.getProtocol());
+        Assert.assertEquals(1, clientTemplate.getProtocolMappers().size());
+        List<ProtocolMapperRepresentation> clientTemplateMappers = clientTemplate.getProtocolMappers();
+        ProtocolMapperRepresentation templateGssCredentialMapper = findMapperByName(clientTemplateMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
+        assertGssProtocolMapper(templateGssCredentialMapper);
+
+        // Test client template scopes
+        Set<RoleRepresentation> allClientTemplateScopes = allScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));
+        Assert.assertEquals(3, allClientTemplateScopes.size());
+        Assert.assertTrue(containsRole(allClientTemplateScopes, findRealmRole(realmRsc, "admin")));//allClientTemplateScopes.contains(realm.getRole("admin")));
+        Assert.assertTrue(containsRole(allClientTemplateScopes, findClientRole(realmRsc, application.getId(), "app-user")));//allClientTemplateScopes.contains(application.getRole("app-user")));
+        Assert.assertTrue(containsRole(allClientTemplateScopes, findClientRole(realmRsc, application.getId(), "app-admin")));//allClientTemplateScopes.contains(application.getRole("app-admin")));
+
+        List<RoleRepresentation> clientTemplateRealmScopes = realmScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));
+        Assert.assertTrue(containsRole(clientTemplateRealmScopes, findRealmRole(realmRsc, "admin")));//clientTemplateRealmScopes.contains(realm.getRole("admin")));
+
+        List<RoleRepresentation> clientTemplateAppScopes = clientScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));//application.getClientScopeMappings(oauthClient);
+        Assert.assertTrue(containsRole(clientTemplateAppScopes, findClientRole(realmRsc, application.getId(), "app-user")));//clientTemplateAppScopes.contains(application.getRole("app-user")));
+        Assert.assertTrue(containsRole(clientTemplateAppScopes, findClientRole(realmRsc, application.getId(), "app-admin")));//clientTemplateAppScopes.contains(application.getRole("app-admin")));
+
+        // Test user consents
+        //admin =  session.users().getUserByUsername("admin", realm);
+
+        UserResource adminRsc = realmRsc.users().get(admin.getId());
+        List<Map<String, Object>> consents = adminRsc.getConsents();
+        Assert.assertEquals(2, consents.size());//.getConsents().size());
+
+        Map<String, Object> appAdminConsent = findConsentByClientId(consents, application.getClientId());
+        Assert.assertEquals(2, calcNumberGrantedRoles(appAdminConsent));
+        Assert.assertTrue(getGrantedProtocolMappers(appAdminConsent) == null || getGrantedProtocolMappers(appAdminConsent).isEmpty());
+        Assert.assertTrue(isRealmRoleGranted(appAdminConsent, "admin"));//appAdminConsent.isRoleGranted(realm.getRole("admin")));
+        Assert.assertTrue(isClientRoleGranted(appAdminConsent, application.getClientId(), "app-admin"));//appAdminConsent.isRoleGranted(application.getRole("app-admin")));
+
+        Map<String, Object> otherAppAdminConsent = findConsentByClientId(consents, otherApp.getClientId());//admin.getConsentByClient(otherApp.getId());
+        Assert.assertEquals(1, calcNumberGrantedRoles(otherAppAdminConsent));
+        Assert.assertEquals(1, getGrantedProtocolMappers(otherAppAdminConsent).size());//otherAppAdminConsent.getGrantedProtocolMappers().size());
+        Assert.assertTrue(isRealmRoleGranted(otherAppAdminConsent, "admin"));//otherAppAdminConsent.isRoleGranted(realm.getRole("admin")));
+        Assert.assertFalse(isClientRoleGranted(otherAppAdminConsent, application.getClientId(), "app-admin"));//otherAppAdminConsent.isRoleGranted(application.getRole("app-admin")));
+        Assert.assertTrue(isProtocolMapperGranted(otherAppAdminConsent, gssCredentialMapper));
+
+        Assert.assertTrue(application.isStandardFlowEnabled());
+        Assert.assertTrue(application.isImplicitFlowEnabled());
+        Assert.assertTrue(application.isDirectAccessGrantsEnabled());
+        Assert.assertFalse(otherApp.isStandardFlowEnabled());
+        Assert.assertFalse(otherApp.isImplicitFlowEnabled());
+        Assert.assertFalse(otherApp.isDirectAccessGrantsEnabled());
+
+        // Test service accounts
+        Assert.assertFalse(application.isServiceAccountsEnabled());
+        Assert.assertTrue(otherApp.isServiceAccountsEnabled());
+        Assert.assertNull(testingClient.testing().getUserByServiceAccountClient(realm.getRealm(), application.getClientId()));//session.users().getUserByServiceAccountClient(application));
+        UserRepresentation linked = testingClient.testing().getUserByServiceAccountClient(realm.getRealm(), otherApp.getClientId());//session.users().getUserByServiceAccountClient(otherApp);
+        Assert.assertNotNull(linked);
+        Assert.assertEquals("my-service-user", linked.getUsername());
+    }
+
+    private static boolean isProtocolMapperGranted(Map<String, Object> consent, ProtocolMapperRepresentation mapperRep) {
+        Map<String, List> grantedMappers = (Map<String, List>)consent.get("grantedProtocolMappers");
+        if (grantedMappers == null) return false;
+        List<String> mappers = grantedMappers.get(mapperRep.getProtocol());
+        if (mappers == null) return false;
+        return mappers.contains(mapperRep.getName());
+    }
+
+    private static boolean isRealmRoleGranted(Map<String, Object> consent, String roleName) {
+        if (consent.get("grantedRealmRoles") == null) return false;
+        return ((List)consent.get("grantedRealmRoles")).contains(roleName);
+    }
+
+    private static boolean isClientRoleGranted(Map<String, Object> consent, String clientId, String roleName) {
+        if (consent.get("grantedClientRoles") == null) return false;
+        Map<String, List> grantedClientRoles = (Map<String, List>)consent.get("grantedClientRoles");
+        List rolesForClient = grantedClientRoles.get(clientId);
+        if (rolesForClient == null) return false;
+        return rolesForClient.contains(roleName);
+    }
+
+    private static Map<String, List<String>> getGrantedProtocolMappers(Map<String, Object> consent) {
+        return (Map<String, List<String>>)consent.get("grantedProtocolMappers");
+    }
+
+    private static int calcNumberGrantedRoles(Map<String, Object> consent) {
+        int numGranted = 0;
+        List realmRoles = (List)consent.get("grantedRealmRoles");
+        if (realmRoles != null) numGranted += realmRoles.size();
+        Map clientRoles = (Map)consent.get("grantedClientRoles");
+        if (clientRoles != null) numGranted += clientRoles.size();
+        return numGranted;
+    }
+
+    private static Map<String, Object> findConsentByClientId(List<Map<String, Object>> consents, String clientId) {
+        for (Map<String, Object> consent : consents) {
+            if (clientId.equals(consent.get("clientId"))) return consent;
+        }
+        return null;
+    }
+
+    private static void assertGssProtocolMapper(ProtocolMapperRepresentation gssCredentialMapper) {
+        Assert.assertEquals(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, gssCredentialMapper.getName());
+        Assert.assertEquals( OIDCLoginProtocol.LOGIN_PROTOCOL, gssCredentialMapper.getProtocol());
+        Assert.assertEquals(UserSessionNoteMapper.PROVIDER_ID, gssCredentialMapper.getProtocolMapper());
+        String includeInAccessToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
+        String includeInIdToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
+        Assert.assertTrue(includeInAccessToken.equalsIgnoreCase("true"));
+        Assert.assertTrue(includeInIdToken == null || Boolean.parseBoolean(includeInIdToken) == false);
+    }
+
+    private static ProtocolMapperRepresentation findMapperByName(List<ProtocolMapperRepresentation> mappers, String type, String name) {
+        for (ProtocolMapperRepresentation mapper : mappers) {
+            if (mapper.getProtocol().equals(type) &&
+                mapper.getName().equals(name)) {
+                return mapper;
+            }
+        }
+        return null;
+    }
+
+    private static boolean hasClient(List<ClientRepresentation> clients, ClientRepresentation client) {
+        for (ClientRepresentation clientRep : clients) {
+            if (client.getId().equals(clientRep.getId())) return true;
+        }
+        return false;
+    }
+
+    // Workaround for KEYCLOAK-3104.  For this realm, search() only works if username is null.
+    private static UserRepresentation findByUsername(RealmResource realmRsc, String username) {
+        for (UserRepresentation user : realmRsc.users().search(null, 0, Integer.MAX_VALUE)) {
+            if (user.getUsername().equalsIgnoreCase(username)) return user;
+        }
+        return null;
+    }
+
+    private static Set<RoleRepresentation> allScopeMappings(ClientResource client) {
+        Set<RoleRepresentation> allRoles = new HashSet<>();
+        List<RoleRepresentation> realmRoles = realmScopeMappings(client);
+        if (realmRoles != null) allRoles.addAll(realmRoles);
+
+        allRoles.addAll(clientScopeMappings(client));
+
+        return allRoles;
+    }
+
+    private static Set<RoleRepresentation> allScopeMappings(ClientTemplateResource client) {
+        Set<RoleRepresentation> allRoles = new HashSet<>();
+        List<RoleRepresentation> realmRoles = realmScopeMappings(client);
+        if (realmRoles != null) allRoles.addAll(realmRoles);
+
+        allRoles.addAll(clientScopeMappings(client));
+
+        return allRoles;
+    }
+
+    private static List<RoleRepresentation> clientScopeMappings(ClientResource client) {
+        List<RoleRepresentation> clientScopeMappings = new LinkedList<>();
+        Map<String, ClientMappingsRepresentation> clientRoles = client.getScopeMappings().getAll().getClientMappings();
+        if (clientRoles == null) return clientScopeMappings;
+
+        for (String clientKey : clientRoles.keySet()) {
+            List<RoleRepresentation> clientRoleScopeMappings = clientRoles.get(clientKey).getMappings();
+            if (clientRoleScopeMappings != null) clientScopeMappings.addAll(clientRoleScopeMappings);
+        }
+
+        return clientScopeMappings;
+    }
+
+    private static List<RoleRepresentation> clientScopeMappings(ClientTemplateResource client) {
+        List<RoleRepresentation> clientScopeMappings = new LinkedList<>();
+        Map<String, ClientMappingsRepresentation> clientRoles = client.getScopeMappings().getAll().getClientMappings();
+        if (clientRoles == null) return clientScopeMappings;
+
+        for (String clientKey : clientRoles.keySet()) {
+            List<RoleRepresentation> clientRoleScopeMappings = clientRoles.get(clientKey).getMappings();
+            if (clientRoleScopeMappings != null) clientScopeMappings.addAll(clientRoleScopeMappings);
+        }
+
+        return clientScopeMappings;
+    }
+
+    private static List<RoleRepresentation> realmScopeMappings(ClientResource client) {
+        return client.getScopeMappings().realmLevel().listAll();
+    }
+
+    private static List<RoleRepresentation> realmScopeMappings(ClientTemplateResource client) {
+        return client.getScopeMappings().realmLevel().listAll();
+    }
+
+    private static Set<RoleRepresentation> allRoles(RealmResource realmRsc, UserRepresentation user) {
+        UserResource userRsc = realmRsc.users().get(user.getId());
+        Set<RoleRepresentation> roles = new HashSet<>();
+
+        List<RoleRepresentation> realmRoles = userRsc.roles().getAll().getRealmMappings();
+        if (realmRoles != null) roles.addAll(realmRoles);
+
+        roles.addAll(allClientRolesForUser(realmRsc, user));
+
+        return roles;
+    }
+
+    private static List<RoleRepresentation> realmRolesForUser(RealmResource realmRsc, UserRepresentation user) {
+        return realmRsc.users().get(user.getId()).roles().getAll().getRealmMappings();
+    }
+
+    private static List<RoleRepresentation> allClientRolesForUser(RealmResource realmRsc, UserRepresentation user) {
+        UserResource userRsc = realmRsc.users().get(user.getId());
+        List<RoleRepresentation> roles = new LinkedList<>();
+        for(String client : userRsc.roles().getAll().getClientMappings().keySet()) {
+            List<RoleRepresentation> clientRoles = userRsc.roles().getAll().getClientMappings().get(client).getMappings();
+            if (clientRoles != null) roles.addAll(clientRoles);
+        }
+        return roles;
+    }
+
+    private static List<RoleRepresentation> clientRolesForUser(RealmResource realmRsc, ClientRepresentation client, UserRepresentation user) {
+        UserResource userRsc = realmRsc.users().get(user.getId());
+        return userRsc.roles().clientLevel(client.getId()).listAll();
+    }
+
+    private static RoleRepresentation findRealmRole(RealmResource realmRsc, String roleName) {
+        return realmRsc.roles().get(roleName).toRepresentation();
+    }
+
+    private static RoleRepresentation findClientRole(RealmResource realmRsc, String clientDbId, String roleName) {
+        return realmRsc.clients().get(clientDbId).roles().get(roleName).toRepresentation();
+    }
+
+    private static boolean containsRole(Collection<RoleRepresentation> roles, RoleRepresentation role) {
+        for (RoleRepresentation setRole : roles) {
+            if (setRole.getId().equals(role.getId())) return true;
+        }
+        return false;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
index a22c359..90178a9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
@@ -26,24 +26,22 @@ import org.keycloak.models.Constants;
 import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.AssertEvents.ExpectedEvent;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.AppPage.RequestType;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.pages.LoginTotpPage;
 import org.keycloak.testsuite.pages.RegisterPage;
 
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
 import java.net.MalformedURLException;
 import org.jboss.arquillian.graphene.page.Page;
-import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.util.GreenMailRule;
 import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.testsuite.util.RealmRepUtil;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -53,7 +51,7 @@ public class BruteForceTest extends TestRealmKeycloakTest {
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
-        UserRepresentation user = findUserInRealmRep(testRealm, "test-user@localhost");
+        UserRepresentation user = RealmRepUtil.findUser(testRealm, "test-user@localhost");
         CredentialRepresentation credRep = new CredentialRepresentation();
         credRep.setType(CredentialRepresentation.TOTP);
         credRep.setValue("totpSecret");
@@ -63,7 +61,8 @@ public class BruteForceTest extends TestRealmKeycloakTest {
         testRealm.setBruteForceProtected(true);
         testRealm.setFailureFactor(2);
 
-        findClientInRealmRep(testRealm, "test-app").setDirectAccessGrantsEnabled(true);
+        RealmRepUtil.findClientByClientId(testRealm, "test-app").setDirectAccessGrantsEnabled(true);
+        testRealm.getUsers().add(UserBuilder.create().username("user2").email("user2@localhost").password("password").build());
     }
 
     @Before
@@ -110,33 +109,11 @@ public class BruteForceTest extends TestRealmKeycloakTest {
     }
 
     protected void clearUserFailures() throws Exception {
-        String token = getAdminToken();
-        Client client = ClientBuilder.newClient();
-        Response response = client.target(AppPage.AUTH_SERVER_URL)
-                .path("admin/realms/test/attack-detection/brute-force/usernames/test-user@localhost")
-                .request()
-                .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
-                .delete();
-        Assert.assertEquals(204, response.getStatus());
-        response.close();
-        client.close();
-
-
+        adminClient.realm("test").attackDetection().clearBruteForceForUser(findUser("test-user@localhost").getId());
     }
 
     protected void clearAllUserFailures() throws Exception {
-        String token = getAdminToken();
-        Client client = ClientBuilder.newClient();
-        Response response = client.target(AppPage.AUTH_SERVER_URL)
-                .path("admin/realms/test/attack-detection/brute-force/usernames")
-                .request()
-                .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
-                .delete();
-        Assert.assertEquals(204, response.getStatus());
-        response.close();
-        client.close();
-
-
+        adminClient.realm("test").attackDetection().clearAllBruteForce();
     }
 
     @Test
@@ -169,8 +146,8 @@ public class BruteForceTest extends TestRealmKeycloakTest {
             OAuthClient.AccessTokenResponse response = getTestToken("password", totpSecret);
             Assert.assertNull(response.getAccessToken());
             Assert.assertNotNull(response.getError());
-            Assert.assertEquals(response.getError(), "invalid_grant");
-            Assert.assertEquals(response.getErrorDescription(), "Account temporarily disabled");
+            Assert.assertEquals("invalid_grant", response.getError());
+            Assert.assertEquals("Account temporarily disabled", response.getErrorDescription());
             events.clear();
         }
         clearUserFailures();
@@ -293,6 +270,17 @@ public class BruteForceTest extends TestRealmKeycloakTest {
     }
 
     @Test
+    public void testEmail() throws Exception {
+        String userId = adminClient.realm("test").users().search("user2", null, null, null, 0, 1).get(0).getId();
+
+        loginSuccess("user2@localhost");
+        loginInvalidPassword("user2@localhost");
+        loginInvalidPassword("user2@localhost");
+        expectTemporarilyDisabled("user2@localhost", userId);
+        clearAllUserFailures();
+    }
+
+    @Test
     public void testBrowserMissingPassword() throws Exception {
         loginSuccess();
         loginMissingPassword();
@@ -334,20 +322,25 @@ public class BruteForceTest extends TestRealmKeycloakTest {
     }
 
     public void expectTemporarilyDisabled() throws Exception {
-        expectTemporarilyDisabled("test-user@localhost");
+        expectTemporarilyDisabled("test-user@localhost", null);
     }
 
-    public void expectTemporarilyDisabled(String username) throws Exception {
+    public void expectTemporarilyDisabled(String username, String userId) throws Exception {
         loginPage.open();
         loginPage.login(username, "password");
 
         loginPage.assertCurrent();
         String src = driver.getPageSource();
         Assert.assertEquals("Invalid username or password.", loginPage.getError());
-        events.expectLogin().session((String) null).error(Errors.USER_TEMPORARILY_DISABLED)
-                .detail(Details.USERNAME, "test-user@localhost")
-                .removeDetail(Details.CONSENT)
-                .assertEvent();
+        ExpectedEvent event = events.expectLogin()
+                .session((String) null)
+                .error(Errors.USER_TEMPORARILY_DISABLED)
+                .detail(Details.USERNAME, username)
+                .removeDetail(Details.CONSENT);
+        if(userId != null) {
+            event.user(userId);
+        }
+        event.assertEvent();
     }
 
     public void loginSuccess() throws Exception {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
index d9f2182..73b2af4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
@@ -45,6 +45,7 @@ import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.ExecutionBuilder;
 import org.keycloak.testsuite.util.FlowBuilder;
 import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.RealmRepUtil;
 import org.keycloak.testsuite.util.UserBuilder;
 
 import static org.junit.Assert.assertEquals;
@@ -73,7 +74,7 @@ public class CustomFlowTest extends AbstractFlowTest {
                                               .build();
         testRealm.getClients().add(dummyClient);
 
-        ClientRepresentation testApp = findClientInRealmRep(testRealm, "test-app");
+        ClientRepresentation testApp = RealmRepUtil.findClientByClientId(testRealm, "test-app");
         testApp.setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
         testApp.setDirectAccessGrantsEnabled(true);
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginHotpTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginHotpTest.java
index 8964040..43c6153 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginHotpTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginHotpTest.java
@@ -36,6 +36,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.util.GreenMailRule;
+import org.keycloak.testsuite.util.RealmRepUtil;
 import org.keycloak.testsuite.util.UserBuilder;
 
 /**
@@ -48,7 +49,7 @@ public class LoginHotpTest extends TestRealmKeycloakTest {
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
-        UserRepresentation user = findUserInRealmRep(testRealm, "test-user@localhost");
+        UserRepresentation user = RealmRepUtil.findUser(testRealm, "test-user@localhost");
         UserBuilder.edit(user)
                    .hotpSecret("hotpSecret")
                    .otpEnabled();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index 1c04847..66e6bad 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -34,6 +34,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.util.GreenMailRule;
+import org.keycloak.testsuite.util.RealmRepUtil;
 import org.keycloak.testsuite.util.UserBuilder;
 
 /**
@@ -44,7 +45,7 @@ public class LoginTotpTest extends TestRealmKeycloakTest {
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
-        UserRepresentation user = findUserInRealmRep(testRealm, "test-user@localhost");
+        UserRepresentation user = RealmRepUtil.findUser(testRealm, "test-user@localhost");
         UserBuilder.edit(user)
                    .totpSecret("totpSecret")
                    .otpEnabled();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
index 74b0516..900767f 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
@@ -122,7 +122,7 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
         EventRepresentation loginEvent = events.expectLogin().assertEvent();
         String sessionId = loginEvent.getSessionId();
         AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        String tokenResponse = oauth.introspectRefreshTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
         ObjectMapper objectMapper = new ObjectMapper();
         JsonNode jsonNode = objectMapper.readTree(tokenResponse);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
index 0e41cee..4eaba86 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
@@ -28,28 +28,12 @@ import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
 
 /**
  * This class provides loading of the testRealm called "test".  It also
- * provides an OAuthClient for the testRealm.
+ * provides a few utility methods for the testRealm.
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
  */
 public abstract class TestRealmKeycloakTest extends AbstractKeycloakTest {
 
-    protected UserRepresentation findUserInRealmRep(RealmRepresentation testRealm, String userName) {
-        for (UserRepresentation user : testRealm.getUsers()) {
-            if (user.getUsername().equals(userName)) return user;
-        }
-
-        return null;
-    }
-
-    protected ClientRepresentation findClientInRealmRep(RealmRepresentation testRealm, String clientId) {
-        for (ClientRepresentation client : testRealm.getClients()) {
-            if (client.getClientId().equals(clientId)) return client;
-        }
-
-        return null;
-    }
-
     protected RealmResource testRealm() {
         return adminClient.realm("test");
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java
index 93ae0cc..ac90ea8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java
@@ -30,11 +30,15 @@ public class MailAssert {
 
     private static final Logger log = Logger.getLogger(MailAssert.class);
     
-    public static String assertEmailAndGetUrl(String from, String recipient, String content) {
+    public static String assertEmailAndGetUrl(String from, String recipient, String content, Boolean sslEnabled) {
 
         try {
-            MimeMessage message = MailServer.getLastReceivedMessage();
-            assertNotNull("There is no received email.", message);
+            MimeMessage message;
+            if (sslEnabled){
+                message= SslMailServer.getLastReceivedMessage();
+            } else {
+                message = MailServer.getLastReceivedMessage();
+            }            assertNotNull("There is no received email.", message);
             assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString());
             assertEquals(from, message.getFrom()[0].toString());
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmRepUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmRepUtil.java
new file mode 100644
index 0000000..7d5b0c0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmRepUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.util;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class RealmRepUtil {
+
+    // don't allow instance
+    private RealmRepUtil() {
+    }
+
+    public static UserRepresentation findUser(RealmRepresentation testRealm, String userName) {
+        for (UserRepresentation user : testRealm.getUsers()) {
+            if (user.getUsername().equals(userName)) return user;
+        }
+
+        return null;
+    }
+
+    public static ClientRepresentation findClientByClientId(RealmRepresentation testRealm, String clientId) {
+        for (ClientRepresentation client : testRealm.getClients()) {
+            if (client.getClientId().equals(clientId)) return client;
+        }
+
+        return null;
+    }
+
+    public static ClientRepresentation findClientById(RealmRepresentation testRealm, String id) {
+        for (ClientRepresentation client : testRealm.getClients()) {
+            if (client.getId().equals(id)) return client;
+        }
+        return null;
+    }
+
+    public static RoleRepresentation findRealmRole(RealmRepresentation realm, String roleName) {
+        if (realm.getRoles() == null) return null;
+        if (realm.getRoles().getRealm() == null) return null;
+        for (RoleRepresentation role : realm.getRoles().getRealm()) {
+            if (role.getName().equals(roleName)) return role;
+        }
+
+        return null;
+    }
+
+    public static RoleRepresentation findClientRole(RealmRepresentation realm, String clientId, String roleName) {
+        if (realm.getRoles() == null) return null;
+        if (realm.getRoles().getClient() == null) return null;
+        if (realm.getRoles().getClient().get(clientId) == null) return null;
+        for (RoleRepresentation role : realm.getRoles().getClient().get(clientId)) {
+            if (roleName.equals(role.getName())) return role;
+        }
+
+        return null;
+    }
+
+    public static String findDefaultRole(RealmRepresentation realm, String roleName) {
+        if (realm.getDefaultRoles() == null) return null;
+        for (String role : realm.getDefaultRoles()) {
+            if (role.equals(roleName)) return role;
+        }
+
+        return null;
+    }
+
+    public static Set<RoleRepresentation> allRoles(RealmRepresentation realm, UserRepresentation user) {
+        Set<RoleRepresentation> allRoles = new HashSet<>();
+        for (String roleName : user.getRealmRoles()) {
+            allRoles.add(findRealmRole(realm, roleName));
+        }
+
+        for (String clientId : user.getClientRoles().keySet()) {
+            for (String roleName : user.getClientRoles().get(clientId)) {
+                allRoles.add(findClientRole(realm, clientId, roleName));
+            }
+        }
+
+        return allRoles;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java
new file mode 100644
index 0000000..0088e42
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.util;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.*;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.mail.internet.MimeMessage;
+import javax.net.ssl.*;
+
+import org.jboss.logging.Logger;
+import org.subethamail.smtp.server.SMTPServer;
+
+import static org.keycloak.testsuite.util.MailServerConfiguration.*;
+import static org.keycloak.testsuite.util.MailServerConfiguration.PORT_SSL;
+import static org.keycloak.testsuite.util.MailServerConfiguration.STARTTLS;
+
+public class SslMailServer {
+
+    private static final Logger log = Logger.getLogger(MailServer.class);
+
+    public static final String PRIVATE_KEY = "keystore/keycloak.jks";
+
+    public static final String TRUSTED_CERTIFICATE = "keystore/keycloak.truststore";
+
+    //private key tested with invalid certificate
+    public static final String INVALID_KEY = "keystore/email_invalid.jks";
+
+    private static MessageHandlerFactoryImpl messageHandlerFactory = new MessageHandlerFactoryImpl();
+
+    private static SMTPServer smtpServer;
+
+    private static Map<String, String> serverConfiguration = new HashMap<>();
+
+
+    public static void start() {
+        smtpServer = new SMTPServer(messageHandlerFactory);
+        smtpServer.setHostName(HOST);
+        smtpServer.setPort(Integer.parseInt(PORT));
+        smtpServer.start();
+
+        log.info("Started mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
+    }
+
+    public static void stop() {
+        if (smtpServer != null) {
+            log.info("Stopping mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
+            // Suppress error from SubEthaSmtp on shutdown
+            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+                @Override
+                public void uncaughtException(Thread t, Throwable e) {
+                    if (!(e.getCause() instanceof SocketException && e.getStackTrace()[0].getClassName()
+                            .equals("org.subethamail.smtp.server.Session"))) {
+                        log.error("Exception in thread \"" + t.getName() + "\" ");
+                        log.error(e.getMessage(), e);
+                    }
+                }
+            });
+            smtpServer.stop();
+        }
+    }
+
+    public static void startWithSsl(String privateKey){
+        InputStream keyStoreIS = null;
+        try {
+            keyStoreIS = new FileInputStream(privateKey);
+            char[] keyStorePassphrase = "secret".toCharArray();
+            KeyStore ksKeys = null;
+            ksKeys = KeyStore.getInstance("JKS");
+            ksKeys.load(keyStoreIS, keyStorePassphrase);
+
+            // KeyManager decides which key material to use.
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+            kmf.init(ksKeys, keyStorePassphrase);
+
+            // Trust store for client authentication.
+            InputStream trustStoreIS = new FileInputStream(String.valueOf(MailServer.class.getClassLoader().getResource(TRUSTED_CERTIFICATE).getFile()));
+            char[] trustStorePassphrase = "secret".toCharArray();
+            KeyStore ksTrust = KeyStore.getInstance("JKS");
+            ksTrust.load(trustStoreIS, trustStorePassphrase);
+
+            // TrustManager decides which certificate authorities to use.
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+            tmf.init(ksTrust);
+
+            final SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+            smtpServer = new SMTPServer(messageHandlerFactory) {
+                @Override
+                public SSLSocket createSSLSocket(Socket socket) throws IOException {
+                    InetSocketAddress remoteAddress =
+                            (InetSocketAddress) socket.getRemoteSocketAddress();
+                    SSLSocketFactory sf = sslContext.getSocketFactory();
+                    SSLSocket s = (SSLSocket) (sf.createSocket(
+                            socket, remoteAddress.getHostName(), socket.getPort(), true));
+
+                    // we are a server
+                    s.setUseClientMode(false);
+
+                    // select protocols and cipher suites
+                    s.setEnabledProtocols(s.getSupportedProtocols());
+                    s.setEnabledCipherSuites(s.getSupportedCipherSuites());
+                    return s;
+                }
+            };
+        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyManagementException | CertificateException e) {
+            throw new RuntimeException(e);
+        }
+
+        smtpServer.setHostName(HOST);
+        smtpServer.setPort(Integer.parseInt(PORT_SSL));
+        smtpServer.setEnableTLS(true);
+        smtpServer.start();
+
+        log.info("Started mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
+    }
+
+    public static Map<String, String> getServerConfiguration() {
+        serverConfiguration.put("from", FROM);
+        serverConfiguration.put("host", HOST);
+        serverConfiguration.put("port", PORT_SSL);
+        serverConfiguration.put("starttls", STARTTLS);
+        return serverConfiguration;
+    }
+
+    public static MimeMessage getLastReceivedMessage() throws InterruptedException {
+        return messageHandlerFactory.getMessage();
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
index 79d050a..9a4a7f6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
@@ -2,7 +2,7 @@
     "id": "demo",
     "realm": "demo",
     "enabled": true,
-    "accessTokenLifespan": 3000,
+    "accessTokenLifespan": 600,
     "accessCodeLifespan": 10,
     "accessCodeLifespanUserAction": 6000,
     "sslRequired": "external",
@@ -209,6 +209,16 @@
             "secret": "password"
         },
         {
+            "clientId": "token-min-ttl",
+            "enabled": true,
+            "adminUrl": "/token-min-ttl",
+            "baseUrl": "/token-min-ttl",
+            "redirectUris": [
+                "/token-min-ttl/*"
+            ],
+            "secret": "password"
+        },
+        {
             "clientId": "third-party",
             "enabled": true,
             "redirectUris": [
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/offline-client/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/offline-client/WEB-INF/web.xml
index a329f9a..66acbed 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/offline-client/WEB-INF/web.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/offline-client/WEB-INF/web.xml
@@ -23,6 +23,11 @@
 
     <module-name>offline-client</module-name>
 
+    <filter>
+        <filter-name>AdapterActionsFilter</filter-name>
+        <filter-class>org.keycloak.testsuite.adapter.filter.AdapterActionsFilter</filter-class>
+    </filter>
+
     <servlet>
         <servlet-name>Servlet</servlet-name>
         <servlet-class>org.keycloak.testsuite.adapter.servlet.OfflineTokenServlet</servlet-class>
@@ -33,6 +38,11 @@
         <servlet-class>org.keycloak.testsuite.adapter.servlet.ErrorServlet</servlet-class>
     </servlet>
 
+    <filter-mapping>
+        <filter-name>AdapterActionsFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
     <servlet-mapping>
         <servlet-name>Servlet</servlet-name>
         <url-pattern>/*</url-pattern>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..8c59313
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/jetty-web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Get name="securityHandler">
+        <Set name="authenticator">
+            <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
+                <!--
+                <Set name="adapterConfig">
+                    <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+                        <Set name="realm">tomcat</Set>
+                        <Set name="resource">customer-portal</Set>
+                        <Set name="authServerUrl">http://localhost:8180/auth</Set>
+                        <Set name="sslRequired">external</Set>
+                        <Set name="credentials">
+                            <Map>
+                                <Entry>
+                                    <Item>secret</Item>
+                                    <Item>password</Item>
+                                </Entry>
+                            </Map>
+                        </Set>
+                        <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+                    </New>
+                </Set>
+                -->
+            </New>
+        </Set>
+    </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml
new file mode 100644
index 0000000..f8110bc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>token-min-ttl</module-name>
+
+    <filter>
+        <filter-name>AdapterActionsFilter</filter-name>
+        <filter-class>org.keycloak.testsuite.adapter.filter.AdapterActionsFilter</filter-class>
+    </filter>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.TokenMinTTLServlet</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>Error Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.ErrorServlet</servlet-class>
+    </servlet>
+
+    <filter-mapping>
+        <filter-name>AdapterActionsFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Error Servlet</servlet-name>
+        <url-pattern>/error.html</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Errors</web-resource-name>
+            <url-pattern>/error.html</url-pattern>
+        </web-resource-collection>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK</auth-method>
+        <realm-name>test</realm-name>
+        <form-login-config>
+            <form-login-page>/error.html</form-login-page>
+            <form-error-page>/error.html</form-error-page>
+        </form-login-config>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/email_invalid.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/email_invalid.jks
new file mode 100644
index 0000000..e940f19
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/email_invalid.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.jks
new file mode 100644
index 0000000..81570ab
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore
new file mode 100644
index 0000000..2df5170
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index 9208927..c37291d 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -27,6 +27,10 @@
         "provider": "${keycloak.userSessionPersister.provider:jpa}"
     },
 
+    "authorizationPersister": {
+        "provider": "${keycloak.authorization.provider:jpa}"
+    },
+
     "userCache": {
         "provider": "${keycloak.user.cache.provider:default}",
         "default" : {
@@ -117,10 +121,10 @@
 
     "truststore": {
         "file": {
-            "file": "${keycloak.truststore.file:src/main/keystore/keycloak.truststore}",
+            "file": "${keycloak.truststore.file:src/test/resources/keystore/keycloak.truststore}",
             "password": "${keycloak.truststore.password:secret}",
             "hostname-verification-policy": "${keycloak.truststore.policy:WILDCARD}",
-            "disabled": "${keycloak.truststore.disabled:true}"
+            "disabled": "${keycloak.truststore.disabled:false}"
         }
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
new file mode 100755
index 0000000..23be719
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
@@ -0,0 +1,290 @@
+{
+    "realm": "test-realm",
+    "enabled": true,
+    "accessTokenLifespan": 6000,
+    "accessTokenLifespanForImplicitFlow": 1500,
+    "accessCodeLifespan": 30,
+    "accessCodeLifespanUserAction": 600,
+    "offlineSessionIdleTimeout": 3600000,
+    "requiredCredentials": [ "password" ],
+    "defaultRoles": [ "foo", "bar" ],
+    "verifyEmail" : "true",
+    "smtpServer": {
+        "from": "auto@keycloak.org",
+        "host": "localhost",
+        "port":"3025"
+    },
+    "identityProviders" : [
+        {
+            "providerId" : "google",
+            "alias" : "google1",
+            "enabled": true,
+            "config": {
+                "clientId": "googleId",
+                "clientSecret": "googleSecret"
+            }
+        },
+        {
+            "providerId" : "facebook",
+            "alias" : "facebook1",
+            "enabled": true,
+            "config": {
+                "clientId": "facebookId",
+                "clientSecret": "facebookSecret"
+            }
+        },
+        {
+            "providerId" : "twitter",
+            "alias" : "twitter1",
+            "enabled": true,
+            "config": {
+                "clientId": "twitterId",
+                "clientSecret": "twitterSecret"
+            }
+        }
+    ],
+    "userFederationProviders": [
+        {
+            "displayName": "MyLDAPProvider1",
+            "providerName": "ldap",
+            "priority": 1,
+            "config": {
+                "connectionUrl": "ldap://foo"
+            }
+        },
+        {
+            "displayName": "MyLDAPProvider2",
+            "providerName": "ldap",
+            "priority": 2,
+            "config": {
+                "connectionUrl": "ldap://bar"
+            }
+        }
+    ],
+    "userFederationMappers": [
+        {
+            "name": "FullNameMapper",
+            "federationProviderDisplayName": "MyLDAPProvider1",
+            "federationMapperType": "full-name-ldap-mapper",
+            "config": {
+                "ldap.full.name.attribute": "cn"
+            }
+        }
+    ],
+    "users": [
+        {
+            "username": "wburke",
+            "enabled": true,
+            "createdTimestamp" : 123654,
+            "attributes": {
+                "email": "bburke@redhat.com"
+            },
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "userpassword"
+                }
+            ],
+            "applicationRoles": {
+                "Application": [ "app-user" ],
+                "OtherApp": [  "otherapp-user" ]
+            }
+        },
+        {
+            "username": "loginclient",
+            "createdTimestamp" : "123655",
+            "enabled": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "clientpassword"
+                }
+            ]
+        },
+        {
+            "username": "admin",
+            "enabled": true,
+            "attributes": {
+                "key1": [
+                    "val1"
+                ],
+                "key2": [
+                    "val21",
+                    "val22"
+                ]
+            },
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "adminpassword"
+                }
+            ],
+            "realmRoles": [ "admin" ],
+            "applicationRoles": {
+                "Application": [ "app-admin" ],
+                "OtherApp": [  "otherapp-admin" ]
+            },
+            "clientConsents": [
+                {
+                    "clientId": "Application",
+                    "grantedRealmRoles": [ "admin" ],
+                    "grantedClientRoles": {
+                        "Application": [ "app-admin" ]
+                    }
+                },
+                {
+                    "clientId": "OtherApp",
+                    "grantedRealmRoles": [ "admin" ],
+                    "grantedProtocolMappers": {
+                        "openid-connect": [ "gss delegation credential" ]
+                    }
+                }
+            ]
+        },
+        {
+            "username": "mySocialUser",
+            "enabled": true,
+            "federatedIdentities": [
+                {
+                    "identityProvider": "facebook1",
+                    "userId": "facebook1",
+                    "userName": "fbuser1"
+                },
+                {
+                    "identityProvider": "twitter1",
+                    "userId": "twitter1",
+                    "userName": "twuser1"
+                },
+                {
+                    "identityProvider": "google1",
+                    "userId": "google1",
+                    "userName": "mySocialUser@gmail.com"
+                }
+            ]
+        },
+        {
+            "username": "my-service-user",
+            "enabled": true,
+            "serviceAccountClientId": "OtherApp"
+        }
+    ],
+    "clients": [
+        {
+            "clientId": "Application",
+            "name": "Applicationn",
+            "enabled": true,
+            "implicitFlowEnabled": true,
+            "directAccessGrantsEnabled": true,
+            "nodeReRegistrationTimeout": 50,
+            "registeredNodes": {
+                "node1": 10,
+                "172.10.15.20": 20
+            }
+        },
+        {
+            "clientId": "OtherApp",
+            "name": "Other Application",
+            "enabled": true,
+            "standardFlowEnabled": false,
+            "directAccessGrantsEnabled": false,
+            "serviceAccountsEnabled": true,
+            "clientAuthenticatorType": "client-jwt",
+            "protocolMappers" : [
+                {
+                    "name" : "gss delegation credential",
+                    "protocol" : "openid-connect",
+                    "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+                    "consentRequired" : true,
+                    "consentText" : "gss delegation credential",
+                    "config" : {
+                        "user.session.note" : "gss_delegation_credential",
+                        "access.token.claim" : "true",
+                        "claim.name" : "gss_delegation_credential",
+                        "Claim JSON Type" : "String"
+                    }
+                }
+            ]
+        }
+    ],
+    "oauthClients" : [
+        {
+            "name" : "oauthclient",
+            "enabled": true,
+            "secret": "clientpassword"
+        }
+    ],
+    "clientTemplates" : [
+        {
+            "name" : "foo-template",
+            "description" : "foo-template-desc",
+            "protocol" : "openid-connect",
+            "protocolMappers" : [
+                {
+                    "name" : "gss delegation credential",
+                    "protocol" : "openid-connect",
+                    "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+                    "consentRequired" : true,
+                    "consentText" : "gss delegation credential",
+                    "config" : {
+                        "user.session.note" : "gss_delegation_credential",
+                        "access.token.claim" : "true",
+                        "claim.name" : "gss_delegation_credential",
+                        "Claim JSON Type" : "String"
+                    }
+                }
+            ]
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "admin"
+            }
+        ],
+        "application" : {
+            "Application" : [
+                {
+                    "name": "app-admin",
+                    "scopeParamRequired": true
+                },
+                {
+                    "name": "app-user"
+                }
+            ],
+            "OtherApp" : [
+                {
+                    "name": "otherapp-admin",
+                    "scopeParamRequired": false
+                },
+                {
+                    "name": "otherapp-user"
+                }
+            ]
+        }
+    },
+    "scopeMappings": [
+        {
+            "client": "oauthclient",
+            "roles": ["admin"]
+        },
+        {
+            "clientTemplate": "foo-template",
+            "roles": ["admin"]
+        }
+    ],
+    "applicationScopeMappings": {
+        "Application": [
+            {
+                "client": "oauthclient",
+                "roles": ["app-user"]
+            },
+            {
+                "clientTemplate": "foo-template",
+                "roles": ["app-user", "app-admin" ]
+            }
+        ]
+
+    }
+
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm2.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm2.json
new file mode 100755
index 0000000..4e3d9fb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm2.json
@@ -0,0 +1,89 @@
+{
+    "realm": "demo-delete",
+    "enabled": true,
+    "accessTokenLifespan": 3000,
+    "accessCodeLifespan": 10,
+    "accessCodeLifespanUserAction": 6000,
+    "sslRequired": "external",
+    "registrationAllowed": false,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "users" : [
+        {
+            "username" : "bburke@redhat.com",
+            "enabled": true,
+            "email" : "bburke@redhat.com",
+            "firstName": "Bill",
+            "lastName": "Burke",
+            "credentials" : [
+                { "type" : "password",
+                  "value" : "password" }
+            ],
+            "realmRoles": ["user"],
+            "applicationRoles": {
+                "account": [ "manage-account" ]
+            }
+
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "User privileges"
+            },
+            {
+                "name": "admin",
+                "description": "Administrator privileges"
+            }
+        ]
+    },
+    "scopeMappings": [
+        {
+            "client": "third-party",
+            "roles": ["user"]
+        },
+        {
+            "client": "customer-portal",
+            "roles": ["user"]
+        },
+        {
+            "client": "product-portal",
+            "roles": ["user"]
+        }
+
+    ],
+    "applications": [
+        {
+            "name": "customer-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/customer-portal",
+            "redirectUris": [
+                "http://localhost:8080/customer-portal/*"
+            ],
+            "secret": "password"
+        },
+        {
+            "name": "product-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/product-portal",
+            "redirectUris": [
+                "http://localhost:8080/product-portal/*"
+            ],
+            "secret": "password"
+        }
+    ],
+    "oauthClients": [
+        {
+            "name": "third-party",
+            "enabled": true,
+            "redirectUris": [
+                "http://localhost:8080/oauth-client/*",
+                "http://localhost:8080/oauth-client-cdi/*"
+            ],
+            "secret": "password"
+        }
+    ]
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-demo.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-demo.json
new file mode 100755
index 0000000..c98bbf7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-demo.json
@@ -0,0 +1,63 @@
+{
+    "realm": "demo",
+    "enabled": true,
+    "accessTokenLifespan": 300,
+    "accessCodeLifespan": 10,
+    "accessCodeLifespanUserAction": 600,
+    "sslRequired": "external",
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "users" : [
+        {
+            "username" : "bburke@redhat.com",
+            "enabled": true,
+            "email" : "bburke@redhat.com",
+            "credentials" : [
+                { "type" : "Password",
+                  "value" : "password" }
+            ],
+            "realmRoles": [ "user" ]
+        }
+    ],
+    "oauthClients" : [
+        {
+            "name" : "third-party",
+            "enabled": true,
+            "secret": "password"
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "Have User privileges"
+            },
+            {
+                "name": "admin",
+                "description": "Have Administrator privileges"
+            }
+        ]
+    },
+
+    "scopeMappings": [
+        {
+            "client": "third-party",
+            "roles": ["user"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "customer-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
+            "secret": "password"
+        },
+        {
+            "name": "product-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/product-portal/j_admin_request",
+            "secret": "password"
+        }
+    ]
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-noclient-id.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-noclient-id.json
new file mode 100755
index 0000000..4751c7f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm-noclient-id.json
@@ -0,0 +1,57 @@
+
+{
+    "realm": "demo-no-client-id",
+    "enabled": true,
+    "accessTokenLifespan": 300,
+    "accessCodeLifespan": 10,
+    "accessCodeLifespanUserAction": 600,
+    "sslRequired": "external",
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "users" : [
+        {
+            "username" : "bburke@redhat.com",
+            "enabled": true,
+            "email" : "bburke@redhat.com",
+            "credentials" : [
+                { "type" : "Password",
+                    "value" : "password" }
+            ],
+            "realmRoles": [ "user" ]
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "Have User privileges"
+            },
+            {
+                "name": "admin",
+                "description": "Have Administrator privileges"
+            }
+        ]
+    },
+    "scopeMappings": [
+        {
+            "client": "third-party",
+            "roles": ["user"]
+        }
+    ],
+    "clients": [
+        {
+            "name": "third-party",
+            "enabled": true,
+            "bearerOnly": true
+        }
+    ],
+    "clientScopeMappings": {
+        "realm-management": [
+            {
+                "client": "some-client",
+                "roles": ["create-client"]
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 08e0638..b53c739 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -214,22 +214,22 @@
                                         <artifactItems>
                                             <artifactItem>
                                                 <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>product-portal-example</artifactId>        
+                                                <artifactId>product-portal-example</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
                                             </artifactItem>
                                             <artifactItem>
                                                 <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>customer-portal-example</artifactId>        
+                                                <artifactId>customer-portal-example</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
-                                            </artifactItem>                                        
+                                            </artifactItem>
                                             <artifactItem>
                                                 <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>database-service</artifactId>        
+                                                <artifactId>database-service</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
-                                            </artifactItem>       
+                                            </artifactItem>
                                             <artifactItem>
                                                 <groupId>org.keycloak.testsuite</groupId>
                                                 <artifactId>integration-arquillian-test-apps-js-console</artifactId>
@@ -237,14 +237,20 @@
                                                 <type>war</type>
                                             </artifactItem>
                                             <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-test-apps-js-database</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>war</type>
+                                            </artifactItem>
+                                            <artifactItem>
                                                 <groupId>org.keycloak</groupId>
-                                                <artifactId>examples-multitenant</artifactId>        
+                                                <artifactId>examples-multitenant</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
-                                            </artifactItem>                    
+                                            </artifactItem>
                                             <artifactItem>
                                                 <groupId>org.keycloak</groupId>
-                                                <artifactId>examples-basicauth</artifactId>        
+                                                <artifactId>examples-basicauth</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
                                             </artifactItem>
@@ -293,7 +299,7 @@
                                         <artifactItems>
                                             <artifactItem>
                                                 <groupId>org.keycloak</groupId>
-                                                <artifactId>keycloak-examples-dist</artifactId>        
+                                                <artifactId>keycloak-examples-dist</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>zip</type>
                                                 <includes>**/*realm.json,**/testsaml.json</includes>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index c3b3ccd..2773015 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -736,6 +736,16 @@
                         </exclusion>
                     </exclusions>
                 </dependency>
+                <dependency>
+                    <groupId>org.subethamail</groupId>
+                    <artifactId>subethasmtp</artifactId>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>org.slf4j</groupId>
+                            <artifactId>slf4j-api</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
 
                 <!-- Keycloak deps for tests -->
 
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 83eaa85..e7fa229 100755
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -56,8 +56,6 @@
         <module>tomcat7</module>
         <module>tomcat8</module>
         <module>jetty</module>
-        <module>performance</module>
-        <module>stress</module>
         <module>integration-arquillian</module>
     </modules>
         
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 8c0727f..6c5ac3c 100755
--- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -53,6 +53,7 @@ role_view-profile=View profile
 role_manage-account=Manage account
 role_read-token=Read token
 role_offline-access=Offline access
+role_uma_authorization=Obtain permissions
 client_account=Account
 client_security-admin-console=Security Admin Console
 client_admin-cli=Admin CLI
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties b/themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
index e2e8743..8b07e7b 100644
--- a/themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
@@ -53,6 +53,7 @@ role_view-profile=Visualiza perfil
 role_manage-account=Gerencia conta
 role_read-token=L\u00EA token
 role_offline-access=Acesso Offline
+role_uma_authorization=Obter permiss\u00F5es
 client_account=Conta
 client_security-admin-console=Console de Administra\u00E7\u00E3o de Seguran\u00E7a
 client_admin-cli=Admin CLI
diff --git a/themes/src/main/resources/theme/base/admin/index.ftl b/themes/src/main/resources/theme/base/admin/index.ftl
index 9f5f94a..a352281 100755
--- a/themes/src/main/resources/theme/base/admin/index.ftl
+++ b/themes/src/main/resources/theme/base/admin/index.ftl
@@ -50,6 +50,20 @@
     <script src="${resourceUrl}/js/controllers/groups.js" type="text/javascript"></script>
     <script src="${resourceUrl}/js/loaders.js" type="text/javascript"></script>
     <script src="${resourceUrl}/js/services.js" type="text/javascript"></script>
+
+    <!-- Authorization -->
+    <script src="${resourceUrl}/js/authz/lib/ace/ace.js" type="text/javascript"></script>
+    <script src="${resourceUrl}/js/authz/lib/ace/mode-javascript.js" type="text/javascript"></script>
+    <script src="${resourceUrl}/js/authz/lib/ace/ui-ace.min.js" type="text/javascript"></script>
+    <script src="${resourceUrl}/js/authz/authz-app.js" type="text/javascript"></script>
+    <script src="${resourceUrl}/js/authz/authz-controller.js" type="text/javascript"></script>
+    <script src="${resourceUrl}/js/authz/authz-services.js" type="text/javascript"></script>
+
+    <#if properties.scripts?has_content>
+        <#list properties.scripts?split(' ') as script>
+            <script type="text/javascript" src="${resourceUrl}/${script}"></script>
+        </#list>
+    </#if>
 </head>
 <body data-ng-controller="GlobalCtrl" data-ng-cloak data-ng-show="auth.user">
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js
new file mode 100644
index 0000000..467038c
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js
@@ -0,0 +1,412 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.
+ */
+
+module.requires.push('ui.ace');
+
+module.config(['$routeProvider', function ($routeProvider) {
+    $routeProvider
+        .when('/realms/:realm/authz', {
+            templateUrl: resourceUrl + '/partials/authz/resource-server-list.html',
+            resolve: {
+                realm: function (RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller: 'ResourceServerCtrl'
+        }).when('/realms/:realm/clients/:client/authz/resource-server/create', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            },
+            clients: function (ClientListLoader) {
+                return ClientListLoader();
+            }
+        },
+        controller: 'ResourceServerDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            },
+            clients: function (ClientListLoader) {
+                return ClientListLoader();
+            },
+            serverInfo: function (ServerInfoLoader) {
+                return ServerInfoLoader();
+            }
+        },
+        controller: 'ResourceServerDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/evaluate', {
+        templateUrl: resourceUrl + '/partials/authz/policy/resource-server-policy-evaluate.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            },
+            clients: function (ClientListLoader) {
+                return ClientListLoader();
+            },
+            roles: function (RoleListLoader) {
+                return new RoleListLoader();
+            }
+        },
+        controller: 'PolicyEvaluateCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/evaluate/result', {
+        templateUrl: resourceUrl + '/partials/authz/policy/resource-server-policy-evaluate-result.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            },
+        },
+        controller: 'PolicyEvaluateCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/resource', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-resource-list.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerResourceCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/resource/create', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-resource-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerResourceDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-resource-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerResourceDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/scope', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-scope-list.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerScopeCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/scope/create', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-scope-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerScopeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/scope/:id', {
+        templateUrl: resourceUrl + '/partials/authz/resource-server-scope-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerScopeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/permission', {
+        templateUrl: resourceUrl + '/partials/authz/permission/resource-server-permission-list.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPermissionCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy', {
+        templateUrl: resourceUrl + '/partials/authz/policy/resource-server-policy-list.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/drools/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-drools-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyDroolsDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/drools/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-drools-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyDroolsDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/permission/resource/create', {
+        templateUrl: resourceUrl + '/partials/authz/permission/provider/resource-server-policy-resource-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyResourceDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/permission/resource/:id', {
+        templateUrl: resourceUrl + '/partials/authz/permission/provider/resource-server-policy-resource-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyResourceDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/permission/scope/create', {
+        templateUrl: resourceUrl + '/partials/authz/permission/provider/resource-server-policy-scope-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyScopeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/permission/scope/:id', {
+        templateUrl: resourceUrl + '/partials/authz/permission/provider/resource-server-policy-scope-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyScopeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/user/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-user-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyUserDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/user/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-user-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyUserDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/role/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-role-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyRoleDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/role/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-role-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyRoleDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/js/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-js-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyJSDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/js/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-js-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyJSDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/time/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-time-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyTimeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/time/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-time-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyTimeDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/aggregate/create', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyAggregateDetailCtrl'
+    }).when('/realms/:realm/clients/:client/authz/resource-server/policy/aggregate/:id', {
+        templateUrl: resourceUrl + '/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html',
+        resolve: {
+            realm: function (RealmLoader) {
+                return RealmLoader();
+            },
+            client : function(ClientLoader) {
+                return ClientLoader();
+            }
+        },
+        controller: 'ResourceServerPolicyAggregateDetailCtrl'
+    });
+}]);
+
+module.directive('kcTabsResourceServer', function () {
+    return {
+        scope: true,
+        restrict: 'E',
+        replace: true,
+        templateUrl: resourceUrl + '/templates/authz/kc-tabs-resource-server.html'
+    }
+});
+
+module.filter('unique', function () {
+
+    return function (items, filterOn) {
+
+        if (filterOn === false) {
+            return items;
+        }
+
+        if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
+            var hashCheck = {}, newItems = [];
+
+            var extractValueToCompare = function (item) {
+                if (angular.isObject(item) && angular.isString(filterOn)) {
+                    return item[filterOn];
+                } else {
+                    return item;
+                }
+            };
+
+            angular.forEach(items, function (item) {
+                var valueToCheck, isDuplicate = false;
+
+                for (var i = 0; i < newItems.length; i++) {
+                    if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
+                        isDuplicate = true;
+                        break;
+                    }
+                }
+                if (!isDuplicate) {
+                    newItems.push(item);
+                }
+
+            });
+            items = newItems;
+        }
+        return items;
+    };
+});
+
+module.filter('toCamelCase', function () {
+    return function (input) {
+        input = input || '';
+        return input.replace(/\w\S*/g, function (txt) {
+            return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
+        });
+    };
+})
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
new file mode 100644
index 0000000..ecbe9d5
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -0,0 +1,1205 @@
+module.controller('ResourceServerCtrl', function($scope, realm, ResourceServer) {
+    $scope.realm = realm;
+
+    ResourceServer.query({realm : realm.realm}, function (data) {
+        $scope.servers = data;
+    });
+});
+
+module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $location, $upload, realm, ResourceServer, client, AuthzDialog, Notifications) {
+    $scope.realm = realm;
+    $scope.client = client;
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = angular.copy(data);
+        $scope.changed = false;
+
+        $scope.$watch('server', function() {
+            if (!angular.equals($scope.server, data)) {
+                $scope.changed = true;
+            }
+        }, true);
+
+        $scope.save = function() {
+            ResourceServer.update({realm : realm.realm, client : $scope.server.clientId}, $scope.server, function() {
+                $route.reload();
+                Notifications.success("The resource server has been created.");
+            });
+        }
+
+        $scope.reset = function() {
+            $scope.server = angular.copy(data);
+            $scope.changed = false;
+        }
+
+        $scope.export = function() {
+            $scope.exportSettings = true;
+            ResourceServer.settings({
+                realm : $route.current.params.realm,
+                client : client.id
+            }, function(data) {
+                var tmp = angular.fromJson(data);
+                $scope.settings = angular.toJson(tmp, true);
+            })
+        }
+
+        $scope.downloadSettings = function() {
+            saveAs(new Blob([$scope.settings], { type: 'application/json' }), $scope.server.name + "-authz-config.json");
+        }
+
+        $scope.cancelExport = function() {
+            delete $scope.settings
+        }
+
+        $scope.onFileSelect = function($files) {
+            $scope.files = $files;
+        };
+
+        $scope.clearFileSelect = function() {
+            $scope.files = null;
+        }
+
+        $scope.uploadFile = function() {
+            //$files: an array of files selected, each file has name, size, and type.
+            for (var i = 0; i < $scope.files.length; i++) {
+                var $file = $scope.files[i];
+                $scope.upload = $upload.upload({
+                    url: authUrl + '/admin/realms/' + $route.current.params.realm  + '/clients/' + client.id + '/authz/resource-server', //upload.php script, node.js route, or servlet url
+                    // method: POST or PUT,
+                    // headers: {'headerKey': 'headerValue'}, withCredential: true,
+                    data: {myObj: ""},
+                    file: $file
+                    /* set file formData name for 'Content-Desposition' header. Default: 'file' */
+                    //fileFormDataName: myFile,
+                    /* customize how data is added to formData. See #40#issuecomment-28612000 for example */
+                    //formDataAppender: function(formData, key, val){}
+                }).progress(function(evt) {
+                    console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
+                }).success(function(data, status, headers) {
+                    $route.reload();
+                    Notifications.success("The resource server has been updated.");
+                }).error(function() {
+                    Notifications.error("The resource server can not be uploaded. Please verify the file.");
+                });
+            }
+        };
+    });
+});
+
+module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerResource, client) {
+    $scope.realm = realm;
+    $scope.client = client;
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        $scope.createPolicy = function(resource) {
+            $location.path('/realms/' + $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/permission/resource/create').search({rsrid: resource._id});
+        }
+
+        ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
+            $scope.resources = data;
+        });
+    });
+});
+
+module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerResource, ResourceServerScope, AuthzDialog, Notifications) {
+    $scope.realm = realm;
+    $scope.client = client;
+
+    ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+        $scope.scopes = data;
+    });
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        var resourceId = $route.current.params.rsrid;
+
+        if (!resourceId) {
+            $scope.create = true;
+            $scope.changed = false;
+
+            var resource = {};
+            resource.scopes = [];
+
+            $scope.resource = angular.copy(resource);
+
+            $scope.$watch('resource', function() {
+                if (!angular.equals($scope.resource, resource)) {
+                    $scope.changed = true;
+                }
+            }, true);
+
+            $scope.save = function() {
+                ResourceServerResource.save({realm : realm.realm, client : $scope.client.id}, $scope.resource, function(data) {
+                    $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource/" + data._id);
+                    Notifications.success("The resource has been created.");
+                });
+            }
+
+            $scope.cancel = function() {
+                $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource/");
+            }
+        } else {
+            ResourceServerResource.get({
+                realm : $route.current.params.realm,
+                client : client.id,
+                rsrid : $route.current.params.rsrid,
+            }, function(data) {
+                $scope.resource = angular.copy(data);
+                $scope.changed = false;
+
+                for (i = 0; i < $scope.resource.scopes.length; i++) {
+                    $scope.resource.scopes[i] = $scope.resource.scopes[i].name;
+                }
+
+                $scope.$watch('resource', function() {
+                    if (!angular.equals($scope.resource, data)) {
+                        $scope.changed = true;
+                    }
+                }, true);
+
+                $scope.save = function() {
+                    ResourceServerResource.update({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, $scope.resource, function() {
+                        $route.reload();
+                        Notifications.success("The resource has been updated.");
+                    });
+                }
+
+                $scope.remove = function() {
+                    var msg = "";
+
+                    if ($scope.resource.policies.length > 0) {
+                        msg = "<p>This resource is referenced in some policies:</p>";
+                        msg += "<ul>";
+                        for (i = 0; i < $scope.resource.policies.length; i++) {
+                            msg+= "<li><strong>" + $scope.resource.policies[i].name + "</strong></li>";
+                        }
+                        msg += "</ul>";
+                        msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
+                    }
+
+                    AuthzDialog.confirmDeleteWithMsg($scope.resource.name, "Resource", msg, function() {
+                        ResourceServerResource.delete({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, null, function() {
+                            $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource");
+                            Notifications.success("The resource has been deleted.");
+                        });
+                    });
+                }
+
+                $scope.reset = function() {
+                    $scope.resource = angular.copy(data);
+                    $scope.changed = false;
+                }
+            });
+        }
+    });
+});
+
+module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) {
+    $scope.realm = realm;
+    $scope.client = client;
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+            $scope.scopes = data;
+        });
+    });
+});
+
+module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) {
+    $scope.realm = realm;
+    $scope.client = client;
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        var scopeId = $route.current.params.id;
+
+        if (!scopeId) {
+            $scope.create = true;
+            $scope.changed = false;
+
+            var scope = {};
+
+            $scope.resource = angular.copy(scope);
+
+            $scope.$watch('scope', function() {
+                if (!angular.equals($scope.scope, scope)) {
+                    $scope.changed = true;
+                }
+            }, true);
+
+            $scope.save = function() {
+                ResourceServerScope.save({realm : realm.realm, client : $scope.client.id}, $scope.scope, function(data) {
+                    $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope/" + data.id);
+                    Notifications.success("The scope has been created.");
+                });
+            }
+        } else {
+            ResourceServerScope.get({
+                realm : $route.current.params.realm,
+                client : client.id,
+                id : $route.current.params.id,
+            }, function(data) {
+                $scope.scope = angular.copy(data);
+                $scope.changed = false;
+
+                $scope.$watch('scope', function() {
+                    if (!angular.equals($scope.scope, data)) {
+                        $scope.changed = true;
+                    }
+                }, true);
+
+                $scope.save = function() {
+                    ResourceServerScope.update({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, $scope.scope, function() {
+                        $scope.changed = false;
+                        Notifications.success("The scope has been updated.");
+                    });
+                }
+
+                $scope.remove = function() {
+                    var msg = "";
+
+                    if ($scope.scope.policies.length > 0) {
+                        msg = "<p>This resource is referenced in some policies:</p>";
+                        msg += "<ul>";
+                        for (i = 0; i < $scope.scope.policies.length; i++) {
+                            msg+= "<li><strong>" + $scope.scope.policies[i].name + "</strong></li>";
+                        }
+                        msg += "</ul>";
+                        msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
+                    }
+
+                    AuthzDialog.confirmDeleteWithMsg($scope.scope.name, "Scope", msg, function() {
+                        ResourceServerScope.delete({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, null, function() {
+                            $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope");
+                            Notifications.success("The scope has been deleted.");
+                        });
+                    });
+                }
+
+                $scope.reset = function() {
+                    $scope.scope = angular.copy(data);
+                    $scope.changed = false;
+                }
+            });
+        }
+    });
+});
+
+module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
+    $scope.realm = realm;
+    $scope.client = client;
+    $scope.policyProviders = [];
+
+    PolicyProvider.query({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function (data) {
+        for (i = 0; i < data.length; i++) {
+            if (data[i].type != 'resource' && data[i].type != 'scope') {
+                $scope.policyProviders.push(data[i]);
+            }
+        }
+    });
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+            $scope.policies = [];
+
+            for (i = 0; i < data.length; i++) {
+                if (data[i].type != 'resource' && data[i].type != 'scope') {
+                    $scope.policies.push(data[i]);
+                }
+            }
+        });
+    });
+
+    $scope.addPolicy = function(policyType) {
+        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + policyType.type + "/create");
+    }
+});
+
+module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
+    $scope.realm = realm;
+    $scope.client = client;
+    $scope.policyProviders = [];
+
+    PolicyProvider.query({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function (data) {
+        for (i = 0; i < data.length; i++) {
+            if (data[i].type == 'resource' || data[i].type == 'scope') {
+                $scope.policyProviders.push(data[i]);
+            }
+        }
+    });
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+
+        ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+            $scope.policies = [];
+
+            for (i = 0; i < data.length; i++) {
+                if (data[i].type == 'resource' || data[i].type == 'scope') {
+                    $scope.policies.push(data[i]);
+                }
+            }
+        });
+    });
+
+    $scope.addPolicy = function(policyType) {
+        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + policyType.type + "/create");
+    }
+});
+
+module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http, $route, realm, client, PolicyController) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "drools";
+        },
+
+        onInit : function() {
+            $scope.drools = {};
+
+            $scope.resolveModules = function(policy) {
+                if (!policy) {
+                    policy = $scope.policy;
+                }
+
+                $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/drools/resolveModules'
+                        , policy).success(function(data) {
+                            $scope.drools.moduleNames = data;
+                            $scope.resolveSessions();
+                        });
+            }
+
+            $scope.resolveSessions = function() {
+                $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/drools/resolveSessions'
+                        , $scope.policy).success(function(data) {
+                            $scope.drools.moduleSessions = data;
+                        });
+            }
+        },
+
+        onInitUpdate : function(policy) {
+            policy.config.scannerPeriod = parseInt(policy.config.scannerPeriod);
+            $scope.resolveModules(policy);
+        },
+
+        onUpdate : function() {
+            $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.config.scannerPeriod = 1;
+            newPolicy.config.scannerPeriodUnit = 'Hours';
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "resource";
+        },
+
+        isPermission : function() {
+            return true;
+        },
+
+        onInit : function() {
+            ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.resources = data;
+            });
+
+            ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.policies = [];
+
+                for (i = 0; i < data.length; i++) {
+                    if (data[i].type != 'resource' && data[i].type != 'scope') {
+                        $scope.policies.push(data[i]);
+                    }
+                }
+            });
+
+            $scope.applyToResourceType = function() {
+                if ($scope.policy.config.default) {
+                    $scope.policy.config.resources = [];
+                } else {
+                    $scope.policy.config.defaultResourceType = null;
+                }
+            }
+        },
+
+        onInitUpdate : function(policy) {
+            policy.config.default = eval(policy.config.default);
+            policy.config.resources = eval(policy.config.resources);
+            policy.config.applyPolicies = eval(policy.config.applyPolicies);
+        },
+
+        onUpdate : function() {
+            $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.decisionStrategy = 'UNANIMOUS';
+            newPolicy.config = {};
+            newPolicy.config.resources = '';
+
+            var resourceId = $location.search()['rsrid'];
+
+            if (resourceId) {
+                newPolicy.config.resources = [resourceId];
+            }
+        },
+
+        onCreate : function() {
+            $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource, ResourceServerScope) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "scope";
+        },
+
+        isPermission : function() {
+            return true;
+        },
+
+        onInit : function() {
+            ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.scopes = data;
+            });
+
+            ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.resources = data;
+            });
+
+            ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.policies = [];
+
+                for (i = 0; i < data.length; i++) {
+                    if (data[i].type != 'resource' && data[i].type != 'scope') {
+                        $scope.policies.push(data[i]);
+                    }
+                }
+            });
+
+            $scope.resolveScopes = function(policy, keepScopes) {
+                if (!keepScopes) {
+                    policy.config.scopes = [];
+                }
+
+                if (!policy) {
+                    policy = $scope.policy;
+                }
+
+                if (policy.config.resources != null) {
+                    ResourceServerResource.get({
+                        realm : $route.current.params.realm,
+                        client : client.id,
+                        rsrid : policy.config.resources
+                    }, function(data) {
+                        $scope.scopes = data.scopes;
+                    });
+                } else {
+                    ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+                        $scope.scopes = data;
+                    });
+                }
+            }
+        },
+
+        onInitUpdate : function(policy) {
+            if (policy.config.resources) {
+                policy.config.resources = eval(policy.config.resources);
+
+                if (policy.config.resources.length > 0) {
+                    policy.config.resources = policy.config.resources[0];
+                } else {
+                    policy.config.resources = null;
+                }
+            }
+
+            $scope.resolveScopes(policy, true);
+
+            policy.config.applyPolicies = eval(policy.config.applyPolicies);
+            policy.config.scopes = eval(policy.config.scopes);
+        },
+
+        onUpdate : function() {
+            if ($scope.policy.config.resources != null) {
+                var resources = undefined;
+
+                if ($scope.policy.config.resources.length != 0) {
+                    resources = JSON.stringify([$scope.policy.config.resources])
+                }
+
+                $scope.policy.config.resources = resources;
+            }
+
+            $scope.policy.config.scopes = JSON.stringify($scope.policy.config.scopes);
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.decisionStrategy = 'UNANIMOUS';
+            newPolicy.config = {};
+            newPolicy.config.resources = '';
+        },
+
+        onCreate : function() {
+            if ($scope.policy.config.resources != null) {
+                var resources = undefined;
+
+                if ($scope.policy.config.resources.length != 0) {
+                    resources = JSON.stringify([$scope.policy.config.resources])
+                }
+
+                $scope.policy.config.resources = resources;
+            }
+            $scope.policy.config.scopes = JSON.stringify($scope.policy.config.scopes);
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route, realm, client, PolicyController, User) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "user";
+        },
+
+        onInit : function() {
+            User.query({realm: $route.current.params.realm}, function(data) {
+                $scope.users = data;
+            });
+
+            $scope.selectedUsers = [];
+
+            $scope.selectUser = function(user) {
+                if (!user || !user.id) {
+                    return;
+                }
+
+                $scope.selectedUser = {};
+                $scope.selectedUsers.push(user);
+            }
+
+            $scope.removeFromList = function(list, index) {
+                list.splice(index, 1);
+            }
+        },
+
+        onInitUpdate : function(policy) {
+            var selectedUsers = [];
+
+            if (policy.config.users) {
+                var users = eval(policy.config.users);
+
+                for (i = 0; i < users.length; i++) {
+                    User.get({realm: $route.current.params.realm, userId: users[i]}, function(data) {
+                        selectedUsers.push(data);
+                        $scope.selectedUsers = angular.copy(selectedUsers);
+                    });
+                }
+            }
+
+            $scope.$watch('selectedUsers', function() {
+                if (!angular.equals($scope.selectedUsers, selectedUsers)) {
+                    $scope.changed = true;
+                }
+            }, true);
+        },
+
+        onUpdate : function() {
+            var users = [];
+
+            for (i = 0; i < $scope.selectedUsers.length; i++) {
+                users.push($scope.selectedUsers[i].id);
+            }
+
+            $scope.policy.config.users = JSON.stringify(users);
+        },
+
+        onCreate : function() {
+            var users = [];
+
+            for (i = 0; i < $scope.selectedUsers.length; i++) {
+                users.push($scope.selectedUsers[i].id);
+            }
+
+            $scope.policy.config.users = JSON.stringify(users);
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route, realm, client, PolicyController, Role, RoleById) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "role";
+        },
+
+        onInit : function() {
+            Role.query({realm: $route.current.params.realm}, function(data) {
+                $scope.roles = data;
+            });
+
+            $scope.selectedRoles = [];
+
+            $scope.selectRole = function(role) {
+                if (!role || !role.id) {
+                    return;
+                }
+
+                $scope.selectedRole = {};
+                $scope.selectedRoles.push(role);
+            }
+
+            $scope.removeFromList = function(list, index) {
+                list.splice(index, 1);
+            }
+        },
+
+        onInitUpdate : function(policy) {
+            var selectedRoles = [];
+
+            if (policy.config.roles) {
+                var roles = eval(policy.config.roles);
+
+                for (i = 0; i < roles.length; i++) {
+                    RoleById.get({realm: $route.current.params.realm, role: roles[i]}, function(data) {
+                        selectedRoles.push(data);
+                        $scope.selectedRoles = angular.copy(selectedRoles);
+                    });
+                }
+            }
+
+            $scope.$watch('selectedRoles', function() {
+                if (!angular.equals($scope.selectedRoles, selectedRoles)) {
+                    $scope.changed = true;
+                }
+            }, true);
+        },
+
+        onUpdate : function() {
+            var roles = [];
+
+            for (i = 0; i < $scope.selectedRoles.length; i++) {
+                roles.push($scope.selectedRoles[i].id);
+            }
+
+            $scope.policy.config.roles = JSON.stringify(roles);
+        },
+
+        onCreate : function() {
+            var roles = [];
+
+            for (i = 0; i < $scope.selectedRoles.length; i++) {
+                roles.push($scope.selectedRoles[i].id);
+            }
+
+            $scope.policy.config.roles = JSON.stringify(roles);
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $location, realm, PolicyController, client) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "js";
+        },
+
+        onInit : function() {
+            $scope.initEditor = function(editor){
+                var session = editor.getSession();
+
+                session.setMode('ace/mode/javascript');
+            };
+        },
+
+        onInitUpdate : function(policy) {
+
+        },
+
+        onUpdate : function() {
+
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.config = {};
+        },
+
+        onCreate : function() {
+
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyTimeDetailCtrl', function($scope, $route, $location, realm, PolicyController, client) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "time";
+        },
+
+        onInit : function() {
+        },
+
+        onInitUpdate : function(policy) {
+
+        },
+
+        onUpdate : function() {
+
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.config.expirationTime = 1;
+            newPolicy.config.expirationUnit = 'Minutes';
+        },
+
+        onCreate : function() {
+
+        }
+    }, realm, client, $scope);
+});
+
+module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $route, $location, realm, PolicyController, ResourceServerPolicy, client) {
+    PolicyController.onInit({
+        getPolicyType : function() {
+            return "aggregate";
+        },
+
+        onInit : function() {
+            ResourceServerPolicy.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.policies = [];
+
+                for (i = 0; i < data.length; i++) {
+                    if (data[i].type != 'resource' && data[i].type != 'scope') {
+                        $scope.policies.push(data[i]);
+                    }
+                }
+            });
+        },
+
+        onInitUpdate : function(policy) {
+            policy.config.applyPolicies = eval(policy.config.applyPolicies);
+        },
+
+        onUpdate : function() {
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        },
+
+        onInitCreate : function(newPolicy) {
+            newPolicy.config = {};
+            newPolicy.decisionStrategy = 'UNANIMOUS';
+        },
+
+        onCreate : function() {
+            $scope.policy.config.applyPolicies = JSON.stringify($scope.policy.config.applyPolicies);
+        }
+    }, realm, client, $scope);
+});
+
+module.service("PolicyController", function($http, $route, $location, ResourceServer, ResourceServerPolicy, AuthzDialog, Notifications) {
+
+    var PolicyController = {};
+
+    PolicyController.onInit = function(delegate, realm, client, $scope) {
+        if (!delegate.isPermission) {
+            delegate.isPermission = function () {
+                return false;
+            }
+        }
+
+        $scope.realm = realm;
+        $scope.client = client;
+
+        $scope.decisionStrategies = ['AFFIRMATIVE', 'UNANIMOUS', 'CONSENSUS'];
+        $scope.logics = ['POSITIVE', 'NEGATIVE'];
+
+        delegate.onInit();
+
+        ResourceServer.get({
+            realm : $route.current.params.realm,
+            client : client.id
+        }, function(data) {
+            $scope.server = data;
+
+            var policyId = $route.current.params.id;
+
+            if (!policyId) {
+                $scope.create = true;
+                $scope.changed = false;
+
+                var policy = {};
+
+                policy.type = delegate.getPolicyType();
+                policy.config = {};
+                policy.logic = 'POSITIVE';
+
+                if (delegate.onInitCreate) {
+                    delegate.onInitCreate(policy);
+                }
+
+                $scope.policy = angular.copy(policy);
+
+                $scope.$watch('policy', function() {
+                    if (!angular.equals($scope.policy, policy)) {
+                        $scope.changed = true;
+                    }
+                }, true);
+
+                $scope.save = function() {
+                    if (delegate.onCreate) {
+                        delegate.onCreate();
+                    }
+                    ResourceServerPolicy.save({realm : realm.realm, client : client.id}, $scope.policy, function(data) {
+                        if (delegate.isPermission()) {
+                            $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + $scope.policy.type + "/" + data.id);
+                            Notifications.success("The permission has been created.");
+                        } else {
+                            $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + $scope.policy.type + "/" + data.id);
+                            Notifications.success("The policy has been created.");
+                        }
+                    });
+                }
+
+                $scope.cancel = function() {
+                    if (delegate.isPermission()) {
+                        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/");
+                    } else {
+                        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/");
+                    }
+                }
+            } else {
+                ResourceServerPolicy.get({
+                    realm : $route.current.params.realm,
+                    client : client.id,
+                    id : $route.current.params.id,
+                }, function(data) {
+                    var policy = angular.copy(data);
+
+                    if (delegate.onInitUpdate) {
+                        delegate.onInitUpdate(policy);
+                    }
+
+                    $scope.policy = angular.copy(policy);
+                    $scope.changed = false;
+
+                    $scope.$watch('policy', function() {
+                        if (!angular.equals($scope.policy, policy)) {
+                            $scope.changed = true;
+                        }
+                    }, true);
+
+                    $scope.save = function() {
+                        if (delegate.onUpdate) {
+                            delegate.onUpdate();
+                        }
+                        ResourceServerPolicy.update({realm : realm.realm, client : client.id, id : $scope.policy.id}, $scope.policy, function() {
+                            $route.reload();
+                            if (delegate.isPermission()) {
+                                Notifications.success("The permission has been updated.");
+                            } else {
+                                Notifications.success("The policy has been updated.");
+                            }
+                        });
+                    }
+
+                    $scope.reset = function() {
+                        var freshPolicy = angular.copy(data);
+
+                        if (delegate.onInitUpdate) {
+                            delegate.onInitUpdate(freshPolicy);
+                        }
+
+                        $scope.policy = angular.copy(freshPolicy);
+                        $scope.changed = false;
+                    }
+                });
+
+                $scope.remove = function() {
+                    var msg = "";
+
+                    if ($scope.policy.dependentPolicies.length > 0) {
+                        msg = "<p>This policy is being used by other policies:</p>";
+                        msg += "<ul>";
+                        for (i = 0; i < $scope.policy.dependentPolicies.length; i++) {
+                            msg+= "<li><strong>" + $scope.policy.dependentPolicies[i].name + "</strong></li>";
+                        }
+                        msg += "</ul>";
+                        msg += "<p>If you remove this policy, the policies above will be affected and will not be associated with this policy anymore.</p>";
+                    }
+
+                    AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() {
+                        ResourceServerPolicy.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
+                            if (delegate.isPermission()) {
+                                $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission");
+                                Notifications.success("The permission has been deleted.");
+                            } else {
+                                $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy");
+                                Notifications.success("The policy has been deleted.");
+                            }
+                        });
+                    });
+                }
+            }
+        });
+    }
+
+    return PolicyController;
+});
+
+module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $location, realm, clients, roles, ResourceServer, client, ResourceServerResource, ResourceServerScope, User, Notifications) {
+    $scope.realm = realm;
+    $scope.client = client;
+    $scope.clients = clients;
+    $scope.roles = roles;
+    $scope.authzRequest = {};
+    $scope.authzRequest.resources = [];
+    $scope.authzRequest.context = {};
+    $scope.authzRequest.context.attributes = {};
+    $scope.authzRequest.roleIds = [];
+    $scope.newResource = {};
+    $scope.resultUrl = resourceUrl + '/partials/authz/policy/resource-server-policy-evaluate-result.html';
+
+    ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+        $scope.scopes = data;
+    });
+
+    $scope.addContextAttribute = function() {
+        if (!$scope.newContextAttribute.value || $scope.newContextAttribute.value == '') {
+            Notifications.error("You must provide a value to a context attribute.");
+            return;
+        }
+
+        $scope.authzRequest.context.attributes[$scope.newContextAttribute.key] = $scope.newContextAttribute.value;
+        delete $scope.newContextAttribute;
+    }
+
+    $scope.removeContextAttribute = function(key) {
+        delete $scope.authzRequest.context.attributes[key];
+    }
+
+    $scope.getContextAttribute = function(key) {
+        for (i = 0; i < $scope.defaultContextAttributes.length; i++) {
+            if ($scope.defaultContextAttributes[i].key == key) {
+                return $scope.defaultContextAttributes[i];
+            }
+        }
+
+        return $scope.authzRequest.context.attributes[key];
+    }
+
+    $scope.getContextAttributeName = function(key) {
+        var attribute = $scope.getContextAttribute(key);
+
+        if (!attribute.name) {
+            return key;
+        }
+
+        return attribute.name;
+    }
+
+    $scope.defaultContextAttributes = [
+        {
+            key : "custom",
+            name : "Custom Attribute...",
+            custom: true
+        },
+        {
+            key : "kc.identity.authc.method",
+            name : "Authentication Method",
+            values: [
+                {
+                    key : "pwd",
+                    name : "Password"
+                },
+                {
+                    key : "otp",
+                    name : "One-Time Password"
+                },
+                {
+                    key : "kbr",
+                    name : "Kerberos"
+                }
+            ]
+        },
+        {
+            key : "kc.realm.name",
+            name : "Realm"
+        },
+        {
+            key : "kc.time.date_time",
+            name : "Date/Time (MM/dd/yyyy hh:mm:ss)"
+        },
+        {
+            key : "kc.client.network.ip_address",
+            name : "Client IPv4 Address"
+        },
+        {
+            key : "kc.client.network.host",
+            name : "Client Host"
+        },
+        {
+            key : "kc.client.user_agent",
+            name : "Client/User Agent"
+        }
+    ];
+
+    $scope.isDefaultContextAttribute = function() {
+        if (!$scope.newContextAttribute) {
+            return true;
+        }
+
+        if ($scope.newContextAttribute.custom) {
+            return false;
+        }
+
+        if (!$scope.getContextAttribute($scope.newContextAttribute.key).custom) {
+            return true;
+        }
+
+        return false;
+    }
+
+    $scope.selectDefaultContextAttribute = function() {
+        $scope.newContextAttribute = angular.copy($scope.newContextAttribute);
+    }
+
+    $scope.setApplyToResourceType = function() {
+        if ($scope.applyResourceType) {
+            ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.scopes = data;
+            });
+        }
+
+        delete $scope.newResource;
+        $scope.authzRequest.resources = [];
+    }
+
+    $scope.addResource = function() {
+        var resource = {};
+
+        resource.id = $scope.newResource._id;
+
+        for (i = 0; i < $scope.resources.length; i++) {
+            if ($scope.resources[i]._id == resource.id) {
+                resource.name = $scope.resources[i].name;
+                break;
+            }
+        }
+
+        resource.scopes = $scope.newResource.scopes;
+
+        $scope.authzRequest.resources.push(resource);
+
+        delete $scope.newResource;
+    }
+
+    $scope.removeResource = function(index) {
+        $scope.authzRequest.resources.splice(index, 1);
+    }
+
+    $scope.resolveScopes = function() {
+        if ($scope.newResource._id) {
+            $scope.newResource.scopes = [];
+            $scope.scopes = [];
+            ResourceServerResource.get({
+                realm: $route.current.params.realm,
+                client : client.id,
+                rsrid: $scope.newResource._id
+            }, function (data) {
+                $scope.scopes = data.scopes;
+            });
+        } else {
+            ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
+                $scope.scopes = data;
+            });
+        }
+    }
+
+    $scope.save = function() {
+        $scope.authzRequest.entitlements = false;
+        if ($scope.applyResourceType) {
+            if (!$scope.newResource) {
+                $scope.newResource = {};
+            }
+            $scope.authzRequest.resources[0].scopes = $scope.newResource.scopes;
+        }
+
+        $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/evaluate'
+                , $scope.authzRequest).success(function(data) {
+                    $scope.evaluationResult = data;
+                    $scope.showResultTab();
+                });
+    }
+
+    $scope.entitlements = function() {
+        $scope.authzRequest.entitlements = true;
+        $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/evaluate'
+            , $scope.authzRequest).success(function(data) {
+            $scope.evaluationResult = data;
+            $scope.showResultTab();
+        });
+    }
+
+    $scope.showResultTab = function() {
+        $scope.showResult = true;
+    }
+
+    $scope.showRequestTab = function() {
+        $scope.showResult = false;
+    }
+
+    User.query({realm: $route.current.params.realm}, function(data) {
+        $scope.users = data;
+    });
+
+    ResourceServerResource.query({realm : realm.realm, client : client.id}, function (data) {
+        $scope.resources = data;
+    });
+
+    ResourceServer.get({
+        realm : $route.current.params.realm,
+        client : client.id
+    }, function(data) {
+        $scope.server = data;
+    });
+});
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
new file mode 100644
index 0000000..c74db28
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
@@ -0,0 +1,117 @@
+module.factory('ResourceServer', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server', {
+        realm : '@realm',
+        client: '@client'
+    }, {
+        'update' : {method : 'PUT'},
+        'settings' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/settings', method : 'GET'}
+    });
+});
+
+module.factory('ResourceServerResource', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid', {
+        realm : '@realm',
+        client: '@client',
+        rsrid : '@rsrid'
+    }, {
+        'update' : {method : 'PUT'}
+    });
+});
+
+module.factory('ResourceServerScope', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/:id', {
+        realm : '@realm',
+        client: '@client',
+        id : '@id'
+    }, {
+        'update' : {method : 'PUT'}
+    });
+});
+
+module.factory('ResourceServerPolicy', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id', {
+        realm : '@realm',
+        client: '@client',
+        id : '@id'
+    }, {
+        'update' : {method : 'PUT'}
+    });
+});
+
+module.factory('PolicyProvider', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/providers', {
+        realm : '@realm',
+        client: '@client'
+    });
+});
+
+module.service('AuthzDialog', function($modal) {
+    var dialog = {};
+
+    var openDialog = function(title, message, btns, template) {
+        var controller = function($scope, $modalInstance, $sce, title, message, btns) {
+            $scope.title = title;
+            $scope.message = $sce.trustAsHtml(message);
+            $scope.btns = btns;
+
+            $scope.ok = function () {
+                $modalInstance.close();
+            };
+            $scope.cancel = function () {
+                $modalInstance.dismiss('cancel');
+            };
+        };
+
+        return $modal.open({
+            templateUrl: resourceUrl + template,
+            controller: controller,
+            resolve: {
+                title: function() {
+                    return title;
+                },
+                message: function() {
+                    return message;
+                },
+                btns: function() {
+                    return btns;
+                }
+            }
+        }).result;
+    }
+
+    dialog.confirmDeleteWithMsg = function(name, type, msg, success) {
+        var title = 'Delete ' + type;
+        msg += 'Are you sure you want to permanently delete the ' + type + ' <strong>' + name + '</strong> ?';
+        var btns = {
+            ok: {
+                label: 'Delete',
+                cssClass: 'btn btn-danger'
+            },
+            cancel: {
+                label: 'Cancel',
+                cssClass: 'btn btn-default'
+            }
+        }
+
+        openDialog(title, msg, btns, '/templates/authz/kc-authz-modal.html').then(success);
+    };
+
+    dialog.confirmDelete = function(name, type, success) {
+        var title = 'Delete ' + type;
+        var msg = 'Are you sure you want to permanently delete the ' + type + ' <strong>' + name + '</strong> ?';
+        var btns = {
+            ok: {
+                label: 'Delete',
+                cssClass: 'btn btn-danger'
+            },
+            cancel: {
+                label: 'Cancel',
+                cssClass: 'btn btn-default'
+            }
+        }
+
+        openDialog(title, msg, btns, '/templates/authz/kc-authz-modal.html').then(success);
+    }
+
+    return dialog;
+});
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ace.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ace.js
new file mode 100644
index 0000000..e099651
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ace.js
@@ -0,0 +1,11 @@
+(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE="",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u<a;++u){var f=s(e,t[u]);if(f==undefined&&r.original)return;o.push(f)}return n&&n.apply(null,o)||!0}},r=function(e,t){var i=n("",e,t);return i==undefined&&r.original?r.original.apply(this,arguments):i},i=function(e,t){if(t.indexOf("!")!==-1){var n=t.split("!");return i(e,n[0])+"!"+i(e,n[1])}if(t.charAt(0)=="."){var r=e.split("/").slice(0,-1).join("/");t=r+"/"+t;while(t.indexOf(".")!==-1&&s!=t){var s=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return t},s=function(e,r){r=i(e,r);var s=t.modules[r];if(!s){s=t.payloads[r];if(typeof s=="function"){var o={},u={id:r,uri:"",exports:o,packaged:!0},a=function(e,t){return n(r,e,t)},f=s(a,o,u);o=f||u.exports,t.modules[r]=o,delete t.payloads[r]}s=t.modules[r]=o||s}return s};o(ACE_NAMESPACE)})(),define("ace/lib/regexp",["require","exports","module"],function(e,t,n){"use strict";function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":"")}function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(var r=n||0;r<e.length;r++)if(e[r]===t)return r;return-1}var r={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},i=r.exec.call(/()??/,"")[1]===undefined,s=function(){var e=/^/g;return r.test.call(e,""),!e.lastIndex}();if(s&&i)return;RegExp.prototype.exec=function(e){var t=r.exec.apply(this,arguments),n,a;if(typeof e=="string"&&t){!i&&t.length>1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;e<arguments.length-2;e++)arguments[e]===undefined&&(t[e]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var f=1;f<t.length;f++)n=this._xregexp.captureNames[f-1],n&&(t[n]=t[f]);!s&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+t<n||(t=n-e);var r=this.slice(e,e+t),i=u.call(arguments,2),s=i.length;if(e===n)s&&this.push.apply(this,i);else{var o=Math.min(t,n-e),a=e+o,f=a+s-o,l=n-a,c=n-o;if(f<a)for(var h=0;h<l;++h)this[f+h]=this[a+h];else if(f>a)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h<s;++h)this[e+h]=i[h]}}return r};else{var v=Array.prototype.splice;Array.prototype.splice=function(e,t){return arguments.length?v.apply(this,[e===void 0?0:e,t===void 0?this.length-e:t].concat(u.call(arguments,2))):[]}}Array.isArray||(Array.isArray=function(t){return a(t)=="[object Array]"});var m=Object("a"),g=m[0]!="a"||!(0 in m);Array.prototype.forEach||(Array.prototype.forEach=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=arguments[1],s=-1,o=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s<o)s in r&&t.call(i,r[s],s,n)}),Array.prototype.map||(Array.prototype.map=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u<i;u++)u in r&&(s[u]=t.call(o,r[u],u,n));return s}),Array.prototype.filter||(Array.prototype.filter=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f<i;f++)f in r&&(o=r[f],t.call(u,o,f,n)&&s.push(o));return s}),Array.prototype.every||(Array.prototype.every=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o<i;o++)if(o in r&&!t.call(s,r[o],o,n))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o<i;o++)if(o in r&&t.call(s,r[o],o,n))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s<i;s++)s in r&&(o=t.call(void 0,o,r[s],s,n));return o}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i<r;i++)if(i in n&&n[i]===t)return i;return-1};if(!Array.prototype.lastIndexOf||[0,1].lastIndexOf(0,-3)!=-1)Array.prototype.lastIndexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n<r;n++){var i=A[n];f(e,i)&&I.push(i)}return I}}Date.now||(Date.now=function(){return(new Date).getTime()});var _="	\n\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff";if(!String.prototype.trim||_.trim()){_="["+_+"]";var D=new RegExp("^"+_+_+"*"),P=new RegExp(_+_+"*$");String.prototype.trim=function(){return String(this).replace(D,"").replace(P,"")}}var F=function(e){if(e==null)throw new TypeError("can't convert "+e+" to object");return Object(e)}}),define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(e,t,n){"use strict";e("./regexp"),e("./es5-shim")}),define("ace/lib/dom",["require","exports","module"],function(e,t,n){"use strict";var r="http://www.w3.org/1999/xhtml";t.getDocumentHead=function(e){return e||(e=document),e.head||e.getElementsByTagName("head")[0]||e.documentElement},t.createElement=function(e,t){return document.createElementNS?document.createElementNS(t||r,e):document.createElement(e)},t.hasCssClass=function(e,t){var n=(e.className||"").split(/\s+/g);return n.indexOf(t)!==-1},t.addCssClass=function(e,n){t.hasCssClass(e,n)||(e.className+=" "+n)},t.removeCssClass=function(e,t){var n=e.className.split(/\s+/g);for(;;){var r=n.indexOf(t);if(r==-1)break;n.splice(r,1)}e.className=n.join(" ")},t.toggleCssClass=function(e,t){var n=e.className.split(/\s+/g),r=!0;for(;;){var i=n.indexOf(t);if(i==-1)break;r=!1,n.splice(i,1)}return r&&n.push(t),e.className=n.join(" "),r},t.setCssClass=function(e,n,r){r?t.addCssClass(e,n):t.removeCssClass(e,n)},t.hasCssString=function(e,t){var n=0,r;t=t||document;if(t.createStyleSheet&&(r=t.styleSheets)){while(n<r.length)if(r[n++].owningElement.id===e)return!0}else if(r=t.getElementsByTagName("style"))while(n<r.length)if(r[n++].id===e)return!0;return!1},t.importCssString=function(n,r,i){i=i||document;if(r&&t.hasCssString(r,i))return null;var s;r&&(n+="\n/*# sourceURL=ace/css/"+r+" */"),i.createStyleSheet?(s=i.createStyleSheet(),s.cssText=n,r&&(s.owningElement.id=r)):(s=t.createElement("style"),s.appendChild(i.createTextNode(n)),r&&(s.id=r),t.getDocumentHead(i).appendChild(s))},t.importCssStylsheet=function(e,n){if(n.createStyleSheet)n.createStyleSheet(e);else{var r=t.createElement("link");r.rel="stylesheet",r.href=e,t.getDocumentHead(n).appendChild(r)}},t.getInnerWidth=function(e){return parseInt(t.computedStyle(e,"paddingLeft"),10)+parseInt(t.computedStyle(e,"paddingRight"),10)+e.clientWidth},t.getInnerHeight=function(e){return parseInt(t.computedStyle(e,"paddingTop"),10)+parseInt(t.computedStyle(e,"paddingBottom"),10)+e.clientHeight},t.scrollbarWidth=function(e){var n=t.createElement("ace_inner");n.style.width="100%",n.style.minWidth="0px",n.style.height="200px",n.style.display="block";var r=t.createElement("ace_outer"),i=r.style;i.position="absolute",i.left="-10000px",i.overflow="hidden",i.width="200px",i.minWidth="0px",i.height="150px",i.display="block",r.appendChild(n);var s=e.documentElement;s.appendChild(r);var o=n.offsetWidth;i.overflow="scroll";var u=n.offsetWidth;return o==u&&(u=r.clientWidth),s.removeChild(r),o-u};if(typeof document=="undefined"){t.importCssString=function(){};return}window.pageYOffset!==undefined?(t.getPageScrollTop=function(){return window.pageYOffset},t.getPageScrollLeft=function(){return window.pageXOffset}):(t.getPageScrollTop=function(){return document.body.scrollTop},t.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?t.computedStyle=function(e,t){return t?(window.getComputedStyle(e,"")||{})[t]||"":window.getComputedStyle(e,"")||{}}:t.computedStyle=function(e,t){return t?e.currentStyle[t]:e.currentStyle},t.setInnerHtml=function(e,t){var n=e.cloneNode(!1);return n.innerHTML=t,e.parentNode.replaceChild(n,e),n},"textContent"in document.documentElement?(t.setInnerText=function(e,t){e.textContent=t},t.getInnerText=function(e){return e.textContent}):(t.setInnerText=function(e,t){e.innerText=t},t.getInnerText=function(e){return e.innerText}),t.getParentWindow=function(e){return e.defaultView||e.parentWindow}}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/lib/keys",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop"],function(e,t,n){"use strict";e("./fixoldbrowsers");var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),define("ace/lib/useragent",["require","exports","module"],function(e,t,n){"use strict";t.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};if(typeof navigator!="object")return;var r=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),i=navigator.userAgent;t.isWin=r=="win",t.isMac=r=="mac",t.isLinux=r=="linux",t.isIE=navigator.appName=="Microsoft Internet Explorer"||navigator.appName.indexOf("MSAppHost")>=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){s.OSKey&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null),s.count=0,s.lastT=0}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){var t=e.keyCode;s[t]=(s[t]||0)+1,t==91||t==92?s.OSKey=!0:s.OSKey&&e.timeStamp-s.lastT>200&&s.count==1&&f(),s[t]==1&&s.count++,s.lastT=e.timeStamp;var r=a(n,e,t);return u=e.defaultPrevented,r}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){var t=e.keyCode;s[t]?s.count=Math.max(s.count-1,0):f();if(t==91||t==92)s.OSKey=!1;s[t]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n<r;n++)e[n]&&typeof e[n]=="object"?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=function s(e){if(typeof e!="object"||!e)return e;var t;if(Array.isArray(e)){t=[];for(var n=0;n<e.length;n++)t[n]=s(e[n]);return t}var r=e.constructor;if(r===RegExp)return e;t=r();for(var n in e)t[n]=s(e[n]);return t},t.arrayToMap=function(e){var t={};for(var n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return e.replace(/&/g,"&#38;").replace(/"/g,"&#34;").replace(/'/g,"&#39;").replace(/</g,"&#60;")},t.getMatchOffsets=function(e,t){var n=[];return e.replace(t,function(e){n.push({offset:arguments[arguments.length-2],length:e.length})}),n},t.deferredCall=function(e){var t=null,n=function(){t=null,e()},r=function(e){return r.cancel(),t=setTimeout(n,e||0),r};return r.schedule=r,r.call=function(){return this.cancel(),e(),r},r.cancel=function(){return clearTimeout(t),t=null,r},r.isPending=function(){return t},r},t.delayedCall=function(e,t){var n=null,r=function(){n=null,e()},i=function(e){n==null&&(n=setTimeout(r,e||t))};return i.delay=function(e){n&&clearTimeout(n),n=setTimeout(r,e||t)},i.schedule=i,i.call=function(){this.cancel(),e()},i.cancel=function(){n&&clearTimeout(n),n=null},i.isPending=function(){return n},i}}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=i.isChrome<18,a=i.isIE,f=function(e,t){function b(e){if(h)return;h=!0;if(k)t=0,r=e?0:n.value.length-1;else var t=e?2:1,r=2;try{n.setSelectionRange(t,r)}catch(i){}h=!1}function w(){if(h)return;n.value=f,i.isWebKit&&y.schedule()}function R(){clearTimeout(q),q=setTimeout(function(){p&&(n.style.cssText=p,p=""),t.renderer.$keepTextAreaAtCursor==null&&(t.renderer.$keepTextAreaAtCursor=!0,t.renderer.$moveTextAreaToCursor())},i.isOldIE?200:0)}var n=s.createElement("textarea");n.className="ace_text-input",i.isTouchPad&&n.setAttribute("x-palm-disable-auto-cap",!0),n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",i.isOldIE&&(n.style.top="-1000px"),e.insertBefore(n,e.firstChild);var f="",l=!1,c=!1,h=!1,p="",d=!0;try{var v=document.activeElement===n}catch(m){}r.addListener(n,"blur",function(e){t.onBlur(e),v=!1}),r.addListener(n,"focus",function(e){v=!0,t.onFocus(e),b()}),this.focus=function(){if(p)return n.focus();var e=n.style.top;n.style.position="fixed",n.style.top="0px",n.focus(),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return v};var g=o.delayedCall(function(){v&&b(d)}),y=o.delayedCall(function(){h||(n.value=f,v&&b())});i.isWebKit||t.addEventListener("changeSelection",function(){t.selection.isEmpty()!=d&&(d=!d,g.schedule())}),w(),v&&t.onFocus();var E=function(e){return e.selectionStart===0&&e.selectionEnd===e.value.length};!n.setSelectionRange&&n.createTextRange&&(n.setSelectionRange=function(e,t){var n=this.createTextRange();n.collapse(!0),n.moveStart("character",e),n.moveEnd("character",t),n.select()},E=function(e){try{var t=e.ownerDocument.selection.createRange()}catch(n){}return!t||t.parentElement()!=e?!1:t.text==e.value});if(i.isOldIE){var S=!1,x=function(e){if(S)return;var t=n.value;if(h||!t||t==f)return;if(e&&t==f[0])return T.schedule();A(t),S=!0,w(),S=!1},T=o.delayedCall(x);r.addListener(n,"propertychange",x);var N={13:1,27:1};r.addListener(n,"keyup",function(e){h&&(!n.value||N[e.keyCode])&&setTimeout(F,0);if((n.value.charCodeAt(0)||0)<129)return T.call();h?j():B()}),r.addListener(n,"keydown",function(e){T.schedule(50)})}var C=function(e){l?l=!1:E(n)?(t.selectAll(),b()):k&&b(t.selection.isEmpty())},k=null;this.setInputHandler=function(e){k=e},this.getInputHandler=function(){return k};var L=!1,A=function(e){k&&(e=k(e),k=null),c?(b(),e&&t.onPaste(e),c=!1):e==f.charAt(0)?L?t.execCommand("del",{source:"ace"}):t.execCommand("backspace",{source:"ace"}):(e.substring(0,2)==f?e=e.substr(2):e.charAt(0)==f.charAt(0)?e=e.substr(1):e.charAt(e.length-1)==f.charAt(0)&&(e=e.slice(0,-1)),e.charAt(e.length-1)==f.charAt(0)&&(e=e.slice(0,-1)),e&&t.onTextInput(e)),L&&(L=!1)},O=function(e){if(h)return;var t=n.value;A(t),w()},M=function(e,t){var n=e.clipboardData||window.clipboardData;if(!n||u)return;var r=a?"Text":"text/plain";return t?n.setData(r,t)!==!1:n.getData(r)},_=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);M(e,s)?(i?t.onCut():t.onCopy(),r.preventDefault(e)):(l=!0,n.value=s,n.select(),setTimeout(function(){l=!1,w(),b(),i?t.onCut():t.onCopy()}))},D=function(e){_(e,!0)},P=function(e){_(e,!1)},H=function(e){var s=M(e);typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(b),r.preventDefault(e)):(n.value="",c=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",C),r.addListener(n,"input",O),r.addListener(n,"cut",D),r.addListener(n,"copy",P),r.addListener(n,"paste",H),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:P(e);break;case 86:H(e);break;case 88:D(e)}});var B=function(e){if(h||!t.onCompositionStart||t.$readOnly)return;h={},t.onCompositionStart(),setTimeout(j,0),t.on("mousedown",F),t.selection.isEmpty()||(t.insert(""),t.session.markUndoGroup(),t.selection.clearSelection()),t.session.markUndoGroup()},j=function(){if(!h||!t.onCompositionUpdate||t.$readOnly)return;var e=n.value.replace(/\x01/g,"");if(h.lastValue===e)return;t.onCompositionUpdate(e),h.lastValue&&t.undo(),h.lastValue=e;if(h.lastValue){var r=t.selection.getRange();t.insert(h.lastValue),t.session.markUndoGroup(),h.range=t.selection.getRange(),t.selection.setRange(r),t.selection.clearSelection()}},F=function(e){if(!t.onCompositionEnd||t.$readOnly)return;var r=h;h=!1;var i=setTimeout(function(){i=null;var e=n.value.replace(/\x01/g,"");if(h)return;e==r.lastValue?w():!r.lastValue&&e&&(w(),A(e))});k=function(n){return i&&clearTimeout(i),n=n.replace(/\x01/g,""),n==r.lastValue?"":(r.lastValue&&i&&t.undo(),n)},t.onCompositionEnd(),t.removeListener("mousedown",F),e.type=="compositionend"&&r.range&&t.selection.setRange(r.range)},I=o.delayedCall(j,50);r.addListener(n,"compositionstart",B),i.isGecko?r.addListener(n,"text",function(){I.schedule()}):(r.addListener(n,"keyup",function(){I.schedule()}),r.addListener(n,"keydown",function(){I.schedule()})),r.addListener(n,"compositionend",F),this.getElement=function(){return n},this.setReadOnly=function(e){n.readOnly=e},this.onContextMenu=function(e){L=!0,b(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){if(!o&&i.isOldIE)return;p||(p=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+"height:"+n.style.height+";"+(i.isIE?"opacity:0.1;":"");var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout(q),i.isWin&&!i.isOldIE&&r.capture(t.container,h,R)},this.onContextMenuClose=R;var q,U=function(e){t.textInput.onContextMenu(e),R()};r.addListener(n,"mouseup",U),r.addListener(n,"mousedown",function(e){e.preventDefault(),R()}),r.addListener(t.renderer.scroller,"contextmenu",U),r.addListener(n,"contextmenu",U)};t.TextInput=f}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function u(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e)),t.setDefaultHandler("touchmove",this.onTouchMove.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function a(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function f(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=0;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var r=this.editor,i=e.getButton();if(i!==0){var s=r.getSelectionRange(),o=s.isEmpty();r.$blockScrolling++,(o||i==1)&&r.selection.moveToPosition(n),r.$blockScrolling--,i==2&&r.textInput.onContextMenu(e.domEvent);return}this.mousedownEvent.time=Date.now();if(t&&!r.isFocused()){r.focus();if(this.$focusTimout&&!this.$clickSelection&&!r.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;n.$blockScrolling++,this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);n.$blockScrolling++;if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("<br/>"),i.setHtml(f),i.show(),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left<a.x.right?-3:2),l/i<=1&&(c.row+=a.y.top<a.y.bottom?-1:1);var h=e.row!=c.row,v=e.column!=c.column,m=!n||e.row!=n.row;h||v&&!m?E?r-E>=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o<n.length;o++){n[o](t,this);if(t.propagationStopped)break}if(r&&!t.defaultPrevented)return r(t,this)},r._signal=function(e,t){var n=(this._eventRegistry||{})[e];if(!n)return;n=n.slice();for(var r=0;r<n.length;r++)n[r](t,this)},r.once=function(e,t){var n=this;t&&this.addEventListener(e,function r(){n.removeEventListener(e,r),t.apply(null,arguments)})},r.setDefaultHandler=function(e,t){var n=this._defaultHandlers;n||(n=this._defaultHandlers={_disabled_:{}});if(n[e]){var r=n[e],i=n._disabled_[e];i||(n._disabled_[e]=i=[]),i.push(r);var s=i.indexOf(t);s!=-1&&i.splice(s,1)}n[e]=t},r.removeDefaultHandler=function(e,t){var n=this._defaultHandlers;if(!n)return;var r=n._disabled_[e];if(n[e]==t){var i=n[e];r&&this.setDefaultHandler(e,r.pop())}else if(r){var s=r.indexOf(t);s!=-1&&r.splice(s,1)}},r.on=r.addEventListener=function(e,t,n){this._eventRegistry=this._eventRegistry||{};var r=this._eventRegistry[e];return r||(r=this._eventRegistry[e]=[]),r.indexOf(t)==-1&&r[n?"unshift":"push"](t),t},r.off=r.removeListener=r.removeEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];if(!n)return;var r=n.indexOf(t);r!==-1&&n.splice(r,1)},r.removeAllListeners=function(e){this._eventRegistry&&(this._eventRegistry[e]=[])},t.EventEmitter=r}),define("ace/lib/app_config",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"no use strict";function o(e){typeof console!="undefined"&&console.warn&&console.warn.apply(console,arguments)}function u(e,t){var n=new Error(e);n.data=t,typeof console=="object"&&console.error&&console.error(n),setTimeout(function(){throw n})}var r=e("./oop"),i=e("./event_emitter").EventEmitter,s={setOptions:function(e){Object.keys(e).forEach(function(t){this.setOption(t,e[t])},this)},getOptions:function(e){var t={};return e?Array.isArray(e)||(t=e,e=Object.keys(t)):e=Object.keys(this.$options),e.forEach(function(e){t[e]=this.getOption(e)},this),t},setOption:function(e,t){if(this["$"+e]===t)return;var n=this.$options[e];if(!n)return o('misspelled option "'+e+'"');if(n.forwardTo)return this[n.forwardTo]&&this[n.forwardTo].setOption(e,t);n.handlesSet||(this["$"+e]=t),n&&n.set&&n.set.call(this,t)},getOption:function(e){var t=this.$options[e];return t?t.forwardTo?this[t.forwardTo]&&this[t.forwardTo].getOption(e):t&&t.get?t.get.call(this):this["$"+e]:o('misspelled option "'+e+'"')}},a=function(){this.$defaultOptions={}};(function(){r.implement(this,i),this.defineOptions=function(e,t,n){return e.$options||(this.$defaultOptions[t]=e.$options={}),Object.keys(n).forEach(function(t){var r=n[t];typeof r=="string"&&(r={forwardTo:r}),r.name||(r.name=t),e.$options[r.name]=r,"initialValue"in r&&(e["$"+r.name]=r.initialValue)}),r.implement(e,s),this},this.resetOptions=function(e){Object.keys(e.$options).forEach(function(t){var n=e.$options[t];"value"in n&&e.setOption(t,n.value)})},this.setDefaultValue=function(e,t,n){var r=this.$defaultOptions[e]||(this.$defaultOptions[e]={});r[t]&&(r.forwardTo?this.setDefaultValue(r.forwardTo,t,n):r[t].value=n)},this.setDefaultValues=function(e,t){Object.keys(t).forEach(function(n){this.setDefaultValue(e,n,t[n])},this)},this.warn=o,this.reportError=u}).call(a.prototype),t.AppConfig=a}),define("ace/config",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/lib/net","ace/lib/app_config"],function(e,t,n){"no use strict";function f(r){if(!u||!u.document)return;a.packaged=r||e.packaged||n.packaged||u.define&&define.packaged;var i={},s="",o=document.currentScript||document._currentScript,f=o&&o.ownerDocument||document,c=f.getElementsByTagName("script");for(var h=0;h<c.length;h++){var p=c[h],d=p.src||p.getAttribute("src");if(!d)continue;var v=p.attributes;for(var m=0,g=v.length;m<g;m++){var y=v[m];y.name.indexOf("data-ace-")===0&&(i[l(y.name.replace(/^data-ace-/,""))]=y.value)}var b=d.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);b&&(s=b[1])}s&&(i.base=i.base||s,i.packaged=!0),i.basePath=i.base,i.workerPath=i.workerPath||i.base,i.modePath=i.modePath||i.base,i.themePath=i.themePath||i.base,delete i.base;for(var w in i)typeof i[w]!="undefined"&&t.set(w,i[w])}function l(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./lib/net"),o=e("./lib/app_config").AppConfig;n.exports=t=new o;var u=function(){return this||typeof window!="undefined"&&window}(),a={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{}};t.get=function(e){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);return a[e]},t.set=function(e,t){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);a[e]=t},t.all=function(){return r.copyObject(a)},t.moduleUrl=function(e,t){if(a.$moduleUrls[e])return a.$moduleUrls[e];var n=e.split("/");t=t||n[n.length-2]||"";var r=t=="snippets"?"/":"-",i=n[n.length-1];if(t=="worker"&&r=="-"){var s=new RegExp("^"+t+"[\\-_]|[\\-_]"+t+"$","g");i=i.replace(s,"")}(!i||i==t)&&n.length>1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?t<this.start.column?-1:t>this.end.column?1:0:e<this.start.row?-1:e>this.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.row<e)var n={row:e,column:0};if(this.start.row>t)var r={row:t+1,column:0};else if(this.start.row<e)var r={row:e,column:0};return i.fromPoints(r||this.start,n||this.end)},this.extend=function(e,t){var n=this.compare(e,t);if(n==0)return this;if(n==-1)var r={row:e,column:t};else var s={row:e,column:t};return i.fromPoints(r||this.start,s||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return i.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new i(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new i(this.start.row,0,this.end.row,0)},this.toScreenRange=function(e){var t=e.documentToScreenPosition(this.start),n=e.documentToScreenPosition(this.end);return new i(t.row,t.column,n.row,n.column)},this.moveBy=function(e,t){this.start.row+=e,this.start.column+=t,this.end.row+=e,this.end.column+=t}}).call(i.prototype),i.fromPoints=function(e,t){return new i(e.row,e.column,t.row,t.column)},i.comparePoints=r,i.comparePoints=function(e,t){return e.row-t.row||e.column-t.column},t.Range=i}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.lead=this.selectionLead=this.doc.createAnchor(0,0),this.anchor=this.selectionAnchor=this.doc.createAnchor(0,0);var t=this;this.lead.on("change",function(e){t._emit("changeCursor"),t.$isEmpty||t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){t.$isEmpty||t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.anchor.setPosition(e,t),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.shiftSelection=function(e){if(this.$isEmpty){this.moveCursorTo(this.lead.row,this.lead.column+e);return}var t=this.getSelectionAnchor(),n=this.getSelectionLead(),r=this.isBackwards();(!r||t.column!==0)&&this.setSelectionAnchor(t.row,t.column+e),(r||n.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(n.row,n.column+e)})},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row<this.doc.getLength()-1&&this.moveCursorTo(this.lead.row+1,0);else{var n=this.session.getTabSize(),e=this.lead;this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column,e.column+n).split(" ").length-1==n?this.moveCursorBy(0,n):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var e=this.lead.row,t=this.lead.column,n=this.session.documentToScreenRow(e,t),r=this.session.screenToDocumentPosition(n,0),i=this.session.getDisplayLine(e,null,r.row,r.column),s=i.match(/^\s*/);s[0].length!=t&&!this.session.$useEmacsStyleLineStart&&(r.column+=s[0].length),this.moveCursorToPosition(r)},this.moveCursorLineEnd=function(){var e=this.lead,t=this.session.getDocumentLastRowColumnPosition(e.row,e.column);if(this.lead.column==t.column){var n=this.session.getLine(t.row);if(t.column==n.length){var r=n.search(/\s+$/);r>0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e<this.doc.getLength()-1&&this.moveCursorWordRight();return}if(i=this.session.tokenRe.exec(r))t+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.moveCursorLongWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1)){this.moveCursorTo(n.start.row,n.start.column);return}var r=this.session.getFoldStringAt(e,t,-1);r==null&&(r=this.doc.getLine(e).substring(0,t));var s=i.stringReverse(r),o;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(o=this.session.nonTokenRe.exec(s))t-=this.session.nonTokenRe.lastIndex,s=s.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0;if(t<=0){this.moveCursorTo(e,0),this.moveCursorLeft(),e>0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e<s&&/^\s*$/.test(r));/^\s+/.test(r)||(r=""),t=0}var o=this.$shortWordEndIndex(r);this.moveCursorTo(e,t+o)},this.moveCursorShortWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1))return this.moveCursorTo(n.start.row,n.start.column);var r=this.session.getLine(e).substring(0,t);if(t===0){do e--,r=this.doc.getLine(e);while(e>0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a<n.length;a++){var f=n[a];f.defaultToken&&(s.defaultToken=f.defaultToken),f.caseInsensitive&&(o="gi");if(f.regex==null)continue;f.regex instanceof RegExp&&(f.regex=f.regex.toString().slice(1,-1));var l=f.regex,c=(new RegExp("(?:("+l+")|(.))")).exec("a").length-2;Array.isArray(f.token)?f.token.length==1||c==1?f.token=f.token[0]:c-1!=f.token.length?(this.reportError("number of classes and regexp groups doesn't match",{rule:f,groupCount:c-1}),f.token=f.token[0]):(f.tokenArray=f.token,f.token=null,f.onMatch=this.$arrayTokens):typeof f.token=="function"&&!f.onMatch&&(c>1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;i<s;i++)t[i]&&(r[r.length]={type:n[i],value:t[i]});return r},this.$arrayTokens=function(e){if(!e)return[];var t=this.splitRegex.exec(e);if(!t)return"text";var n=[],r=this.tokenArray;for(var i=0,s=r.length;i<s;i++)t[i+1]&&(n[n.length]={type:r[i],value:t[i+1]});return n},this.removeCapturingGroups=function(e){var t=e.replace(/\[(?:\\.|[^\]])*?\]|\\.|\(\?[:=!]|(\()/g,function(e,t){return t?"(?:":e});return t},this.createSplitterRegexp=function(e,t){if(e.indexOf("(?=")!=-1){var n=0,r=!1,i={};e.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g,function(e,t,s,o,u,a){return r?r=u!="]":u?r=!0:o?(n==i.stack&&(i.end=a+1,i.stack=-1),n--):s&&(n++,s.length!=1&&(i.stack=n,i.start=a)),e}),i.end!=null&&/^\)*$/.test(e.substr(i.end))&&(e=e.substring(0,i.start)+e.substr(i.end))}return e.charAt(0)!="^"&&(e="^"+e),e.charAt(e.length-1)!="$"&&(e+="$"),new RegExp(e,(t||"").replace("g",""))},this.getLineTokens=function(e,t){if(t&&typeof t!="string"){var n=t.slice(0);t=n[0],t==="#tmp"&&(n.shift(),t=n.shift())}else var n=[];var r=t||"start",s=this.states[r];s||(r="start",s=this.states[r]);var o=this.matchMappings[r],u=this.regExps[r];u.lastIndex=0;var a,f=[],l=0,c=0,h={type:null,value:""};while(a=u.exec(e)){var p=o.defaultToken,d=null,v=a[0],m=u.lastIndex;if(m-v.length>l){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;y<a.length-2;y++){if(a[y+1]===undefined)continue;d=s[o[y]],d.onMatch?p=d.onMatch(v,r,n):p=d.token,d.next&&(typeof d.next=="string"?r=d.next:r=d.next(r,n),s=this.states[r],s||(this.reportError("state doesn't exist",r),r="start",s=this.states[r]),o=this.matchMappings[r],l=m,u=this.regExps[r],u.lastIndex=m);break}if(v)if(typeof p=="string")!!d&&d.merge===!1||h.type!==p?(h.type&&f.push(h),h={type:p,value:v}):h.value+=v;else if(p){h.type&&f.push(h),h={type:null,value:""};for(var y=0;y<p.length;y++)f.push(p[y])}if(l==e.length)break;l=m;if(c++>i){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l<e.length)h.type&&f.push(h),h={value:e.substring(l,l+=2e3),type:"overflow"};r="start",n=[];break}}return h.type&&f.push(h),n.length>1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i<r.length;i++){var s=r[i];if(s.next||s.onMatch)typeof s.next=="string"&&s.next.indexOf(t)!==0&&(s.next=t+s.next),s.nextState&&s.nextState.indexOf(t)!==0&&(s.nextState=t+s.nextState)}this.$rules[t+n]=r}},this.getRules=function(){return this.$rules},this.embedRules=function(e,t,n,i,s){var o=typeof e=="function"?(new e).getRules():e;if(i)for(var u=0;u<i.length;u++)i[u]=t+i[u];else{i=[];for(var a in o)i.push(t+a)}this.addRules(o,t);if(n){var f=Array.prototype[s?"push":"unshift"];for(var u=0;u<i.length;u++)f.apply(this.$rules[i[u]],r.deepCopy(n))}this.$embeds||(this.$embeds=[]),this.$embeds.push(t)},this.getEmbeds=function(){return this.$embeds};var e=function(e,t){return(e!="start"||t.length)&&t.unshift(this.nextState,e),this.nextState},t=function(e,t){return t.shift(),t.shift()||"start"};this.normalizeRules=function(){function i(s){var o=r[s];o.processed=!0;for(var u=0;u<o.length;u++){var a=o[u],f=null;Array.isArray(a)&&(f=a,a={}),!a.regex&&a.start&&(a.regex=a.start,a.next||(a.next=[]),a.next.push({defaultToken:a.token},{token:a.token+".end",regex:a.end||a.start,next:"pop"}),a.token=a.token+".start",a.push=!0);var l=a.next||a.push;if(l&&Array.isArray(l)){var c=a.stateName;c||(c=a.token,typeof c!="string"&&(c=c[0]||""),r[c]&&(c+=n++)),r[c]=l,a.next=c,i(c)}else l=="pop"&&(a.next=t);a.push&&(a.nextState=a.next||a.push,a.next=e,delete a.push);if(a.rules)for(var h in a.rules)r[h]?r[h].push&&r[h].push.apply(r[h],a.rules[h]):r[h]=a.rules[h];var p=typeof a=="string"?a:typeof a.include=="string"?a.include:"";p&&(f=r[p]);if(f){var d=[u,1].concat(f);a.noEscape&&(d=d.filter(function(e){return!e.next})),o.splice.apply(o,d),u--}a.keywordMap&&(a.token=this.createKeywordMapper(a.keywordMap,a.defaultToken||"text",a.caseInsensitive),delete a.defaultToken)}}var n=0,r=this.$rules;Object.keys(r).forEach(i,this)},this.createKeywordMapper=function(e,t,n,r){var i=Object.create(null);return Object.keys(e).forEach(function(t){var s=e[t];n&&(s=s.toLowerCase());var o=s.split(r||"|");for(var u=o.length;u--;)i[o[u]]=t}),Object.getPrototypeOf(i)&&(i.__proto__=null),this.$keywordList=Object.keys(i),e=null,n?function(e){return i[e.toLowerCase()]||t}:function(e){return i[e]||t}},this.getKeywords=function(){return this.$keywords}}).call(i.prototype),t.TextHighlightRules=i}),define("ace/mode/behaviour",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.$behaviours={}};(function(){this.add=function(e,t,n){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[e]:this.$behaviours[e]={}}this.$behaviours[e][t]=n},this.addBehaviours=function(e){for(var t in e)for(var n in e[t])this.add(t,n,e[t][n])},this.remove=function(e){this.$behaviours&&this.$behaviours[e]&&delete this.$behaviours[e]},this.inherit=function(e,t){if(typeof e=="function")var n=(new e).getBehaviours(t);else var n=e.getBehaviours(t);this.addBehaviours(n)},this.getBehaviours=function(e){if(!e)return this.$behaviours;var t={};for(var n=0;n<e.length;n++)this.$behaviours[e[n]]&&(t[e[n]]=this.$behaviours[e[n]]);return t}}).call(r.prototype),t.Behaviour=r}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n,"\\u$&")}t.packages={},r({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/token_iterator",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t,n){this.$session=e,this.$row=t,this.$rowTokens=e.getTokens(t);var r=e.getTokenAt(t,n);this.$tokenIndex=r?r.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){this.$tokenIndex+=1;var e;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(r.prototype),t.TokenIterator=r}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour").Behaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i,this.$behaviour=new s};(function(){this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules,this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i<r.length;i++)if(r[i].type==="comment")return!0}}else{if(Array.isArray(this.lineCommentStart))var p=this.lineCommentStart.map(u.escapeRegExp).join("|"),c=this.lineCommentStart[0];else var p=u.escapeRegExp(this.lineCommentStart),c=this.lineCommentStart;p=new RegExp("^(\\s*)(?:"+p+") ?"),l=t.getUseSoftTabs();var m=function(e,t){var n=e.match(p);if(!n)return;var r=n[1].length,s=n[0].length;!b(e,r,s)&&n[0][s-1]==" "&&s--,i.removeInLine(t,r,s)},y=c+" ",v=function(e,t){if(!s||/\S/.test(e))b(e,a,a)?i.insertInLine({row:t,column:a},y):i.insertInLine({row:t,column:a},c)},g=function(e,t){return p.test(e)},b=function(e,t,n){var r=0;while(t--&&e.charAt(t)==" ")r++;if(r%f!=0)return!1;var r=0;while(e.charAt(n++)==" ")r++;return f>2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(n<a&&(a=n),o&&!g(e,t)&&(o=!1)):E>e.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t<n.length;t++)(function(e){var r=n[t],i=e[r];e[n[t]]=function(){return this.$delegator(r,arguments,i)}})(this)},this.$delegator=function(e,t,n){var r=t[0];typeof r!="string"&&(r=r[0]);for(var i=0;i<this.$embeds.length;i++){if(!this.$modes[this.$embeds[i]])continue;var s=r.split(this.$embeds[i]);if(!s[0]&&s[1]){t[0]=s[1];var o=this.$modes[this.$embeds[i]];return o[e].apply(o,t)}}var u=n.apply(this,t);return n?u:undefined},this.transformAction=function(e,t,n,r,i){if(this.$behaviour){var s=this.$behaviour.getBehaviours();for(var o in s)if(s[o][t]){var u=s[o][t].apply(this,arguments);if(u)return u}}},this.getKeywords=function(e){if(!this.completionKeywords){var t=this.$tokenizer.rules,n=[];for(var r in t){var i=t[r];for(var s=0,o=i.length;s<o;s++)if(typeof i[s].token=="string")/keyword|support|storage/.test(i[s].token)&&n.push(i[s].regex);else if(typeof i[s].token=="object")for(var u=0,a=i[s].token.length;u<a;u++)if(/keyword|support|storage/.test(i[s].token[u])){var r=i[s].regex.match(/\(.+?\)/g)[u];n.push(r.substr(1,r.length-2))}}this.completionKeywords=n}return e?n.concat(this.$keywordList||[]):this.$keywordList},this.$createKeywordList=function(){return this.$highlightRules||this.getTokenizer(),this.$keywordList=this.$highlightRules.$keywordList||[]},this.getCompletions=function(e,t,n,r){var i=this.$keywordList||this.$createKeywordList();return i.map(function(e){return{name:e,value:e,score:0,meta:"keyword"}})},this.$id="ace/mode/text"}).call(l.prototype),t.Mode=l}),define("ace/apply_delta",["require","exports","module"],function(e,t,n){"use strict";function r(e,t){throw console.log("Invalid Delta:",e),"Invalid Delta: "+t}function i(e,t){return t.row>=0&&t.row<e.length&&t.column>=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.column<t.column;return e.row<t.row||e.row==t.row&&r}function t(t,n,r){var i=t.action=="insert",s=(i?1:-1)*(t.end.row-t.start.row),o=(i?1:-1)*(t.end.column-t.start.column),u=t.start,a=i?u:t.end;return e(n,u,r)?{row:n.row,column:n.column}:e(a,n,!r)?{row:n.row+s,column:n.column+(n.row==a.row?o:0)}:{row:u.row,column:u.column}}r.implement(this,i),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.$insertRight=!1,this.onChange=function(e){if(e.start.row==e.end.row&&e.start.row!=this.row)return;if(e.start.row>this.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e<this.getLength()?(t=t.concat([""]),n=0):(t=[""].concat(t),e--,n=this.$lines[e].length),this.insertMergedLines({row:e,column:n},t)},this.insertMergedLines=function(e,t){var n=this.clippedPos(e.row,e.column),r={row:n.row+t.length-1,column:(t.length==1?n.column:0)+t[t.length-1].length};return this.applyDelta({start:n,end:r,action:"insert",lines:t}),this.clonePos(r)},this.remove=function(e){var t=this.clippedPos(e.start.row,e.start.column),n=this.clippedPos(e.end.row,e.end.column);return this.applyDelta({start:t,end:n,action:"remove",lines:this.getLinesForRange({start:t,end:n})}),this.clonePos(t)},this.removeInLine=function(e,t,n){var r=this.clippedPos(e,t),i=this.clippedPos(e,n);return this.applyDelta({start:r,end:i,action:"remove",lines:this.getLinesForRange({start:r,end:i})},!0),this.clonePos(r)},this.removeFullLines=function(e,t){e=Math.min(Math.max(0,e),this.getLength()-1),t=Math.min(Math.max(0,t),this.getLength()-1);var n=t==this.getLength()-1&&e>0,r=t<this.getLength()-1,i=n?e-1:e,s=n?this.getLine(i).length:0,u=r?t+1:t,a=r?0:this.getLine(u).length,f=new o(i,s,u,a),l=this.$lines.slice(e,t+1);return this.applyDelta({start:f.start,end:f.end,action:"remove",lines:this.getLinesForRange(f)}),l},this.removeNewLine=function(e){e<this.getLength()-1&&e>=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t<e.length;t++)this.applyDelta(e[t])},this.revertDeltas=function(e){for(var t=e.length-1;t>=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i<s;i++){e-=n[i].length+r;if(e<0)return{row:i,column:e+n[i].length+r}}return{row:s-1,column:n[s-1].length}},this.positionToIndex=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length,i=0,s=Math.min(e.row,n.length);for(var o=t||0;o<s;++o)i+=n[o].length+r;return i+e.column}}).call(a.prototype),t.Document=a}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var n=this;this.$worker=function(){if(!n.running)return;var e=new Date,t=n.currentLine,r=-1,i=n.doc,s=t;while(n.lines[t])t++;var o=i.getLength(),u=0;n.running=!1;while(t<o){n.$tokenizeRow(t),r=t;do t++;while(n.lines[t]);u++;if(u%5===0&&new Date-e>20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.row<this.startRow||e.endRow>this.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f<i.length;f++){s=i[f],o=s.range.compareStart(t,n);if(o==-1){e(null,t,n,r,a);return}u=e(null,s.start.row,s.start.column,r,a),u=!u&&e(s.placeholder,s.start.row,s.start.column,r);if(u||o===0)return;a=!s.sameRow,r=s.end.column}e(null,t,n,r,a)},this.getNextFoldTo=function(e,t){var n,r;for(var i=0;i<this.folds.length;i++){n=this.folds[i],r=n.range.compareEnd(e,t);if(r==-1)return{fold:n,kind:"after"};if(r===0)return{fold:n,kind:"inside"}}return null},this.addRemoveChars=function(e,t,n){var r=this.getNextFoldTo(e,t),i,s;if(r){i=r.fold;if(r.kind=="inside"&&i.start.column!=t&&i.start.row!=e)window.console&&window.console.log(e,t,i);else if(i.start.row==e){s=this.folds;var o=s.indexOf(i);o===0&&(this.start.column+=n);for(o;o<s.length;o++){i=s[o],i.start.column+=n;if(!i.sameRow)return;i.end.column+=n}this.end.column+=n}}},this.split=function(e,t){var n=this.getNextFoldTo(e,t);if(!n||n.kind=="inside")return null;var r=n.fold,s=this.folds,o=this.foldData,u=s.indexOf(r),a=s[u-1];this.end.row=a.end.row,this.end.column=a.end.column,s=s.splice(u,s.length-u);var f=new i(o,s);return o.splice(o.indexOf(this)+1,0,f),f},this.merge=function(e){var t=e.folds;for(var n=0;n<t.length;n++)this.addFold(t[n]);var r=this.foldData;r.splice(r.indexOf(e),1)},this.toString=function(){var e=[this.range.toString()+": ["];return this.folds.forEach(function(t){e.push("  "+t.toString())}),e.push("]"),e.join("\n")},this.idxToPosition=function(e){var t=0;for(var n=0;n<this.folds.length;n++){var r=this.folds[n];e-=r.start.column-t;if(e<0)return{row:r.start.row,column:r.start.column+e};e-=r.placeholder.length;if(e<0)return r.start;t=r.end.column}return{row:this.end.row,column:this.end.column+e}}}).call(i.prototype),t.FoldLine=i}),define("ace/range_list",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("./range").Range,i=r.comparePoints,s=function(){this.ranges=[]};(function(){this.comparePoints=i,this.pointIndex=function(e,t,n){var r=this.ranges;for(var s=n||0;s<r.length;s++){var o=r[s],u=i(e,o.end);if(u>0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s<t.length;s++){r=n,n=t[s];var o=i(r.end,n.start);if(o<0)continue;if(o==0&&!r.isEmpty()&&!n.isEmpty())continue;i(r.end,n.end)<0&&(r.end.row=n.end.row,r.end.column=n.end.column),t.splice(s,1),e.push(n),n=r,s--}return this.ranges=t,e},this.contains=function(e,t){return this.pointIndex({row:e,column:t})>=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row<e)return[];var r=this.pointIndex({row:e,column:0});r<0&&(r=-r-1);var i=this.pointIndex({row:t,column:0},r);i<0&&(i=-i-1);var s=[];for(var o=r;o<i;o++)s.push(n[o]);return s},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(e){this.session&&this.detach(),this.session=e,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener("change",this.onChange),this.session=null},this.$onChange=function(e){if(e.action=="insert")var t=e.start,n=e.end;else var n=e.start,t=e.end;var r=t.row,i=n.row,s=i-r,o=-t.column+n.column,u=this.ranges;for(var a=0,f=u.length;a<f;a++){var l=u[a];if(l.end.row<r)continue;if(l.start.row>r)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&a<f-1&&l.end.column>l.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a<f)for(;a<f;a++){var l=u[a];l.start.row+=s,l.end.row+=s}}}).call(s.prototype),t.RangeList=s}),define("ace/edit_session/fold",["require","exports","module","ace/range","ace/range_list","ace/lib/oop"],function(e,t,n){"use strict";function u(e,t){e.row-=t.row,e.row==0&&(e.column-=t.column)}function a(e,t){u(e.start,t),u(e.end,t)}function f(e,t){e.row==0&&(e.column+=t.column),e.row+=t.row}function l(e,t){f(e.start,t),f(e.end,t)}var r=e("../range").Range,i=e("../range_list").RangeList,s=e("../lib/oop"),o=t.Fold=function(e,t){this.foldLine=null,this.placeholder=t,this.range=e,this.start=e.start,this.end=e.end,this.sameRow=e.start.row==e.end.row,this.subFolds=this.ranges=[]};s.inherits(o,i),function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(e){this.foldLine=e,this.subFolds.forEach(function(t){t.setFoldLine(e)})},this.clone=function(){var e=this.range.clone(),t=new o(e,this.placeholder);return this.subFolds.forEach(function(e){t.subFolds.push(e.clone())}),t.collapseChildren=this.collapseChildren,t},this.addSubFold=function(e){if(this.range.isEqual(e))return;if(!this.range.containsRange(e))throw new Error("A fold can't intersect already existing fold"+e.range+this.range);a(e,this.start);var t=e.start.row,n=e.start.column;for(var r=0,i=-1;r<this.subFolds.length;r++){i=this.subFolds[r].range.compare(t,n);if(i!=1)break}var s=this.subFolds[r];if(i==0)return s.addSubFold(e);var t=e.range.end.row,n=e.range.end.column;for(var o=r,i=-1;o<this.subFolds.length;o++){i=this.subFolds[o].range.compare(t,n);if(i!=1)break}var u=this.subFolds[o];if(i==0)throw new Error("A fold can't intersect already existing fold"+e.range+this.range);var f=this.subFolds.splice(r,o-r,e);return e.setFoldLine(this.foldLine),e},this.restoreRange=function(e){return l(e,this.start)}}.call(o.prototype)}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(e,t,n){"use strict";function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if(!r)return null;var i=r.folds;for(var s=0;s<i.length;s++){var o=i[s];if(o.range.contains(e,t)){if(n==1&&o.range.isEnd(e,t))continue;if(n==-1&&o.range.isStart(e,t))continue;return o}}},this.getFoldsInRange=function(e){var t=e.start,n=e.end,r=this.$foldData,i=[];t.column+=1,n.column-=1;for(var s=0;s<r.length;s++){var o=r[s].range.compareRange(e);if(o==2)continue;if(o==-2)break;var u=r[s].folds;for(var a=0;a<u.length;a++){var f=u[a];o=f.range.compareRange(e);if(o==-2)break;if(o==2)continue;if(o==42)break;i.push(f)}}return t.column-=1,n.column+=1,i},this.getFoldsInRangeList=function(e){if(Array.isArray(e)){var t=[];e.forEach(function(e){t=t.concat(this.getFoldsInRange(e))},this)}else var t=this.getFoldsInRange(e);return t},this.getAllFolds=function(){var e=[],t=this.$foldData;for(var n=0;n<t.length;n++)for(var r=0;r<t[n].folds.length;r++)e.push(t[n].folds[r]);return e},this.getFoldStringAt=function(e,t,n,r){r=r||this.getFoldLine(e);if(!r)return null;var i={end:{column:0}},s,o;for(var u=0;u<r.folds.length;u++){o=r.folds[u];var a=o.range.compareEnd(e,t);if(a==-1){s=this.getLine(o.start.row).substring(i.end.column,o.start.column);break}if(a===0)return null;i=o}return s||(s=this.getLine(o.start.row).substring(i.end.column)),n==-1?s.substring(0,t-i.end.column):n==1?s.substring(t-i.end.column):s},this.getFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.start.row<=e&&i.end.row>=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.end.row>=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i<n.length;i++){var s=n[i],o=s.end.row,u=s.start.row;if(o>=t){u<t&&(u>=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u<f||u==f&&a<=l-2){var c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d<n.length;d++){var v=n[d];if(f==v.start.row){v.addFold(o),r=!0;break}if(u==v.end.row){v.addFold(o),r=!0;if(!o.sameRow){var m=n[d+1];if(m&&m.start.row==f){v.merge(m);break}}break}if(f<=v.start.row)break}return r||(v=this.$addFoldLine(new i(this.$foldData,o))),this.$useWrapMode?this.$updateWrapData(v.start.row,v.start.row):this.$updateRowLengthCache(v.start.row,v.start.row),this.$modified=!0,this._signal("changeFold",{data:o,action:"add"}),o}throw new Error("The range has to be at least 2 characters width")},this.addFolds=function(e){e.forEach(function(e){this.addFold(e)},this)},this.removeFold=function(e){var t=e.foldLine,n=t.start.row,r=t.end.row,i=this.$foldData,s=t.folds;if(s.length==1)i.splice(i.indexOf(t),1);else if(t.range.isEnd(e.end.row,e.end.column))s.pop(),t.end.row=s[s.length-1].end.row,t.end.column=s[s.length-1].end.column;else if(t.range.isStart(e.start.row,e.start.column))s.shift(),t.start.row=s[0].start.row,t.start.column=s[0].start.column;else if(e.sameRow)s.splice(s.indexOf(e),1);else{var o=t.split(e.start.row,e.start.column);s=o.folds,s.shift(),o.start.row=s[0].start.row,o.start.column=s[0].start.column}this.$updating||(this.$useWrapMode?this.$updateWrapData(n,r):this.$updateRowLengthCache(n,r)),this.$modified=!0,this._signal("changeFold",{data:e,action:"remove"})},this.removeFolds=function(e){var t=[];for(var n=0;n<e.length;n++)t.push(e[n]);t.forEach(function(e){this.removeFold(e)},this),this.$modified=!0},this.expandFold=function(e){this.removeFold(e),e.subFolds.forEach(function(t){e.restoreRange(t),this.addFold(t)},this),e.collapseChildren>0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t<r)return;if(t==r){if(n<i)return;u=Math.max(i,u)}e!=null?o+=e:o+=s.getLine(t).substring(u,n)},t,n),o},this.getDisplayLine=function(e,t,n,r){var i=this.getFoldLine(e);if(!i){var s;return s=this.doc.getLine(e),s.substring(r||0,t||s.length)}return this.getFoldDisplayLine(i,e,t,n,r)},this.$cloneFoldData=function(){var e=[];return e=this.$foldData.map(function(t){var n=t.folds.map(function(e){return e.clone()});return new i(e,n)}),e},this.toggleFold=function(e){var t=this.selection,n=t.getRange(),r,i;if(n.isEmpty()){var s=n.start;r=this.getFoldAt(s.row,s.column);if(r){this.expandFold(r);return}(i=this.findMatchingBracket(s))?n.comparePoint(i)==1?n.end=i:(n.start=i,n.start.column++,n.end.column--):(i=this.findMatchingBracket({row:s.row,column:s.column+1}))?(n.comparePoint(i)==1?n.end=i:n.start=i,n.start.column++):n=this.getCommentFoldRange(s.row,s.column)||n}else{var o=this.getFoldsInRange(n);if(e&&o.length){this.expandFolds(o);return}o.length==1&&(r=o[0])}r||(r=this.getFoldAt(n.start.row,n.start.column));if(r&&r.range.toString()==n.toString()){this.expandFold(r);return}var u="...";if(!n.isMultiLine()){u=this.getTextRange(n);if(u.length<4)return;u=u.trim().substring(0,2)+".."}this.addFold(u,n)},this.getCommentFoldRange=function(e,t,n){var i=new o(this,e,t),s=i.getCurrentToken();if(s&&/^comment|string/.test(s.type)){var u=new r,a=new RegExp(s.type.replace(/\..*/,"\\."));if(n!=1){do s=i.stepBackward();while(s&&a.test(s.type));i.stepForward()}u.start.row=i.getCurrentTokenRow(),u.start.column=i.getCurrentTokenColumn()+2,i=new o(this,e,t);if(n!=-1){do s=i.stepForward();while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return u.end.row=i.getCurrentTokenRow(),u.end.column=i.getCurrentTokenColumn()+s.value.length-2,u}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i<t;i++){r[i]==null&&(r[i]=this.getFoldWidget(i));if(r[i]!="start")continue;var s=this.getFoldWidgetRange(i);if(s&&s.isMultiLine()&&s.end.row<=t&&s.start.row>=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s){t.children||t.all?this.removeFold(s):this.expandFold(s);return}var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range)){this.removeFold(s);return}}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a<l){var c=f.charAt(a);if(c==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else c==e&&(s+=1);a+=1}do u=o.stepForward();while(u&&!n.test(u.type));if(u==null)break;a=0}return null}}var r=e("../token_iterator").TokenIterator,i=e("../range").Range;t.BracketMatch=s}),define("ace/edit_session",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/config","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./config"),o=e("./lib/event_emitter").EventEmitter,u=e("./selection").Selection,a=e("./mode/text").Mode,f=e("./range").Range,l=e("./document").Document,c=e("./background_tokenizer").BackgroundTokenizer,h=e("./search_highlight").SearchHighlight,p=function(e,t){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.$foldData.toString=function(){return this.join("\n")},this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof e!="object"||!e.getLine)e=new l(e);this.setDocument(e),this.selection=new u(this),s.resetOptions(this),this.setMode(t),s._signal("session",this)};(function(){function m(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t<s))return i;r=i-1}}return n-1},this.resetCaches=function(){this.$modified=!0,this.$wrapData=[],this.$rowLengthCache=[],this.$resetRowCache(0),this.bgTokenizer&&this.bgTokenizer.start(0)},this.onChangeFold=function(e){var t=e.data;this.$resetRowCache(t.start.row)},this.onChange=function(e){this.$modified=!0,this.$resetRowCache(e.start.row);var t=this.$updateInternalDataOnChange(e);!this.$fromUndo&&this.$undoManager&&!e.ignore&&(this.$deltasDoc.push(e),t&&t.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:t}),this.$informUndoManager.schedule()),this.bgTokenizer&&this.bgTokenizer.$updateOnChange(e),this._signal("change",e)},this.setValue=function(e){this.doc.setValue(e),this.selection.moveTo(0,0),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.setUndoManager(this.$undoManager),this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(e){return this.bgTokenizer.getState(e)},this.getTokens=function(e){return this.bgTokenizer.getTokens(e)},this.getTokenAt=function(e,t){var n=this.bgTokenizer.getTokens(e),r,i=0;if(t==null)s=n.length-1,i=this.getLine(e).length;else for(var s=0;s<n.length;s++){i+=n[s].value.length;if(i>=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):"	"},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t<e.length;t++)this.$breakpoints[e[t]]="ace_breakpoint";this._signal("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._signal("changeBreakpoint",{})},this.setBreakpoint=function(e,t){t===undefined&&(t="ace_breakpoint"),t?this.$breakpoints[e]=t:delete this.$breakpoints[e],this._signal("changeBreakpoint",{})},this.clearBreakpoint=function(e){delete this.$breakpoints[e],this._signal("changeBreakpoint",{})},this.addMarker=function(e,t,n,r){var i=this.$markerId++,s={range:e,type:n||"line",renderer:typeof n=="function"?n:null,clazz:t,inFront:!!r,id:i};return r?(this.$frontMarkers[i]=s,this._signal("changeFrontMarker")):(this.$backMarkers[i]=s,this._signal("changeBackMarker")),i},this.addDynamicMarker=function(e,t){if(!e.update)return;var n=this.$markerId++;return e.id=n,e.inFront=!!t,t?(this.$frontMarkers[n]=e,this._signal("changeFrontMarker")):(this.$backMarkers[n]=e,this._signal("changeBackMarker")),e},this.removeMarker=function(e){var t=this.$frontMarkers[e]||this.$backMarkers[e];if(!t)return;var n=t.inFront?this.$frontMarkers:this.$backMarkers;t&&(delete n[e],this._signal(t.inFront?"changeFrontMarker":"changeBackMarker"))},this.getMarkers=function(e){return e?this.$frontMarkers:this.$backMarkers},this.highlight=function(e){if(!this.$searchHighlight){var t=new h(null,"ace_selected-word","text");this.$searchHighlight=this.addDynamicMarker(t)}this.$searchHighlight.setRegexp(e)},this.highlightLines=function(e,t,n,r){typeof t!="number"&&(n=t,t=e),n||(n="ace_step");var i=new f(e,0,t,Infinity);return i.id=this.addMarker(i,n,"fullLine",r),i},this.setAnnotations=function(e){this.$annotations=e,this._signal("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations||[]},this.clearAnnotations=function(){this.setAnnotations([])},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r?\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine="\n"},this.getWordRange=function(e,t){var n=this.getLine(e),r=!1;t>0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(o<n.length&&n.charAt(o).match(i))o++;return new f(e,s,e,o)},this.getAWordRange=function(e,t){var n=this.getWordRange(e,t),r=this.getLine(n.end.row);while(r.charAt(n.end.column).match(/[ \t]/))n.end.column+=1;return n},this.setNewLineMode=function(e){this.doc.setNewLineMode(e)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.setUseWorker=function(e){this.setOption("useWorker",e)},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(e){var t=e.data;this.bgTokenizer.start(t.first),this._signal("tokenizerUpdate",e)},this.$modes={},this.$mode=null,this.$modeId=null,this.setMode=function(e,t){if(e&&typeof e=="object"){if(e.getTokenizer)return this.$onChangeMode(e);var n=e,r=n.path}else r=e||"ace/mode/text";this.$modes["ace/mode/text"]||(this.$modes["ace/mode/text"]=new a);if(this.$modes[r]&&!n){this.$onChangeMode(this.$modes[r]),t&&t();return}this.$modeId=r,s.loadModule(["mode",r],function(e){if(this.$modeId!==r)return t&&t();this.$modes[r]&&!n?this.$onChangeMode(this.$modes[r]):e&&e.Mode&&(e=new e.Mode(n),n||(this.$modes[r]=e,e.$id=r),this.$onChangeMode(e)),t&&t()}.bind(this)),this.$mode||this.$onChangeMode(this.$modes["ace/mode/text"],!0)},this.$onChangeMode=function(e,t){t||(this.$modeId=e.$id);if(this.$mode===e)return;this.$mode=e,this.$stopWorker(),this.$useWorker&&this.$startWorker();var n=e.getTokenizer();if(n.addEventListener!==undefined){var r=this.onReloadTokenizer.bind(this);n.addEventListener("update",r)}if(!this.bgTokenizer){this.bgTokenizer=new c(n);var i=this;this.bgTokenizer.addEventListener("update",function(e){i._signal("tokenizerUpdate",e)})}else this.bgTokenizer.setTokenizer(n);this.bgTokenizer.setDocument(this.getDocument()),this.tokenRe=e.tokenRe,this.nonTokenRe=e.nonTokenRe,t||(e.attachToSession&&e.attachToSession(this),this.$options.wrapMethod.set.call(this,this.$wrapMethod),this.$setFolding(e.foldingRules),this.bgTokenizer.start(0),this._emit("changeMode"))},this.$stopWorker=function(){this.$worker&&(this.$worker.terminate(),this.$worker=null)},this.$startWorker=function(){try{this.$worker=this.$mode.createWorker(this)}catch(e){s.warn("Could not load worker",e),this.$worker=null}},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(e){if(this.$scrollTop===e||isNaN(e))return;this.$scrollTop=e,this._signal("changeScrollTop",e)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(e){if(this.$scrollLeft===e||isNaN(e))return;this.$scrollLeft=e,this._signal("changeScrollLeft",e)},this.getScrollLeft=function(){return this.$scrollLeft},this.getScreenWidth=function(){return this.$computeWidth(),this.lineWidgets?Math.max(this.getLineWidgetMaxWidth(),this.screenWidth):this.screenWidth},this.getLineWidgetMaxWidth=function(){if(this.lineWidgetsWidth!=null)return this.lineWidgetsWidth;var e=0;return this.lineWidgets.forEach(function(t){t&&t.screenWidth>e&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;a<u;a++){if(a>o){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;r<e.length;r++){var i=e[r];i.group=="doc"&&(this.doc.applyDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!1,n))}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.setUndoSelect=function(e){this.$undoSelect=e},this.$getUndoSelection=function(e,t,n){function r(e){return t?e.action!=="insert":e.action==="insert"}var i=e[0],s,o,u=!1;r(i)?(s=f.fromPoints(i.start,i.end),u=!0):(s=f.fromPoints(i.start,i.start),u=!1);for(var a=1;a<e.length;a++)i=e[a],r(i)?(o=i.start,s.compare(o.row,o.column)==-1&&s.setStart(o),o=i.end,s.compare(o.row,o.column)==1&&s.setEnd(o),u=!0):(o=i.start,s.compare(o.row,o.column)==-1&&(s=f.fromPoints(i.start,i.start)),u=!1);if(n!=null){f.comparePoints(n.start,s.start)===0&&(n.start.column+=s.end.column-s.start.column,n.end.column+=s.end.column-s.start.column);var l=n.compareRange(s);l==1?s.setStart(n.start):l==-1&&s.setEnd(n.end)}return s},this.replace=function(e,t){return this.doc.replace(e,t)},this.moveText=function(e,t,n){var r=this.getTextRange(e),i=this.getFoldsInRange(e),s=f.fromPoints(t,t);if(!n){this.remove(e);var o=e.start.row-e.end.row,u=o?-e.end.column:e.start.column-e.end.column;u&&(s.start.row==e.end.row&&s.start.column>e.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o<r;++o)if(s.charAt(o)!=" ")break;o<r&&s.charAt(o)=="	"?(n.start.column=o,n.end.column=o+1):(n.start.column=0,n.end.column=o),this.remove(n)}},this.$moveLines=function(e,t,n){e=this.getRowFoldStart(e),t=this.getRowFoldEnd(t);if(n<0){var r=this.getRowFoldStart(e+n);if(r<0)return 0;var i=r-e}else if(n>0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;f<o.length;f++)o[f]=u}else o=this.$getDisplayTokens(r[t].substring(s,i),a.length);a=a.concat(o)}.bind(this),f.end.row,r[f.end.row].length+1),s[f.start.row]=this.$computeWrapSplits(a,o,i),l=f.end.row+1):(a=this.$getDisplayTokens(r[l]),s[l]=this.$computeWrapSplits(a,o,i),l++)};var e=1,t=2,n=3,u=4,l=9,p=10,d=11,v=12;this.$computeWrapSplits=function(e,r,i){function g(){var t=0;if(m===0)return t;if(h)for(var n=0;n<e.length;n++){var r=e[n];if(r==p)t+=1;else{if(r!=d){if(r==v)continue;break}t+=i}}return c&&h!==!1&&(t+=i),Math.min(t,m)}function y(t){var n=e.slice(a,t),r=n.length;n.join("").replace(/12/g,function(){r-=1}).replace(/2/g,function(){r-=1}),s.length||(b=g(),s.indent=b),f+=r,s.push(f),a=t}if(e.length==0)return[];var s=[],o=e.length,a=0,f=0,c=this.$wrapAsCode,h=this.$indentedSoftWrap,m=r<=Math.max(2*i,8)||h===!1?0:Math.floor(r/2),b=0;while(o-a>r-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w<e.length;w++)if(e[w]!=u)break;if(w==e.length)break;y(w);continue}var E=Math.max(w-(r-(r>>2)),a-1);while(w>E&&e[w]<n)w--;if(c){while(w>E&&e[w]<n)w--;while(w>E&&e[w]==l)w--}else while(w>E&&e[w]<p)w--;if(w>E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o<n.length;o++){var u=n.charCodeAt(o);if(u==9){s=this.getScreenTabSize(i.length+r),i.push(d);for(var a=1;a<s;a++)i.push(v)}else u==32?i.push(p):u>39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i<e.length;i++){r=e.charCodeAt(i),r==9?n+=this.getScreenTabSize(n):r>=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]<t.column?n.indent:0}return 0},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t){if(e<0)return{row:0,column:0};var n,r=0,i=0,s,o=0,u=0,a=this.$screenRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u<e){if(u>=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;r<n.length;r++)t=n[r],e-=t.end.row-t.start.row}else{var i=this.$wrapData.length,s=0,r=0,t=this.$foldData[r++],o=t?t.start.row:Infinity;while(s<i){var u=this.$wrapData[s];e+=u?u.length+1:1,s++,s>o&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;s<t.length;s++){i=t.charAt(s),i==="	"?r+=this.getScreenTabSize(r):r+=e.getCharacterWidth(i);if(r>n)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i){if(!e.start){var o=e.offset+(i||0);r=new s(n,o,n,o+e.length);if(!e.length&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start))return r=null,!1}else r=e;return!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;h<a;h++)if(i[c+h].search(u[h])==-1)continue e;var p=i[c],d=i[c+a-1],v=p.length-p.match(u[0])[0].length,m=d.match(u[a-1])[0].length;if(l&&l.end.row===c&&l.end.column>v)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;g<i.length;g++){var y=r.getMatchOffsets(i[g],u);for(var h=0;h<y.length;h++){var b=y[h];o.push(new s(g,b.offset,g,b.offset+b.length))}}if(n){var w=n.start.column,E=n.start.column,g=0,h=o.length-1;while(g<h&&o[g].start.column<w&&o[g].start.row==n.start.row)g++;while(g<h&&o[h].end.column>E&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g<h;g++)o[g].start.row+=n.start.row,o[g].end.row+=n.start.row}return o},this.replace=function(e,t){var n=this.$options,r=this.$assembleRegExp(n);if(n.$isMultiLine)return t;if(!r)return;var i=r.exec(e);if(!i||i[0].length!=e.length)return null;t=e.replace(r,t);if(n.preserveCase){t=t.split("");for(var s=Math.min(e.length,e.length);s--;){var o=e[s];o&&o.toLowerCase()!=o?t[s]=t[s].toUpperCase():t[s]=t[s].toLowerCase()}t=t.join("")}return t},this.$matchIterator=function(e,t){var n=this.$assembleRegExp(t);if(!n)return!1;var i;if(t.$isMultiLine)var o=n.length,u=function(t,r,u){var a=t.search(n[0]);if(a==-1)return;for(var f=1;f<o;f++){t=e.getLine(r+f);if(t.search(n[f])==-1)return}var l=t.match(n[o-1])[0].length,c=new s(r,a,r+o-1,l);n.offset==1?(c.start.row--,c.start.column=Number.MAX_VALUE):u&&(c.start.column+=u);if(i(c))return!0};else if(t.backwards)var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=o.length-1;u>=0;u--)if(i(o[u],t,s))return!0};else var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=0;u<o.length;u++)if(i(o[u],t,s))return!0};var a=this.$lineIterator(e,t);return{forEach:function(e){i=e,a.forEach(u)}}},this.$assembleRegExp=function(e,t){if(e.needle instanceof RegExp)return e.re=e.needle;var n=e.needle;if(!e.needle)return e.re=!1;e.regExp||(n=r.escapeRegExp(n)),e.wholeWord&&(n="\\b"+n+"\\b");var i=e.caseSensitive?"gm":"gmi";e.$isMultiLine=!t&&/[\n\r]/.test(n);if(e.$isMultiLine)return e.re=this.$assembleMultilineRegExp(n,i);try{var s=new RegExp(n,i)}catch(o){s=!1}return e.re=s},this.$assembleMultilineRegExp=function(e,t){var n=e.replace(/\r\n|\r|\n/g,"$\n^").split("\n"),r=[];for(var i=0;i<n.length;i++)try{r.push(new RegExp(n[i],t))}catch(s){return!1}return n[0]==""?(r.shift(),r.offset=1):r.offset=0,r},this.$lineIterator=function(e,t){var n=t.backwards==1,r=t.skipCurrent!=0,i=t.range,s=t.start;s||(s=i?i[n?"end":"start"]:e.selection.getRange()),s.start&&(s=s[r!=n?"end":"start"]);var o=i?i.start.row:0,u=i?i.end.row:e.getLength()-1,a=n?function(n){var r=s.row,i=e.getLine(r).substring(0,s.column);if(n(i,r))return;for(r--;r>=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||0}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r||n.isDefault?r=-100:r=e(n));var o=i[t];for(s=0;s<o.length;s++){var u=o[s],a=e(u);if(a>r)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","Ctrl-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Ctrl-Shift-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+1<e.session.doc.getLength()-1&&(f+=e.session.doc.getNewLineCharacter()),e.clearSelection(),e.session.doc.replace(new s(n.row,0,i.row+2,0),f),a>0?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o<r.length;o++)o==r.length-1&&(r[o].end.row!==t||r[o].end.column!==n)&&i.push(new s(r[o].end.row,r[o].end.column,t,n)),o===0?(r[o].start.row!==0||r[o].start.column!==0)&&i.push(new s(0,0,r[o].start.row,r[o].start.column)):i.push(new s(r[o-1].end.row,r[o-1].end.column,r[o].start.row,r[o].start.column));e.exitMultiSelectMode(),e.clearSelection();for(var o=0;o<i.length;o++)e.selection.addRange(i[o],!1)},readOnly:!0,scrollIntoView:"none"}]}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands","ace/config","ace/token_iterator"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/lang"),o=e("./lib/useragent"),u=e("./keyboard/textinput").TextInput,a=e("./mouse/mouse_handler").MouseHandler,f=e("./mouse/fold_handler").FoldHandler,l=e("./keyboard/keybinding").KeyBinding,c=e("./edit_session").EditSession,h=e("./search").Search,p=e("./range").Range,d=e("./lib/event_emitter").EventEmitter,v=e("./commands/command_manager").CommandManager,m=e("./commands/default_commands").commands,g=e("./config"),y=e("./token_iterator").TokenIterator,b=function(e,t){var n=e.getContainerElement();this.container=n,this.renderer=e,this.commands=new v(o.isMac?"mac":"win",m),this.textInput=new u(e.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.keyBinding=new l(this),this.$mouseHandler=new a(this),new f(this),this.$blockScrolling=0,this.$search=(new h).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on("exec",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=s.delayedCall(function(){this._signal("input",{}),this.session&&this.session.bgTokenizer&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on("change",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(t||new c("")),g.resetOptions(this),g._signal("editor",this)};(function(){r.implement(this,d),this.$initOperationListeners=function(){function e(e){return e[e.length-1]}this.selections=[],this.commands.on("exec",this.startOperation.bind(this),!0),this.commands.on("afterExec",this.endOperation.bind(this),!0),this.$opResetTimer=s.delayedCall(this.endOperation.bind(this)),this.on("change",function(){this.curOp||this.startOperation(),this.curOp.docChanged=!0}.bind(this),!0),this.on("changeSelection",function(){this.curOp||this.startOperation(),this.curOp.selectionChanged=!0}.bind(this),!0)},this.curOp=null,this.prevOp={},this.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(this.previousCommand=null,e={}),this.$opResetTimer.schedule(),this.curOp={command:e.command||{},args:e.args,scrollTop:this.renderer.scrollTop},this.curOp.command.name&&this.curOp.command.scrollIntoView!==undefined&&this.$blockScrolling++},this.endOperation=function(e){if(this.curOp){if(e&&e.returnValue===!1)return this.curOp=null;this._signal("beforeEndOperation");var t=this.curOp.command;t.name&&this.$blockScrolling>0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="</"&&o--);while(i&&o>=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="</"&&o--);while(u&&o<=0);r.stepForward()}if(!i){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}var a=r.getCurrentTokenRow(),f=r.getCurrentTokenColumn(),l=new p(a,f,a,f+i.value.length);t.$tagHighlight&&l.compareRange(t.$backMarkers[t.$tagHighlight].range)!==0&&(t.removeMarker(t.$tagHighlight),t.$tagHighlight=null),l&&!t.$tagHighlight&&(t.$tagHighlight=t.addMarker(l,"ace_bracket","text"))},50)},this.focus=function(){var e=this;setTimeout(function(){e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(e){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus",e)},this.onBlur=function(e){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur",e)},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=this.session.$useWrapMode,n=e.start.row==e.end.row?e.end.row:Infinity;this.renderer.updateLines(e.start.row,n,t),this._signal("change",e),this.$cursorChange(),this.$updateHighlightActiveLine()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$blockScrolling||(g.warn("Automatically scrolling cursor into view after selection change","this will be disabled in the next version","set editor.$blockScrolling = Infinity to disable this message"),this.renderer.scrollCursorIntoView()),this.$highlightBrackets(),this.$highlightTags(),this.$updateHighlightActiveLine(),this._signal("changeSelection")},this.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!="line"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.$maxLines&&this.session.getLength()===1&&!(this.renderer.$minLines>1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e=="	"&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;t<n.length?(r=n.charAt(t)+n.charAt(t-1),i=new p(e.row,t-1,e.row,t+1)):(r=n.charAt(t-1)+n.charAt(t-2),i=new p(e.row,t-2,e.row,t)),this.session.replace(i,r)},this.toLowerCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toLowerCase()),this.selection.setSelectionRange(e)},this.toUpperCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toUpperCase()),this.selection.setSelectionRange(e)},this.indent=function(){var e=this.session,t=this.getSelectionRange();if(t.start.row<t.end.row){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,"	");return}if(t.start.column<t.end.column){var r=e.getTextRange(t);if(!/^\s+$/.test(r)){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,"	");return}}var i=e.getLine(t.start.row),o=t.start,u=e.getTabSize(),a=e.documentToScreenColumn(o.row,o.column);if(this.session.getUseSoftTabs())var f=u-a%u,l=s.stringRepeat(" ",f);else{var f=a%u;while(i[t.start.column]==" "&&f)t.start.column--,f--;this.selection.setSelectionRange(t),l="	"}return this.insert(l)},this.blockIndent=function(){var e=this.$getSelectedRows();this.session.indentRows(e.first,e.last,"	")},this.blockOutdent=function(){var e=this.session.getSelection();this.session.outdentRows(e.getRange())},this.sortLines=function(){var e=this.$getSelectedRows(),t=this.session,n=[];for(i=e.first;i<=e.last;i++)n.push(t.getLine(i));n.sort(function(e,t){return e.toLowerCase()<t.toLowerCase()?-1:e.toLowerCase()>t.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex<t){var i=n.exec(r);if(i.index<=t&&i.index+i[0].length>=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n<o?e*=Math.pow(10,s.end-n-1):e*=Math.pow(10,s.end-n),a+=e,a/=Math.pow(10,u);var f=a.toFixed(u),l=new p(t,s.start,t,s.end);this.session.replace(l,f),this.moveCursorTo(t,Math.max(s.start+1,n+f.length-s.value.length))}}},this.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},this.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange(),r=e.isBackwards();if(n.isEmpty()){var i=n.start.row;t.duplicateLines(i,i)}else{var s=r?n.start:n.end,o=t.insert(s,t.getTextRange(n),!1);n.start=s,n.end=o,e.setSelectionRange(n,r)}},this.moveLinesDown=function(){this.$moveLines(1,!1)},this.moveLinesUp=function(){this.$moveLines(-1,!1)},this.moveText=function(e,t,n){return this.session.moveText(e,t,n)},this.copyLinesUp=function(){this.$moveLines(-1,!0)},this.copyLinesDown=function(){this.$moveLines(1,!0)},this.$moveLines=function(e,t){var n,r,i=this.selection;if(!i.inMultiSelectMode||this.inVirtualSelectionMode){var s=i.toOrientedRange();n=this.$getSelectedRows(s),r=this.session.$moveLines(n.first,n.last,t?0:e),t&&e==-1&&(r=0),s.moveBy(r,0),i.fromOrientedRange(s)}else{var o=i.rangeList.ranges;i.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;var u=0,a=0,f=o.length;for(var l=0;l<f;l++){var c=l;o[l].moveBy(u,0),n=this.$getSelectedRows(o[l]);var h=n.first,p=n.last;while(++l<f){a&&o[l].moveBy(a,0);var d=this.$getSelectedRows(o[l]);if(t&&d.first!=p)break;if(!t&&d.first>p+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f<s.value.length&&!u;f++){if(!c[s.value[f]])continue;l=c[s.value[f]]+"."+s.type.replace("rparen","lparen"),isNaN(a[l])&&(a[l]=0);switch(s.value[f]){case"(":case"[":case"{":a[l]++;break;case")":case"]":case"}":a[l]--,a[l]===-1&&(o="bracket",u=!0)}}else s&&s.type.indexOf("tag-name")!==-1&&(isNaN(a[s.value])&&(a[s.value]=0),i.value==="<"?a[s.value]++:i.value==="</"&&a[s.value]--,a[s.value]===-1&&(o="tag",u=!0));u||(i=s,s=r.stepForward(),f=0)}while(s&&!u);if(!o)return;var h,d;if(o==="bracket"){h=this.session.getBracketRange(n);if(!h){h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1,r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1),d=h.start;if(t||d.row===n.row&&Math.abs(d.column-n.column)<2)h=this.session.getBracketRange(d)}}else if(o==="tag"){if(!s||s.type.indexOf("tag-name")===-1)return;var v=s.value;h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2,r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2);if(h.compare(n.row,n.column)===0){u=!1;do s=i,i=r.stepBackward(),i&&(i.type.indexOf("tag-close")!==-1&&h.setEnd(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+1),s.value===v&&s.type.indexOf("tag-name")!==-1&&(i.value==="<"?a[v]++:i.value==="</"&&a[v]--,a[v]===0&&(u=!0)));while(i&&!u)}s&&s.type.indexOf("tag-name")&&(d=h.start,d.row==n.row&&Math.abs(d.column-n.column)<2&&(d=h.end))}d=h&&h.cursor||d,d&&(e?h&&t?this.selection.setRange(h):h&&h.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(d.row,d.column):this.selection.moveTo(d.row,d.column))},this.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.$blockScrolling+=1,this.exitMultiSelectMode&&this.exitMultiSelectMode(),this.moveCursorTo(e-1,t||0),this.$blockScrolling-=1,this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},this.navigateTo=function(e,t){this.selection.moveTo(e,t)},this.navigateUp=function(e){if(this.selection.isMultiLine()&&!this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(-e||-1,0)},this.navigateDown=function(e){if(this.selection.isMultiLine()&&this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(e||1,0)},this.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),n!==null&&(this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end)),r):r},this.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;this.$blockScrolling+=1;var i=this.getSelectionRange();this.selection.moveTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.top<o.height&&s.top+t.top+o.lineHeight>window.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}).call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r<e.length;r++){var i=e[r],s={group:i.group,deltas:new Array(i.length)};for(var o=0;o<i.deltas.length;o++){var u=i.deltas[o];s.deltas[o]=t(u)}n[r]=s}return n}this.execute=function(e){var t=e.args[0];this.$doc=e.args[1],e.merge&&this.hasUndo()&&(this.dirtyCounter--,t=this.$undoStack.pop().concat(t)),this.$undoStack.push(t),this.$redoStack=[],this.dirtyCounter<0&&(this.dirtyCounter=NaN),this.dirtyCounter++},this.undo=function(e){var t=this.$undoStack.pop(),n=null;return t&&(n=this.$doc.undoChanges(t,e),this.$redoStack.push(t),this.dirtyCounter--),n},this.redo=function(e){var t=this.$redoStack.pop(),n=null;return t&&(n=this.$doc.redoChanges(this.$deserializeDeltas(t),e),this.$undoStack.push(t),this.dirtyCounter++),n},this.reset=function(){this.$undoStack=[],this.$redoStack=[],this.dirtyCounter=0},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;t<e.length;t++){var n=e[t],r=n.row,i=this.$annotations[r];i||(i=this.$annotations[r]={text:[]});var o=n.text;o=o?s.escapeHTML(o):n.html||"",i.text.indexOf(o)===-1&&i.text.push(o);var u=n.type;u=="error"?i.className=" ace_error":u=="warning"&&i.className!=" ace_error"?i.className=" ace_warning":u=="info"&&!i.className&&(i.className=" ace_info")}},this.$updateAnnotations=function(e){if(!this.$annotations.length)return;var t=e.start.row,n=e.end.row-t;if(n!==0)if(e.action=="remove")this.$annotations.splice(t,n+1,null);else{var r=new Array(n+1);r.unshift(t,1),this.$annotations.splice.apply(this.$annotations,r)}},this.update=function(e){var t=this.session,n=e.firstRow,i=Math.min(e.lastRow+e.gutterOffset,t.getLength()-1),s=t.getNextFoldLine(n),o=s?s.start.row:Infinity,u=this.$showFoldWidgets&&t.foldWidgets,a=t.$breakpoints,f=t.$decorations,l=t.$firstLineNumber,c=0,h=t.gutterRenderer||this.$renderer,p=null,d=-1,v=n;for(;;){v>o&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&v<s.end.row?m+=" ace_closed":m+=" ace_open",p.foldWidget.className!=m&&(p.foldWidget.className=m);var g=e.lineHeight+"px";p.foldWidget.style.height!=g&&(p.foldWidget.style.height=g)}else p.foldWidget&&(p.element.removeChild(p.foldWidget),p.foldWidget=null);var b=c=h?h.getText(t,v):v+l;b!=p.textNode.data&&(p.textNode.data=b),v++}this.element.style.height=e.minHeight+"px";if(this.$fixedWidth||t.$useWrapMode)c=t.getLength()+l;var w=h?h.getWidth(t,c,e):c.toString().length*e.characterWidth,E=this.$padding||this.$computePadding();w+=E.left+E.right,w!==this.gutterWidth&&!isNaN(w)&&(this.gutterWidth=w,this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._emit("changeGutterWidth",w))},this.$fixedWidth=!1,this.$showLineNumbers=!0,this.$renderer="",this.setShowLineNumbers=function(e){this.$renderer=!e&&{getWidth:function(){return""},getText:function(){return""}}},this.getShowLineNumbers=function(){return this.$showLineNumbers},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(e){e?r.addCssClass(this.element,"ace_folding-enabled"):r.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=e,this.$padding=null},this.getShowFoldWidgets=function(){return this.$showFoldWidgets},this.$computePadding=function(){if(!this.element.firstChild)return{left:0,right:0};var e=r.computedStyle(this.element.firstChild);return this.$padding={},this.$padding.left=parseInt(e.paddingLeft)+1||0,this.$padding.right=parseInt(e.paddingRight)||0,this.$padding},this.getRegion=function(e){var t=this.$padding||this.$computePadding(),n=this.element.getBoundingClientRect();if(e.x<t.left+n.left)return"markers";if(this.$showFoldWidgets&&e.x>n.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1<f?u.getScreenLastRowColumn(l+1):l==f?0:n.end.column,this.drawSingleLineMarker(t,d,i+(l==a?" ace_start":"")+" ace_br"+e(l==a||l==a+1&&n.start.column,c<h,h>p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"",e.push("<div class='",n," ace_br1 ace_start' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",a,"px;",i,"'></div>"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("<div class='",n," ace_br12' style='","height:",o,"px;","width:",f,"px;","top:",u,"px;","left:",s,"px;",i,"'></div>"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("<div class='",n,l?" ace_br"+l:"","' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",s,"px;",i,"'></div>")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("<div class='",n,"' style='","height:",o,"px;","width:",u,"px;","top:",a,"px;","left:",f,"px;",s||"","'></div>")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("<div class='",n,"' style='","height:",o,"px;","top:",s,"px;","left:0;right:0;",i||"","'></div>")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("<div class='",n,"' style='","height:",o,"px;","top:",s,"px;","left:0;right:0;",i||"","'></div>")}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n<e+1;n++)this.showInvisibles?t.push("<span class='ace_invisible ace_invisible_tab'>"+s.stringRepeat(this.TAB_CHAR,n)+"</span>"):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]="<span class='"+r+i+"'>"+u+"</span>",this.$tabStrings["	"]="<span class='"+r+o+"'>"+a+"</span>"}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;u<r;u++){var a=this.session.getFoldLine(u);if(a){if(a.containsRow(r)){r=a.start.row;break}u=a.end.row}o++}var u=r,a=this.session.getNextFoldLine(u),f=a?a.start.row:Infinity;for(;;){u>f&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow<e.firstRow)return this.update(e);if(e.lastRow<t.firstRow)return this.update(e);var n=this.element;if(t.firstRow<e.firstRow)for(var r=this.session.getFoldedRowCount(t.firstRow,e.firstRow-1);r>0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRow<t.firstRow){var i=this.$renderLinesFragment(e,e.firstRow,t.firstRow-1);n.firstChild?n.insertBefore(i,n.firstChild):n.appendChild(i)}if(e.lastRow>t.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("<div class='ace_line_group' style='height:",e.lineHeight*this.session.getRowLength(i),"px'>"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("</div>"),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?"<span class='ace_invisible ace_invisible_space'>"+s.stringRepeat(i.SPACE_CHAR,e.length)+"</span>":e;if(e=="&")return"&#38;";if(e=="<")return"&#60;";if(e==">")return"&#62;";if(e=="	"){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,"<span class='"+f+"' style='width:"+i.config.characterWidth*2+"px'>"+l+"</span>"}return r?"<span class='ace_invisible ace_invisible_space ace_invalid'>"+i.SPACE_CHAR+"</span>":(t+=1,"<span class='ace_cjk' style='width:"+i.config.characterWidth*2+"px'>"+e+"</span>")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("<span class='",f,"'",l,">",a,"</span>")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]=="	"?(e.push(s.stringRepeat(this.$tabStrings["	"],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f<t.length;f++){var l=t[f],c=l.value;if(f==0&&this.displayIndentGuides){i=c.length,c=this.renderIndentGuide(e,c,u);if(!c)continue;i-=c.length}if(i+c.length<u)a=this.$renderToken(e,a,l,c),i+=c.length;else{while(i+c.length>=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("</div>","<div class='ace_line' style='height:",this.config.lineHeight,"px'>"),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s<t.length;s++)r=t[s],i=r.value,n=this.$renderToken(e,n,r,i)},this.$renderLine=function(e,t,n,r){!r&&r!=0&&(r=this.session.getFoldLine(t));if(r)var i=this.$getFoldLineTokens(t,r);else var i=this.session.getTokens(t);n||e.push("<div class='ace_line' style='height:",this.config.lineHeight*(this.$useLineGroups()?1:this.session.getRowLength(t)),"px'>");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("<span class='ace_invisible ace_invisible_eol'>",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"</span>")),n||e.push("</div>")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value.length,i++;if(i==e.length)return}if(s!=t){var o=e[i].value.substring(t-s);o.length>n-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(s<n&&i<e.length){var o=e[i].value;o.length+s>n?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;n<i;n++){var s=this.getPixelPosition(t[n].cursor,!0);if((s.top>e.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e}}).call(u.prototype);var a=function(e,t){u.call(this,e),this.scrollTop=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(a,u),function(){this.classSuffix="-v",this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}.call(a.prototype);var f=function(e,t){u.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(f,u),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(f.prototype),t.ScrollBar=a,t.ScrollBarV=a,t.ScrollBarH=f,t.VScrollBar=a,t.HScrollBar=f}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block;   }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_br1 {border-top-left-radius    : 3px;}.ace_br2 {border-top-right-radius   : 3px;}.ace_br3 {border-top-left-radius    : 3px; border-top-right-radius:    3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius    : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius   : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius    : 3px; border-bottom-left-radius:  3px;}.ace_br10{border-top-right-radius   : 3px; border-bottom-left-radius:  3px;}.ace_br11{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-left-radius:  3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br13{border-top-left-radius    : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br14{border-top-right-radius   : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br15{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRow<t&&(this.$changedLines.lastRow=t)):this.$changedLines={firstRow:e,lastRow:t};if(this.$changedLines.lastRow<this.layerConfig.firstRow){if(!n)return;this.$changedLines.lastRow=this.layerConfig.lastRow}if(this.$changedLines.firstRow>this.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)),height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(t<n.firstRow)return;if(t===Infinity){this.$showGutter&&this.$gutterLayer.update(n),this.$textLayer.update(n);return}return this.$textLayer.updateLines(n,e,t),!0},this.$getLongestLine=function(){var e=this.session.getScreenWidth();return this.showInvisibles&&!this.session.$useWrapMode&&(e+=1),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-u<s+this.lineHeight&&(t&&a+this.$size.scrollerHeight-u<s-this.lineHeight&&(s+=t*this.$size.scrollerHeight),this.session.setScrollTop(s+this.lineHeight-this.$size.scrollerHeight));var f=this.scrollLeft;f>i?(i<this.$padding+2*this.layerConfig.characterWidth&&(i=-this.scrollMargin.left),this.session.setScrollLeft(i)):f+this.$size.scrollerWidth<i+this.characterWidth?this.session.setScrollLeft(Math.round(i+this.characterWidth-this.$size.scrollerWidth)):f<=this.$padding&&i-f<this.characterWidth&&this.session.setScrollLeft(0)},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(e){this.session.setScrollTop(e*this.lineHeight)},this.alignCursor=function(e,t){typeof e=="number"&&(e={row:e,column:0});var n=this.$cursorLayer.getPixelPosition(e),r=this.$size.scrollerHeight-this.lineHeight,i=n.top-r*(t||0);return this.session.setScrollTop(i),i},this.STEPS=8,this.$calcSteps=function(e,t){var n=0,r=this.STEPS,i=[],s=function(e,t,n){return n*(Math.pow(e-1,3)+1)+t};for(n=0;n<r;++n)i.push(s(n/this.STEPS,e,t-e));return i},this.scrollToLine=function(e,t,n,r){var i=this.$cursorLayer.getPixelPosition({row:e,column:0}),s=i.top;t&&(s-=this.$size.scrollerHeight/2);var o=this.scrollTop;this.session.setScrollTop(s),n!==!1&&this.animateScrolling(o,r)},this.animateScrolling=function(e,t){var n=this.scrollTop;if(!this.$animatedScroll)return;var r=this;if(e==n)return;if(this.$scrollAnimation){var i=this.$scrollAnimation.steps;if(i.length){e=i[0];if(e==n)return}}var s=r.$calcSteps(e,n);this.$scrollAnimation={from:e,to:n,steps:s},clearInterval(this.$timer),r.session.setScrollTop(s.shift()),r.session.$scrollTop=n,this.$timer=setInterval(function(){s.length?(r.session.setScrollTop(s.shift()),r.session.$scrollTop=n):n!=null?(r.session.$scrollTop=-1,r.session.setScrollTop(n),n=null):(r.$timer=clearInterval(r.$timer),r.$scrollAnimation=null,t&&t())},10)},this.scrollToY=function(e){this.scrollTop!==e&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=e)},this.scrollToX=function(e){this.scrollLeft!==e&&(this.scrollLeft=e),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollTo=function(e,t){this.session.setScrollTop(t),this.session.setScrollLeft(t)},this.scrollBy=function(e,t){t&&this.session.setScrollTop(this.session.getScrollTop()+t),e&&this.session.setScrollLeft(this.session.getScrollLeft()+e)},this.isScrollableBy=function(e,t){if(t<0&&this.session.getScrollTop()>=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r.cssClass)return;i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e<this.others.length;e++)this.session.removeMarker(this.others[e].markerId)},this.onUpdate=function(e){if(this.$updating)return this.updateAnchors(e);var t=e;if(t.start.row!==t.end.row)return;if(t.start.row!==this.pos.row)return;this.$updating=!0;var n=e.action==="insert"?t.end.column-t.start.column:t.start.column-t.end.column,i=t.start.column>=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n<t;n++)e.undo(!0);this.selectionBefore&&this.session.selection.fromJSON(this.selectionBefore)}}).call(o.prototype),t.PlaceHolder=o}),define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){function s(e,t){return e.row==t.row&&e.column==t.column}function o(e){var t=e.domEvent,n=t.altKey,o=t.shiftKey,u=t.ctrlKey,a=e.getAccelKey(),f=e.getButton();u&&i.isMac&&(f=t.button);if(e.editor.inMultiSelectMode&&f==2){e.editor.textInput.onContextMenu(e.domEvent);return}if(!u&&!n&&!a){f===0&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode();return}if(f!==0)return;var l=e.editor,c=l.selection,h=l.inMultiSelectMode,p=e.getDocumentPosition(),d=c.getCursor(),v=e.inSelection()||c.isEmpty()&&s(p,d),m=e.x,g=e.y,y=function(e){m=e.clientX,g=e.clientY},b=l.session,w=l.renderer.pixelToScreenCoordinates(m,g),E=w,S;if(l.$mouseHandler.$enableJumpToDef)u&&n||a&&n?S=o?"block":"add":n&&l.$blockSelectEnabled&&(S="block");else if(a&&!n){S="add";if(!h&&o)return}else n&&l.$blockSelectEnabled&&(S="block");S&&i.isMac&&t.ctrlKey&&l.$mouseHandler.cancelContextMenu();if(S=="add"){if(!h&&v)return;if(!h){var x=c.toOrientedRange();l.addSelectionMarker(x)}var T=c.rangeList.rangeAtPoint(p);l.$blockScrolling++,l.inVirtualSelectionMode=!0,o&&(T=null,x=c.ranges[0]||x,l.removeSelectionMarker(x)),l.once("mouseup",function(){var e=c.toOrientedRange();T&&e.isEmpty()&&s(T.cursor,e.cursor)?c.substractPoint(e.cursor):(o?c.substractPoint(x.cursor):x&&(l.removeSelectionMarker(x),c.addRange(x)),c.addRange(e)),l.$blockScrolling--,l.inVirtualSelectionMode=!1})}else if(S=="block"){e.stop(),l.inVirtualSelectionMode=!0;var N,C=[],k=function(){var e=l.renderer.pixelToScreenCoordinates(m,g),t=b.screenToDocumentPosition(e.row,e.column);if(s(E,e)&&s(t,c.lead))return;E=e,l.$blockScrolling++,l.selection.moveToPosition(t),l.renderer.scrollCursorIntoView(),l.removeSelectionMarkers(C),C=c.rectangularRangeBlock(E,w),l.$mouseHandler.$clickSelection&&C.length==1&&C[0].isEmpty()&&(C[0]=l.$mouseHandler.$clickSelection.clone()),C.forEach(l.addSelectionMarker,l),l.updateSelectionMarkers(),l.$blockScrolling--};l.$blockScrolling++,h&&!a?c.toSingleRange():!h&&a&&(N=c.toOrientedRange(),l.addSelectionMarker(N)),o?w=b.documentToScreenPosition(c.lead):c.moveToPosition(p),l.$blockScrolling--,E={row:-1,column:-1};var L=function(e){clearInterval(O),l.removeSelectionMarkers(C),C.length||(C=[c.toOrientedRange()]),l.$blockScrolling++,N&&(l.removeSelectionMarker(N),c.toSingleRange(N));for(var t=0;t<C.length;t++)c.addRange(C[t]);l.inVirtualSelectionMode=!1,l.$mouseHandler.$clickSelection=null,l.$blockScrolling--},A=k;r.capture(l.container,y,L);var O=setInterval(function(){A()},20);return e.preventDefault()}}var r=e("../lib/event"),i=e("../lib/useragent");t.onMouseDown=o}),define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"],function(e,t,n){t.defaultCommands=[{name:"addCursorAbove",exec:function(e){e.selectMoreLines(-1)},bindKey:{win:"Ctrl-Alt-Up",mac:"Ctrl-Alt-Up"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorBelow",exec:function(e){e.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorAboveSkipCurrent",exec:function(e){e.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorBelowSkipCurrent",exec:function(e){e.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreBefore",exec:function(e){e.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreAfter",exec:function(e){e.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextBefore",exec:function(e){e.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextAfter",exec:function(e){e.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"splitIntoLines",exec:function(e){e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"alignCursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()}),u.addListener(t,"keyup",r),u.addListener(t,"blur",r)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c<o;c++)f.push(this.getLineRange(c,!0));l=this.getLineRange(o,!0),l.end.column=n.end.column,f.push(l),f.forEach(this.addRange,this)}},this.toggleBlockSelection=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column<t.column;if(s)var o=e.column,u=t.column;else var o=t.column,u=e.column;var a=e.row<t.row;if(a)var f=e.row,l=t.row;else var f=t.row,l=e.row;o<0&&(o=0),f<0&&(f=0),f==l&&(n=!0);for(var c=f;c<=l;c++){var h=i.fromPoints(this.session.screenToDocumentPosition(c,o),this.session.screenToDocumentPosition(c,u));if(h.isEmpty()){if(p&&v(h.end,p))break;var p=h.end}h.cursor=s?h.start:h.end,r.push(h)}a&&r.reverse();if(!n){var d=r.length-1;while(r[d].isEmpty()&&d>0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r<t.length;r++)n.push(this.session.getTextRange(t[r]));var i=this.session.getDocument().getNewLineCharacter();e=n.join(i),e.length==(n.length-1)*i.length&&(e="")}else this.selection.isEmpty()||(e=this.session.getTextRange(this.getSelectionRange()));return e},this.$checkMultiselectChange=function(e,t){if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var n=this.multiSelect.ranges[0];if(this.multiSelect.isEmpty()&&t==this.multiSelect.anchor)return;var r=t==this.multiSelect.anchor?n.cursor==n.start?n.end:n.start:n.cursor;(r.row!=t.row||this.session.$clipPositionToDocument(r.row,r.column).column!=t.column)&&this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange())}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle;if(t.needle==undefined){var r=this.selection.isEmpty()?this.selection.getWordRange():this.selection.getRange();t.needle=this.session.getTextRange(r)}this.$search.set(t);var i=this.$search.findAll(this.session);if(!i.length)return 0;this.$blockScrolling+=1;var s=this.multiSelect;n||s.toSingleRange(i[0]);for(var o=i.length;o--;)s.addRange(i[o],!0);return r&&s.rangeList.rangeAtPoint(r.start)&&s.addRange(r,!0),this.$blockScrolling-=1,i.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}},this.selectMore=function(e,t,n){var r=this.session,i=r.multiSelect,s=i.toOrientedRange();if(s.isEmpty()){s=r.getWordRange(s.start.row,s.start.column),s.cursor=e==-1?s.start:s.end,this.multiSelect.addRange(s);if(n)return}var o=r.getTextRange(s),u=h(r,o,e);u&&(u.cursor=e==-1?u.start:u.end,this.$blockScrolling+=1,this.session.unfold(u),this.multiSelect.addRange(u),this.$blockScrolling-=1,this.renderer.scrollCursorIntoView(null,.5)),t&&this.multiSelect.substractPoint(s.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges,r=-1,s=n.filter(function(e){if(e.cursor.row==r)return!0;r=e.cursor.row});if(!n.length||s.length==n.length-1){var o=this.selection.getRange(),u=o.start.row,f=o.end.row,l=u==f;if(l){var c=this.session.getLength(),h;do h=this.session.getLine(f);while(/[=:]/.test(h)&&++f<c);do h=this.session.getLine(u);while(/[=:]/.test(h)&&--u>0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),i<v&&(v=i),i});n.forEach(function(t,n){var r=t.cursor,s=d-r.column,o=m[n]-v;s>o?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),s<t[2].length&&(s=t[2].length),o>t[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++t<a){var c=e.getLine(t).search(i);if(c==-1)continue;if(c<=o)break;l=t}if(l>f){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u<s;u++)n[u]&&(n[u].hidden=o);n[s]&&(o?n[i]?n[s].hidden=o:n[i]=n[s]:(n[i]==n[s]&&(n[i]=undefined),n[s].hidden=o))},this.updateOnChange=function(e){var t=this.session.lineWidgets;if(!t)return;var n=e.start.row,r=e.end.row-n;if(r!==0)if(e.action=="remove"){var i=t.splice(n+1,r);i.forEach(function(e){e&&this.removeLineWidget(e)},this),this.$updateRows()}else{var s=new Array(r);s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},this.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},this.addLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e.session=this.session;var n=this.editor.renderer;e.html&&!e.el&&(e.el=i.createElement("div"),e.el.innerHTML=e.html),e.el&&(i.addCssClass(e.el,"ace_lineWidgetContainer"),e.el.style.position="absolute",e.el.style.zIndex=5,n.container.appendChild(e.el),e._inDocument=!0),e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight||(e.pixelHeight=e.el.offsetHeight),e.rowCount==null&&(e.rowCount=e.pixelHeight/n.layerConfig.lineHeight);var r=this.session.getFoldAt(e.row,0);e.$fold=r;if(r){var s=this.session.lineWidgets;e.row==r.end.row&&!s[r.start.row]?s[r.start.row]=e:e.hidden=!0}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,n),this.onWidgetChanged(e),e},this.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},this.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s<n.length;s++){var o=n[s];if(!o||!o.el)continue;if(o.session!=this.session)continue;if(!o._inDocument){if(this.session.lineWidgets[o.row]!=o)continue;o._inDocument=!0,t.container.appendChild(o.el)}o.h=o.el.offsetHeight,o.fixedWidth||(o.w=o.el.offsetWidth,o.screenWidth=Math.ceil(o.w/r.characterWidth));var u=o.h/r.lineHeight;o.coverLine&&(u-=this.session.getRowLineCount(o.row),u<0&&(u=0)),o.rowCount!=u&&(o.rowCount=u,o.row<i&&(i=o.row))}i!=Infinity&&(this.session._emit("changeFold",{data:{start:{row:i}}}),this.session.lineWidgetWidth=null),this.session._changedWidgets=[]},this.renderWidgets=function(e,t){var n=t.layerConfig,r=this.session.lineWidgets;if(!r)return;var i=Math.min(this.firstRow,n.firstRow),s=Math.max(this.lastRow,n.lastRow,r.length);while(i>0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("<br>"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString("    .error_widget_wrapper {        background: inherit;        color: inherit;        border:none    }    .error_widget {        border-top: solid 2px;        border-bottom: solid 2px;        margin: 5px 0;        padding: 10px 40px;        white-space: pre-wrap;    }    .error_widget.ace_error, .error_widget_arrow.ace_error{        border-color: #ff5a5a    }    .error_widget.ace_warning, .error_widget_arrow.ace_warning{        border-color: #F1D817    }    .error_widget.ace_info, .error_widget_arrow.ace_info{        border-color: #5a5a5a    }    .error_widget.ace_ok, .error_widget_arrow.ace_ok{        border-color: #5aaa5a    }    .error_widget_arrow {        position: absolute;        border: solid 5px;        border-top-color: transparent!important;        border-right-color: transparent!important;        border-left-color: transparent!important;        top: -5px;    }","")}),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.3"});
+            (function() {
+                window.require(["ace/ace"], function(a) {
+                    a && a.config.init(true);
+                    if (!window.ace)
+                        window.ace = a;
+                    for (var key in a) if (a.hasOwnProperty(key))
+                        window.ace[key] = a[key];
+                });
+            })();
+        
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/mode-javascript.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/mode-javascript.js
new file mode 100644
index 0000000..d2b278f
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/mode-javascript.js
@@ -0,0 +1 @@
+define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"</?"+e+"",next:"jsxAttributes",nextState:"jsx"};this.$rules.start.unshift(t);var n={regex:"{",token:"paren.quasi.start",push:"start"};this.$rules.jsx=[n,t,{include:"reference"},{defaultToken:"string"}],this.$rules.jsxAttributes=[{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++t<a){n=e.getLine(t);var f=n.search(/\S/);if(f===-1)continue;if(r>f)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++n<s){t=e.getLine(n);var f=u.exec(t);if(!f)continue;f[1]?a--:a++;if(!a)break}var l=n;if(l>o)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c})
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ui-ace.min.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ui-ace.min.js
new file mode 100644
index 0000000..b914093
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/lib/ace/ui-ace.min.js
@@ -0,0 +1,7 @@
+/**
+ * angular-ui-ace - This directive allows you to add ACE editor elements.
+ * @version v0.2.3 - 2015-01-29
+ * @link http://angular-ui.github.com
+ * @license MIT
+ */
+"use strict";angular.module("ui.ace",[]).constant("uiAceConfig",{}).directive("uiAce",["uiAceConfig",function(a){if(angular.isUndefined(window.ace))throw new Error("ui-ace need ace to work... (o rly?)");var b=function(a,b,c){if(angular.isDefined(c.workerPath)){var d=window.ace.require("ace/config");d.set("workerPath",c.workerPath)}angular.isDefined(c.require)&&c.require.forEach(function(a){window.ace.require(a)}),angular.isDefined(c.showGutter)&&a.renderer.setShowGutter(c.showGutter),angular.isDefined(c.useWrapMode)&&b.setUseWrapMode(c.useWrapMode),angular.isDefined(c.showInvisibles)&&a.renderer.setShowInvisibles(c.showInvisibles),angular.isDefined(c.showIndentGuides)&&a.renderer.setDisplayIndentGuides(c.showIndentGuides),angular.isDefined(c.useSoftTabs)&&b.setUseSoftTabs(c.useSoftTabs),angular.isDefined(c.showPrintMargin)&&a.setShowPrintMargin(c.showPrintMargin),angular.isDefined(c.disableSearch)&&c.disableSearch&&a.commands.addCommands([{name:"unfind",bindKey:{win:"Ctrl-F",mac:"Command-F"},exec:function(){return!1},readOnly:!0}]),angular.isString(c.theme)&&a.setTheme("ace/theme/"+c.theme),angular.isString(c.mode)&&b.setMode("ace/mode/"+c.mode),angular.isDefined(c.firstLineNumber)&&(angular.isNumber(c.firstLineNumber)?b.setOption("firstLineNumber",c.firstLineNumber):angular.isFunction(c.firstLineNumber)&&b.setOption("firstLineNumber",c.firstLineNumber()));var e,f;if(angular.isDefined(c.advanced))for(e in c.advanced)f={name:e,value:c.advanced[e]},a.setOption(f.name,f.value);if(angular.isDefined(c.rendererOptions))for(e in c.rendererOptions)f={name:e,value:c.rendererOptions[e]},a.renderer.setOption(f.name,f.value);angular.forEach(c.callbacks,function(b){angular.isFunction(b)&&b(a)})};return{restrict:"EA",require:"?ngModel",link:function(c,d,e,f){var g,h,i=a.ace||{},j=angular.extend({},i,c.$eval(e.uiAce)),k=window.ace.edit(d[0]),l=k.getSession(),m=function(){var a=arguments[0],b=Array.prototype.slice.call(arguments,1);angular.isDefined(a)&&c.$evalAsync(function(){if(!angular.isFunction(a))throw new Error("ui-ace use a function as callback.");a(b)})},n={onChange:function(a){return function(b){var d=l.getValue();!f||d===f.$viewValue||c.$$phase||c.$root.$$phase||c.$evalAsync(function(){f.$setViewValue(d)}),m(a,b,k)}},onBlur:function(a){return function(){m(a,k)}}};e.$observe("readonly",function(a){k.setReadOnly(!!a||""===a)}),f&&(f.$formatters.push(function(a){if(angular.isUndefined(a)||null===a)return"";if(angular.isObject(a)||angular.isArray(a))throw new Error("ui-ace cannot use an object or an array as a model");return a}),f.$render=function(){l.setValue(f.$viewValue)});var o=function(a,d){a!==d&&(j=angular.extend({},i,c.$eval(e.uiAce)),j.callbacks=[j.onLoad],j.onLoad!==i.onLoad&&j.callbacks.unshift(i.onLoad),l.removeListener("change",g),g=n.onChange(j.onChange),l.on("change",g),k.removeListener("blur",h),h=n.onBlur(j.onBlur),k.on("blur",h),b(k,l,j))};c.$watch(e.uiAce,o,!0),o(i),d.on("$destroy",function(){k.session.$stopWorker(),k.destroy()}),c.$watch(function(){return[d[0].offsetWidth,d[0].offsetHeight]},function(){k.resize(),k.renderer.updateFull()},!0)}}}]);
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 01472c4..a5c229a 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -970,8 +970,19 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         return false;
     }
 
+    function configureAuthorizationServices() {
+        if ($scope.client.authorizationServicesEnabled) {
+            if ($scope.accessType == 'public') {
+                $scope.accessType = 'confidential';
+            }
+            $scope.client.publicClient = false;
+            $scope.client.serviceAccountsEnabled = true;
+        }
+    }
+
     $scope.$watch('client', function() {
         $scope.changed = isChanged();
+        configureAuthorizationServices();
     }, true);
 
     $scope.$watch('newRedirectUri', function() {
@@ -1067,9 +1078,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
                 realm : realm.realm,
                 client : client.id
             }, $scope.client, function() {
-                $scope.changed = false;
-                client = angular.copy($scope.client);
-                $location.url("/realms/" + realm.realm + "/clients/" + client.id);
+                $route.reload();
                 Notifications.success("Your changes have been saved to the client.");
             });
         }
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 54c8000..65f45b6 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -44,6 +44,10 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
             return getAccess('view-identity-providers') || getAccess('manage-identity-providers') || this.manageIdentityProviders;
         },
 
+        get viewAuthorization() {
+            return getAccess('view-authorization') || this.manageAuthorization;
+        },
+
         get manageRealm() {
             return getAccess('manage-realm');
         },
@@ -64,6 +68,10 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
             return getAccess('manage-identity-providers');
         },
 
+        get manageAuthorization() {
+            return getAccess('manage-authorization');
+        },
+
         get impersonation() {
             return getAccess('impersonation');
         }
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index f09eb63..8fb01cf 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -246,9 +246,9 @@ module.controller('UserListCtrl', function($scope, realm, User, UserImpersonatio
 
     $scope.query = {
         realm: realm.realm,
-        max : 5,
+        max : 20,
         first : 0
-    }
+    };
 
     $scope.impersonate = function(userId) {
         UserImpersonation.save({realm : realm.realm, user: userId}, function (data) {
@@ -365,7 +365,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
         console.log('realm brute force? ' + realm.bruteForceProtected)
         $scope.temporarilyDisabled = false;
         var isDisabled = function () {
-            BruteForceUser.get({realm: realm.realm, username: user.username}, function(data) {
+            BruteForceUser.get({realm: realm.realm, userId: user.id}, function(data) {
                 console.log('here in isDisabled ' + data.disabled);
                 $scope.temporarilyDisabled = data.disabled;
             });
@@ -375,7 +375,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
         isDisabled();
 
         $scope.unlockUser = function() {
-            BruteForceUser.delete({realm: realm.realm, username: user.username}, function(data) {
+            BruteForceUser.delete({realm: realm.realm, userId: user.id}, function(data) {
                 isDisabled();
             });
         }
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js
index a19b738..11e9d59 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -227,15 +227,15 @@ module.factory('RealmAdminEvents', function($resource) {
 });
 
 module.factory('BruteForce', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/usernames', {
+    return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/users', {
         realm : '@realm'
     });
 });
 
 module.factory('BruteForceUser', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/usernames/:username', {
+    return $resource(authUrl + '/admin/realms/:realm/attack-detection/brute-force/users/:userId', {
         realm : '@realm',
-        username : '@username'
+        userId : '@userId'
     });
 });
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
new file mode 100644
index 0000000..9b05faf
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
@@ -0,0 +1,101 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">Permissions</a></li>
+        <li data-ng-show="create">Add Resource Permission</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Resource Permission</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this permission.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this permission.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.default">Apply to Resource Type</label>
+                <div class="col-md-6">
+                    <input ng-model="policy.config.default" id="policy.config.default" onoffswitch data-ng-click="applyToResourceType()"/>
+                </div>
+                <kc-tooltip>Specifies if this permission would be applied to all resources with a given type. In this case, this permission will be evaluated for all instances
+                of a given resource type.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" data-ng-hide="policy.config.default">
+                <label class="col-md-2 control-label" for="reqActions">Resources <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2="{ minimumInputLength: 1}" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="Select a resource..." multiple data-ng-required="!policy.config.default">
+                        <option ng-repeat="resource in resources" value="{{resource._id}}" ng-selected="true">{{resource.name}}</option>
+                    </select>
+                </div>
+                <kc-tooltip>Specifies that this permission must be applied to a specific resource instance.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" data-ng-show="policy.config.default">
+                <label class="col-md-2 control-label" for="policy.config.defaultResourceType">Resource Type <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <input class="form-control" type="text" id="policy.config.defaultResourceType" name="policy.config.defaultResourceType" data-ng-model="policy.config.defaultResourceType" data-ng-required="policy.config.default">
+                </div>
+
+                <kc-tooltip>Specifies that this permission must be applied to all resources instances of a given type.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="reqActions">Apply Policy <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2="{ minimumInputLength: 1}" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="Select a policy..." multiple required>
+                        <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>Specifies all the policies that must be applied to the resource type or instances defined by this permission.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.decisionStrategy">Decision Strategy</label>
+
+                <div class="col-md-6">
+                    <select class="form-control" id="policy.decisionStrategy"
+                            data-ng-model="policy.decisionStrategy"
+                            ng-change="selectDecisionStrategy()">
+                        <option ng-repeat="strategy in decisionStrategies" value="{{strategy}}">{{strategy | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The decision strategy dictates how the policies associated with a given permission are evaluated and how a final decision is obtained.
+                    'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative.</kc-tooltip>
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
new file mode 100644
index 0000000..36ae53c
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
@@ -0,0 +1,113 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">Permissions</a></li>
+        <li data-ng-show="create">Add Scope Permission</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Scope Permission</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this permission.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this permission.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="reqActions">Resource</label>
+
+                <div class="col-md-6">
+                    <select class="form-control" id="reqActions"
+                            ng-model="policy.config.resources"
+                            ng-change="resolveScopes(policy)"
+                            data-ng-options="resource._id as resource.name for resource in resources">
+                        <option value="">Any resource...</option>
+                    </select>
+                </div>
+                <kc-tooltip>Restrict the scopes to those associated with the selected resource. If not selected all scopes would be available.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" data-ng-show="policy.config.resources">
+                <label class="col-md-2 control-label" for="reqActions">Scopes <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2 id="reqActions"
+                            data-ng-model="policy.config.scopes"
+                            data-placeholder="Any scope..." multiple
+                            data-ng-required="policy.config.resources != ''"
+                            data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
+                </div>
+
+                <kc-tooltip>Specifies that this permission must be applied to one or more scopes.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" data-ng-show="!policy.config.resources">
+                <label class="col-md-2 control-label" for="reqActions">Scopes <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2="{ minimumInputLength: 1}" id="reqActions"
+                            data-ng-model="policy.config.scopes"
+                            data-placeholder="Any scope..." multiple
+                            data-ng-required="policy.config.resources == ''"
+                            data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
+                    </select>
+                </div>
+                <kc-tooltip>Specifies that this permission must be applied to one or more scopes.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="reqActions">Apply Policy <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="Select a policy..." multiple required>
+                        <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>Specifies all the policies that must be applied to the scopes defined by this permission.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.decisionStrategy">Decision Strategy</label>
+
+                <div class="col-md-6">
+                    <select class="form-control" id="policy.decisionStrategy"
+                            data-ng-model="policy.decisionStrategy"
+                            ng-change="selectDecisionStrategy()">
+                        <option ng-repeat="strategy in decisionStrategies" value="{{strategy}}">{{strategy}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The decision strategy dictates how the policies associated with a given permission are evaluated and how a final decision is obtained.
+                    'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative.</kc-tooltip>
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
new file mode 100644
index 0000000..35d471d
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
@@ -0,0 +1,53 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <table class="table table-striped table-bordered">
+        <caption class="hidden">Table of identity providers</caption>
+        <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            Filter by:&nbsp;&nbsp;
+                            <div class="input-group">
+                                <input type="text" placeholder="Name" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                            </div>
+                            <div class="input-group">
+                                <select class="form-control search" data-ng-model="search.type"
+                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type">
+                                    <option value="" selected ng-click="search.type = ''">All types</option>
+                                </select>
+                            </div>
+                        </div>
+                        <div class="pull-right">
+                            <select class="form-control" ng-model="policyType"
+                                    ng-options="p.name for p in policyProviders track by p.type"
+                                    data-ng-change="addPolicy(policyType);">
+                                <option value="" disabled selected>Create permission...</option>
+                            </select>
+                        </div>
+                    </div>
+                </th>
+            </tr>
+            <tr data-ng-hide="policies.length == 0">
+                <th>Permission Name</th>
+                <th>Description</th>
+                <th>Type</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
+                <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
+                <td>{{policy.description}}</td>
+                <td>{{policy.type}}</td>
+            </tr>
+            <tr data-ng-show="(policies | filter:search).length == 0">
+                <td class="text-muted" colspan="3" data-ng-show="search.name">No results</td>
+                <td class="text-muted" colspan="3" data-ng-hide="search.name">No permissions available</td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
new file mode 100644
index 0000000..2cb27e3
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
@@ -0,0 +1,89 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add Aggregate Policy</li>
+        <li data-ng-hide="create">Aggregated</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Aggregate Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="reqActions">Apply Policy <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="Select a policy..." multiple required>
+                        <option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>Specifies all the policies that must be applied to the scopes defined by this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.decisionStrategy">Decision Strategy</label>
+
+                <div class="col-md-6">
+                    <select class="form-control" id="policy.decisionStrategy"
+                            data-ng-model="policy.decisionStrategy"
+                            ng-change="selectDecisionStrategy()">
+                        <option ng-repeat="strategy in decisionStrategies" value="{{strategy}}">{{strategy}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The decision strategy dictates how the policies associated with a given policy are evaluated and how a final decision is obtained.
+                    'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive.
+                    'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-drools-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-drools-detail.html
new file mode 100644
index 0000000..debd0ca
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-drools-detail.html
@@ -0,0 +1,128 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add Drools Policy</li>
+        <li data-ng-hide="create">Drools</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Drools Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.mavenArtifactGroupId">Policy Maven Artifact <span class="required" data-ng-show="create">*</span></label>
+                <button data-ng-click="resolveModules()" class="btn btn-primary">Resolve</button>
+                <div class="col-sm-3">
+                    <input class="form-control" type="text" id="policy.config.mavenArtifactGroupId" name="policy.config.mavenArtifactGroupId" data-ng-model="policy.config.mavenArtifactGroupId" placeholder="Group Identifier" required>
+                </div>
+                <kc-tooltip>A Maven GAV pointing to an artifact from where the rules would be loaded from. Once you have provided the GAV, you can click *Resolve* to load both *Module* and *Session* fields.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.mavenArtifactId"></label>
+                <div class="col-sm-3">
+                    <input class="form-control" type="text" id="policy.config.mavenArtifactId" name="policy.config.mavenArtifactId" data-ng-model="policy.config.mavenArtifactId" autofocus placeholder="Artifact Identifier" required>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.mavenArtifactVersion"></label>
+                <div class="col-sm-3">
+                    <input class="form-control" type="text" id="policy.config.mavenArtifactVersion" name="policy.config.mavenArtifactVersion" data-ng-model="policy.config.mavenArtifactVersion" autofocus placeholder="Version" required>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.moduleName">Module <span class="required" data-ng-show="create">*</span></label>
+                <div class="col-sm-3">
+                    <div>
+                        <select class="form-control" id="policy.config.moduleName"
+                                ng-model="policy.config.moduleName"
+                                ng-options="moduleName as moduleName for moduleName in drools.moduleNames"
+                                ng-change="resolveSessions()"
+                                ng-disabled="!drools.moduleNames.length"
+                                required>
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>The module used by this policy. You must provide a module in order to select a specific session from where rules will be loaded from.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.sessionName">Session <span class="required" data-ng-show="create">*</span></label>
+                <div class="col-sm-3">
+                    <div>
+                        <select class="form-control" id="policy.config.sessionName"
+                                ng-model="policy.config.sessionName"
+                                ng-options="sessionName as sessionName for sessionName in drools.moduleSessions"
+                                ng-disabled="!drools.moduleSessions.length"
+                                required>
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>The session used by this policy. The session provides all the rules to evaluate when processing the policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.scannerPeriod">Update Period</label>
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" required min="1" max="31536000" data-ng-model="policy.config.scannerPeriod" id="policy.config.scannerPeriod"
+                           name="policy.config.scannerPeriod"
+                           ng-disabled="!policy.config.sessionName"/>
+                    <select class="form-control" name="policy.config.scannerPeriodUnit"
+                            data-ng-model="policy.config.scannerPeriodUnit"
+                            ng-disabled="!policy.config.sessionName">
+                        <option>Seconds</option>
+                        <option>Minutes</option>
+                        <option>Hours</option>
+                        <option>Days</option>
+                    </select>
+                </div>
+                <kc-tooltip>Specifies an interval for scanning for artifact updates.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html
new file mode 100644
index 0000000..1153ded
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html
@@ -0,0 +1,71 @@
+<style>
+    .ace_editor { height: 200px; }
+</style>
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add JS Policy</li>
+        <li data-ng-hide="create">JavaScript</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add JS Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Code </label>
+                <div class="col-sm-6">
+                    <div ui-ace="{ onLoad : initEditor }" data-ng-model="policy.config.code"></div>
+                </div>
+                <kc-tooltip>The JavaScript code providing the conditions for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-role-detail.html
new file mode 100644
index 0000000..a716730
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-role-detail.html
@@ -0,0 +1,115 @@
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add Role Policy</li>
+        <li data-ng-hide="create">Role</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Role Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="roles">Roles <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2="{ minimumInputLength: 1}" id="roles" data-ng-model="selectedRole" data-ng-change="selectRole(selectedRole);" data-placeholder="Select a role..."
+                            ng-options="role as role.name for role in roles" data-ng-required="selectedUsers.length == 0 && selectedRoles.length == 0">
+                    </select>
+                </div>
+
+                <kc-tooltip>Specifies which role(s) are allowed by this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" style="margin-top: -15px;">
+                <label class="col-md-2 control-label"></label>
+                <div class="col-sm-3">
+                    <table class="table table-striped table-bordered">
+                        <thead>
+                            <tr data-ng-hide="!selectedRoles.length">
+                                <th>Role name</th>
+                                <th>Actions</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr ng-repeat="role in selectedRoles | orderBy:'name'">
+                                <td>{{role.name}}</td>
+                                <td class="kc-action-cell">
+                                    <button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(selectedRoles, $index);">Remove</button>
+                                </td>
+                            </tr>
+                            <tr data-ng-show="!selectedRoles.length">
+                                <td class="text-muted" colspan="3">No roles assigned.</td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html
new file mode 100644
index 0000000..0247687
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html
@@ -0,0 +1,81 @@
+<style>
+    .ace_editor { height: 200px; }
+</style>
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add Time Policy</li>
+        <li data-ng-hide="create">Time</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+
+    <h1 data-ng-show="create">Add Time Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.nbf">Not Before</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" style="width: 150px" type="text" id="policy.config.nbf" name="notBefore" data-ng-model="policy.config.nbf" placeholder="yyyy-MM-dd hh:mm:ss">
+                </div>
+                <kc-tooltip>Defines the time before which the policy MUST NOT be granted. Only granted if current date/time is after or equal to this value.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">Not On or After</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" style="width: 150px" type="text" id="policy.config.noa" name="policy.config.noa" data-ng-model="policy.config.noa" placeholder="yyyy-MM-dd hh:mm:ss">
+                </div>
+                <kc-tooltip>Defines the time after which the policy MUST NOT be granted. Only granted if current date/time is before or equal to this value.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-user-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-user-detail.html
new file mode 100644
index 0000000..0678aab
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-user-detail.html
@@ -0,0 +1,96 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li data-ng-show="create">Add User Policy</li>
+        <li data-ng-hide="create">User</li>
+        <li data-ng-hide="create">{{policy.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add User Policy</h1>
+    <h1 data-ng-hide="create">{{policy.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required>
+                </div>
+                <kc-tooltip>The name of this policy.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="description">Description </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
+                </div>
+                <kc-tooltip>A description for this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="users">Users <span class="required">*</span></label>
+
+                <div class="col-md-6">
+                    <select ui-select2="{ minimumInputLength: 1}" id="users" data-ng-model="selectedUser" data-ng-change="selectUser(selectedUser);" data-placeholder="Select an user..."
+                            ng-options="user as user.username for user in users" data-ng-required="selectedRoles.length == 0">
+                    </select>
+                </div>
+                <kc-tooltip>Specifies which user(s) are allowed by this policy.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" style="margin-top: -15px;">
+                <label class="col-md-2 control-label"></label>
+                <div class="col-sm-3">
+                    <table class="table table-striped table-bordered">
+                        <thead>
+                            <tr data-ng-hide="!selectedUsers.length">
+                                <th>Username</th>
+                                <th>Actions</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr ng-repeat="user in selectedUsers | orderBy:'username'">
+                                <td>{{user.username}}</td>
+                                <td class="kc-action-cell">
+                                    <button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(selectedUsers, $index);">Remove</button>
+                                </td>
+                            </tr>
+                            <tr data-ng-show="!selectedUsers.length">
+                                <td class="text-muted" colspan="3">No users assigned.</td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="policy.logic">Logic</label>
+
+                <div class="col-sm-1">
+                    <select class="form-control" id="policy.logic"
+                            data-ng-model="policy.logic">
+                        <option ng-repeat="logic in logics" value="{{logic}}">{{logic | toCamelCase}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will
+                    be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa.
+            </div>
+            <input type="hidden" data-ng-model="policy.type"/>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html
new file mode 100644
index 0000000..ebd976b
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate.html
@@ -0,0 +1,283 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/evaluate">Policy Evaluation</a></li>
+    </ol>
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <div data-ng-show="showResult">
+        <br>
+        <a href="" data-ng-click="showRequestTab()">New Evaluation</a>
+    </div>
+
+    <div data-ng-show="evaluationResult && !showResult">
+        <br>
+        <a href="" data-ng-click="showResultTab()">Previous Result</a>
+    </div>
+
+    <div data-ng-hide="showResult">
+        <form class="form-horizontal" name="clientForm" novalidate>
+            <fieldset>
+                <fieldset class="border-top">
+                    <legend><span class="text">Identity Information</span>
+                        <kc-tooltip>The available options to configure the identity information that will be used when evaluating policies.</kc-tooltip>
+                    </legend>
+                    <div class="form-group">
+                        <label class="col-md-2 control-label" for="client">Client</label>
+
+                        <div class="col-sm-2">
+                            <div>
+                                <select class="form-control" id="client"
+                                        ng-model="authzRequest.clientId"
+                                        ng-options="client.id as client.clientId for client in clients track by client.id">
+                                    <option value="">Select a client...</option>
+                                </select>
+                                </select>
+                            </div>
+                        </div>
+                        <kc-tooltip>A resource server is an already existing client application. In this case, the
+                            client application will also act as a resource server in order to have its resources managed
+                            and protected.
+                        </kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix">
+                        <label class="col-md-2 control-label" for="users">User <span class="required"
+                                                                                     data-ng-show="!authzRequest.roleIds || authzRequest.roleIds.length == 0">*</span></label>
+
+                        <div class="col-md-6">
+                            <select ui-select2="{ minimumInputLength: 1, allowClear:true}" id="users"
+                                    data-ng-model="authzRequest.userId" data-placeholder="Select an user..."
+                                    ng-options="user.id as user.username for user in users track by user.id"
+                                    data-ng-required="!authzRequest.roleIds || authzRequest.roleIds.length == 0">
+                                <option value=""></option>
+                            </select>
+                        </div>
+
+                        <kc-tooltip>Specifies which user(s) are allowed by this policy.</kc-tooltip>
+                    </div>
+
+                    <div class="form-group">
+                        <div class="col-md-10 col-md-offset-2">
+                            <button class="btn btn-primary" data-ng-click="entitlements()" data-ng-disabled="authzRequest.userId == null || authzRequest.clientId == null">Entitlements</button>
+                        </div>
+                    </div>
+
+                    <div class="form-group clearfix">
+                        <label class="col-md-2 control-label" for="reqActions">Roles <span class="required"
+                                                                                           data-ng-show="!authzRequest.userId || authzRequest.userId == null">*</span></label>
+
+                        <div class="col-md-6">
+                            <select ui-select2="{ minimumInputLength: 1}"
+                                    data-ng-model="authzRequest.roleIds"
+                                    data-placeholder="Any role..." multiple
+                                    data-ng-required="!authzRequest.userId || authzRequest.userId == null">
+                                <option ng-repeat="role in roles track by role.id" value="{{role.name}}">{{role.name}}
+                                </option>
+                            </select>
+                        </div>
+
+                        <kc-tooltip>Specifies that this policy must be applied to one or more scopes.</kc-tooltip>
+                    </div>
+                </fieldset>
+                <fieldset>
+                    <legend collapsed><span class="text">Contextual Information</span>
+                        <kc-tooltip>The available options to configure any contextual information that will be used when evaluating policies.</kc-tooltip>
+                    </legend>
+                    <div class="form-group clearfix block">
+                        <label class="col-md-2 control-label" for="newRedirectUri">Contextual Attributes</label>
+
+                        <div class="col-sm-6">
+                            <table class="table table-striped table-bordered">
+                                <thead>
+                                    <tr>
+                                        <th>Key</th>
+                                        <th>Value</th>
+                                        <th>Actions</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <tr ng-repeat="(key, value) in (authzRequest.context.attributes)">
+                                        <td>{{getContextAttributeName(key)}}</td>
+                                        <td>
+                                            <select class="form-control" id="attribute-{{key}}"
+                                                    data-ng-model="authzRequest.context.attributes[key]"
+                                                    data-ng-show="getContextAttribute(key).values"
+                                                    ng-options="value1.key as value1.name for value1 in getContextAttribute(key).values">
+                                            </select>
+                                            <input ng-model="authzRequest.context.attributes[key]" class="form-control"
+                                                   type="text" name="{{key}}" id="attribute-{{key}}"
+                                                   data-ng-hide="getContextAttribute(key).values"/>
+                                        </td>
+                                        <td class="kc-action-cell">
+                                            <button class="btn btn-default btn-block btn-sm"
+                                                    data-ng-click="removeContextAttribute(key)">Delete
+                                            </button>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>
+                                            <select class="form-control" id="newContextAttribute.key"
+                                                    data-ng-model="newContextAttribute"
+                                                    ng-change="selectDefaultContextAttribute()"
+                                                    data-ng-hide="!isDefaultContextAttribute()"
+                                                    ng-options="attribute as attribute.name for attribute in defaultContextAttributes track by attribute.key">
+                                            </select>
+                                            <input ng-model="newContextAttribute.key" class="form-control" type="text"
+                                                   id="newAttributeKey" data-ng-hide="isDefaultContextAttribute()"/>
+                                        </td>
+                                        <td>
+                                            <select class="form-control" id="newContextAttribute.value"
+                                                    data-ng-model="newContextAttribute.value"
+                                                    data-ng-show="newContextAttribute.values"
+                                                    ng-options="value.key as value.name for value in newContextAttribute.values track by value.key">
+                                            </select>
+                                            <input ng-model="newContextAttribute.value" class="form-control" type="text"
+                                                   id="newAttributeValue" data-ng-show="!newContextAttribute.values"/>
+                                        </td>
+                                        <td class="kc-action-cell">
+                                            <button class="btn btn-default btn-block btn-sm"
+                                                    data-ng-click="addContextAttribute()"
+                                                    data-ng-disabled="!newContextAttribute.key || newContextAttribute.key == ''">
+                                                Add
+                                            </button>
+                                        </td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+
+                        <kc-tooltip>Any attribute provided by a running environment or execution context.</kc-tooltip>
+                    </div>
+                </fieldset>
+                <fieldset>
+                    <legend><span class="text">Permission</span>
+                        <kc-tooltip>The available options to configure the permissions to which policies will be applied.</kc-tooltip>
+                    </legend>
+                    <div class="form-group">
+                        <label class="col-md-2 control-label" for="applyResourceType">Apply to Resource Type</label>
+
+                        <div class="col-md-6">
+                            <input ng-model="applyResourceType" id="applyResourceType" onoffswitch
+                                   data-ng-click="setApplyToResourceType()"/>
+                        </div>
+                        <kc-tooltip>Specifies if this policy must be applied to all resources with a given type. In this
+                            case, this policy will be evaluated for all instances
+                            of a given resource type.
+                        </kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix" data-ng-hide="applyResourceType">
+                        <label class="col-md-2 control-label" for="reqActions">Resources <span class="required">*</span></label>
+
+                        <div class="col-md-6">
+                            <select ui-select2="{ minimumInputLength: 1, allowClear:true }"
+                                    ng-model="newResource._id"
+                                    data-placeholder="Select a resource..."
+                                    data-ng-required="!applyResourceType && authzRequest.resources.length == 0 && !authzRequest.entitlements"
+                                    data-ng-click="resolveScopes()"
+                                    ng-options="resource._id as resource.name for resource in resources track by resource._id">
+                                <option value=""></option>
+                            </select>
+                        </div>
+                        <kc-tooltip>Specifies that this policy must be applied to a specific resource instance.
+                        </kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix" data-ng-show="applyResourceType">
+                        <label class="col-md-2 control-label" for="newResource.type">Resource Type <span
+                                class="required">*</span></label>
+
+                        <div class="col-md-6">
+                            <input class="form-control" type="text" id="newResource.type" name="newResource.type"
+                                   data-ng-model="authzRequest.resources[0].type"
+                                   data-ng-required="applyResourceType && !authzRequest.resources[0].type && !authzRequest.entitlements">
+                        </div>
+
+                        <kc-tooltip>Specifies that this policy must be applied to all resources instances of a given
+                            type.
+                        </kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix" data-ng-show="applyResourceType || newResource._id == null">
+                        <label class="col-md-2 control-label" for="newResource.scopes">Scopes</label>
+
+                        <div class="col-md-6">
+                            <select ui-select2="{ minimumInputLength: 1}"
+                                    id="newResource.scopes"
+                                    multiple
+                                    data-ng-model="newResource.scopes"
+                                    data-placeholder="Select a scope..."
+                                    data-ng-options="scope.name as scope.name for scope in scopes track by scope.name"/>
+                        </div>
+
+                        <kc-tooltip>Specifies that this policy must be applied to one or more scopes.</kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix" data-ng-show="newResource._id != null">
+                        <label class="col-md-2 control-label" for="newResource.scopes">Scopes</label>
+
+                        <div class="col-md-6">
+                            <select ui-select2
+                                    id="newResource.scopes"
+                                    data-ng-model="newResource.scopes"
+                                    data-placeholder="Any scope..." multiple>
+                                <option ng-repeat="scope in scopes" value="{{scope.name}}">{{scope.name}}</option>
+                            </select>
+                        </div>
+
+                        <kc-tooltip>Specifies that this policy must be applied to one or more scopes.</kc-tooltip>
+                    </div>
+                    <div class="form-group clearfix block" data-ng-show="!applyResourceType">
+                        <label class="col-md-2 control-label" for="newRedirectUri"></label>
+
+                        <div class="col-sm-6">
+                            <button data-ng-click="addResource()" class="btn btn-primary">Add</button>
+                            <table class="table table-striped table-bordered">
+                                <thead>
+                                    <tr>
+                                        <th>Resource</th>
+                                        <th>Scopes</th>
+                                        <th>Actions</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <tr data-ng-show="!authzRequest.resources || authzRequest.resources.length == 0">
+                                        <td colspan="3">
+                                            No resources.
+                                        </td>
+                                    </tr>
+                                    <tr ng-repeat="resource in authzRequest.resources">
+                                        <td>{{resource.name ? resource.name : 'Any resource with scope(s)'}}</td>
+                                        <td>
+                                            <span data-ng-show="!resource.scopes.length">Any scope.</span>
+                                        <span data-ng-show="resource.scopes.length > 0">
+                                            <span ng-repeat="scope in resource.scopes">
+                                                {{scope}} {{$last ? '' : ', '}}
+                                            </span>
+                                        </span>
+                                        </td>
+                                        <td class="kc-action-cell">
+                                            <button class="btn btn-default btn-block btn-sm"
+                                                    data-ng-click="removeResource($index)">Delete
+                                            </button>
+                                        </td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </fieldset>
+
+                <div class="form-group">
+                    <div class="col-md-10 col-md-offset-2">
+                        <button kc-save data-ng-click="evaluate()">Evaluate</button>
+                        <button kc-reset data-ng-disabled="!changed">Reset</button>
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+    <div data-ng-include="resultUrl" data-ng-show="showResult"/>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html
new file mode 100644
index 0000000..a9ff3f4
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html
@@ -0,0 +1,68 @@
+<fieldset>
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <span data-ng-show="evaluationResult.results.length == 0"><strong>Could not obtain any result for the given authorization request. Check if the provided resource(s) or scope(s) are associated with any policy.</strong></span>
+        <fieldset class="border-top" data-ng-repeat="result in evaluationResult.results">
+            <legend collapsed><span class="text">{{result.resource.name}}</span>
+                <kc-tooltip>Provides information about how policies were evaluated for this resource.</kc-tooltip>
+            </legend>
+            <div class="form-group">
+                <label class="col-md-2 control-label">Result</label>
+
+                <div class="col-sm-2">
+                    <div>
+                        <span style="color: green"
+                              data-ng-show="result.status == 'PERMIT'"><strong>{{result.status}}</strong></span>
+                        <span style="color: red"
+                              data-ng-hide="result.status == 'PERMIT'"><strong>{{result.status}}</strong></span>
+                    </div>
+                </div>
+                <kc-tooltip>The overall result for this permission request.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label">Scopes</label>
+
+                <div class="col-sm-2">
+                    <span data-ng-show="result.scopes.length == 0">Any scope.</span>
+
+                    <div>
+                        <ul>
+                            <li data-ng-repeat="scope in result.scopes">
+                                {{scope.name}}
+                            </li>
+                        </ul>
+                    </div>
+                </div>
+                <kc-tooltip>The requested scopes.</kc-tooltip>
+            </div>
+            <div class="form-group" data-ng-show="!evaluationResult.entitlements">
+                <label class="col-md-2 control-label">Policies</label>
+
+                <div class="col-sm-6">
+                    <span data-ng-show="result.policies.length == 0">No policies were found for this resource.</span>
+
+                    <div>
+                        <ul>
+                            <li data-ng-repeat="policyResult in result.policies">
+                                <strong><a
+                                        href="#/realms/{{realm.realm}}/authz/resource-server/{{server.id}}/policy/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
+                                decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
+                                <span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
+                                by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision.</a>
+                                <ul>
+                                    <li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
+                                        <strong><a
+                                                href="#/realms/{{realm.realm}}/authz/resource-server/{{server.id}}/policy/{{subPolicy.policy.type}}/{{subPolicy.policy.id}}">{{subPolicy.policy.name}}</a></strong>
+                                        voted to <span style="color: green"
+                                                       data-ng-show="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>
+                                        <span style="color: red" data-ng-hide="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>.</a>
+                                    </li>
+                                </ul>
+                            </li>
+                        </ul>
+                    </div>
+                </div>
+                <kc-tooltip>Details about which policies were evaluated and their decisions.</kc-tooltip>
+            </div>
+        </fieldset>
+    </form>
+</fieldset>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
new file mode 100644
index 0000000..cb4691c
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
@@ -0,0 +1,53 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <table class="table table-striped table-bordered">
+        <caption class="hidden">Table of identity providers</caption>
+        <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            Filter by:&nbsp;&nbsp;
+                            <div class="input-group">
+                                <input type="text" placeholder="Name" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                            </div>
+                            <div class="input-group">
+                                <select class="form-control search" data-ng-model="search.type"
+                                        ng-options="p.type as p.name group by p.group for p in policyProviders track by p.type">
+                                    <option value="" selected ng-click="search.type = ''">All types</option>
+                                </select>
+                            </div>
+                        </div>
+                        <div class="pull-right">
+                            <select class="form-control" ng-model="policyType"
+                                    ng-options="p.name group by p.group for p in policyProviders track by p.type"
+                                    data-ng-change="addPolicy(policyType);">
+                                <option value="" disabled selected>Create policy...</option>
+                            </select>
+                        </div>
+                    </div>
+                </th>
+            </tr>
+            <tr data-ng-hide="policies.length == 0">
+                <th>Policy Name</th>
+                <th>Description</th>
+                <th>Type</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
+                <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
+                <td>{{policy.description}}</td>
+                <td>{{policy.type}}</td>
+            </tr>
+            <tr data-ng-show="(policies | filter:search).length == 0">
+                <td class="text-muted" colspan="3" data-ng-show="search.name">No results</td>
+                <td class="text-muted" colspan="3" data-ng-hide="search.name">No policies available</td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-detail.html
new file mode 100644
index 0000000..918cadf
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-detail.html
@@ -0,0 +1,82 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset>
+            <div class="form-group">
+                <label for="import-file" class="col-sm-2 control-label">Import</label>
+                <div class="col-md-6">
+                    <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
+                        <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+                        <input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
+                    </div>
+                    <span class="kc-uploaded-file" data-ng-show="files.length > 0">{{files[0].name}}</span>
+                </div>
+                <kc-tooltip>Import a JSON file containing all settings for this resource server.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <div class="col-md-10 col-md-offset-2">
+                    <button type="submit" data-ng-disabled="files.length == 0" data-ng-click="uploadFile()" class="btn btn-primary">Upload</button>
+                    <button type="submit" data-ng-disabled="files.length == 0" data-ng-click="clearFileSelect()" class="btn btn-default" data-ng-show="files.length > 0">Cancel</button>
+                </div>
+            </div>
+        </fieldset>
+        <fieldset class="border-top" data-ng-hide="files.length > 0">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="server.policyEnforcementMode">Policy Enforcement Mode</label>
+                <div class="col-md-2">
+                    <select class="form-control" id="server.policyEnforcementMode" data-ng-model="server.policyEnforcementMode">
+                        <option value="ENFORCING">Enforcing</option>
+                        <option value="PERMISSIVE">Permissive</option>
+                        <option value="DISABLED">Disabled</option>
+                    </select>
+                </div>
+                <kc-tooltip>The policy enforcement mode dictates how policies are enforced when evaluating authorization requests. 'Enforcing' means requests are denied by default even when there is no policy associated with a given resource. 'Permissive' means requests
+                are allowed even when there is no policy associated with a given resource. 'Disabled' completely disables the evaluation of policies and allow access to any resource.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="server.allowRemoteResourceManagement">Remote Resource Management</label>
+                <div class="col-md-6">
+                    <input ng-model="server.allowRemoteResourceManagement" id="server.allowRemoteResourceManagement" onoffswitch />
+                </div>
+                <kc-tooltip>Should resources be managed remotely by the resource server? If false, resources can only be managed from this admin console.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                    <button kc-save data-ng-disabled="!changed">Save</button>
+                    <button kc-cancel data-ng-click="cancel()">Cancel</button>
+                </div>
+                <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                    <button kc-save  data-ng-disabled="!changed">Save</button>
+                    <button kc-reset data-ng-disabled="!changed">Cancel</button>
+                </div>
+            </div>
+        </fieldset>
+
+        <fieldset class="border-top" data-ng-show="!files || files.length == 0">
+            <legend><span class="text">Export Settings</span>
+                <kc-tooltip>Here you can export all settings for this resource server.</kc-tooltip>
+            </legend>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="server.allowRemoteResourceManagement">Export Settings</label>
+                <div class="col-md-6">
+                    <button data-ng-click="export()" class="btn btn-primary" data-ng-hide="settings">Export</button>
+                    <button data-ng-click="downloadSettings()" class="btn btn-primary" data-ng-show="settings">Download</button>
+                    <button data-ng-click="cancelExport()" class="btn btn-primary" data-ng-show="settings">Cancel</button>
+                </div>
+                <kc-tooltip>Export and download all settings for this resource server.</kc-tooltip>
+            </div>
+            <fieldset class="margin-top">
+                <div class="form-group" ng-show="settings">
+                    <div class="col-sm-12">
+                        <a class="btn btn-primary btn-lg" data-ng-click="download()" type="submit" ng-show="installation">Download</a>
+                        <textarea class="form-control" rows="20" kc-select-action="click">{{settings}}</textarea>
+                    </div>
+                </div>
+            </fieldset>
+        </fieldset>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-list.html
new file mode 100644
index 0000000..3b4130e
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-list.html
@@ -0,0 +1,49 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <h1>
+        <span>Resource Servers</span>
+        <kc-tooltip>Resource Servers are applications serving resources to their users. These resources can be a RESTFul API, web pages or any other kind of resource that must be managed and protected by a set of authorization policies.</kc-tooltip>
+    </h1>
+
+    <table class="table table-striped table-bordered">
+        <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            <div class="input-group">
+                                <input type="text" placeholder="Search..." data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="pull-right">
+                            <a id="createServer" class="btn btn-default" href="#/realms/{{realm.realm}}/authz/resource-server/create">Create</a>
+                        </div>
+                    </div>
+                </th>
+            </tr>
+            <tr data-ng-hide="servers.length == 0">
+                <th>Name</th>
+                <th>Policy Enforcement Mode</th>
+                <th>Allows Remote Resource Management ?</th>
+                <th>Allows Entitlement ?</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr ng-repeat="server in servers | filter:search | orderBy:'clientId'">
+                <td><a href="#/realms/{{realm.realm}}/authz/resource-server/{{server.id}}">{{server.name}}</a></td>
+                <td>{{server.policyEnforcementMode | toCamelCase}}</td>
+                <td>{{server.allowRemoteResourceManagement}}</td>
+                <td>{{server.allowEntitlements}}</td>
+            </tr>
+            <tr data-ng-show="(servers | filter:search).length == 0">
+                <td class="text-muted" colspan="3" data-ng-show="search.clientId">No results</td>
+                <td class="text-muted" colspan="3" data-ng-hide="search.clientId">No servers available</td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
new file mode 100644
index 0000000..0b68b2a
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
@@ -0,0 +1,79 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource">Resource</a></li>
+        <li data-ng-show="create">Add Resource</li>
+        <li data-ng-hide="create">{{resource.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Resource</h1>
+    <h1 data-ng-hide="create">{{resource.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="resource.name" autofocus required>
+                </div>
+                <kc-tooltip>An unique name for this resource. The name can be used to uniquely identify a resource, useful when querying for a specific resource.</kc-tooltip>
+            </div>
+            <div class="form-group" data-ng-hide="create">
+                <label class="col-md-2 control-label" for="resource.owner.name">Owner </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="resource.owner.name" name="name" data-ng-model="resource.owner.name" autofocus disabled>
+                </div>
+                <kc-tooltip>The owner of this resource.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="type">Type </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="type" name="name" data-ng-model="resource.type" autofocus>
+                </div>
+                <kc-tooltip>The type of this resource. It can be used to group different resource instances with the same type.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="uri">URI </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="uri" name="name" data-ng-model="resource.uri" autofocus>
+                </div>
+                <kc-tooltip>An URI that can also be used to uniquely identify this resource.</kc-tooltip>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="reqActions">Scopes</label>
+
+                <div class="col-md-6">
+                    <select ui-select2 id="reqActions" ng-model="resource.scopes" data-placeholder="Select an scope..." multiple>
+                        <option ng-repeat="scope in scopes" value="{{scope.name}}" ng-selected="true">{{scope.name}}</option>
+                    </select>
+                </div>
+
+                <kc-tooltip>The scopes associated with this resource.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="iconUri">Icon URI </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="iconUri" name="name" data-ng-model="resource.icon_uri" autofocus>
+                </div>
+                <kc-tooltip>An URI pointing to an icon for this resource.</kc-tooltip>
+            </div>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
new file mode 100644
index 0000000..b4e0264
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-list.html
@@ -0,0 +1,85 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <table class="table table-striped table-bordered">
+        <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="7">
+                    <div class="form-inline">
+                        Filter by:&nbsp;&nbsp;
+                        <div class="form-group">
+                            <div class="input-group">
+                                <input type="text" placeholder="Name" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <input type="text" placeholder="Owner" data-ng-model="search.owner.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
+                            </div>
+                            <div class="input-group">
+                                <select class="form-control search" data-ng-model="search.type"
+                                        ng-options="r.type as r.type for r in resources | unique : 'type'">
+                                    <option value="" selected ng-click="search.type = ''">All types</option>
+                                </select>
+                            </div>
+                        </div>
+
+                        <div class="pull-right">
+                            <a id="createResource" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/create">Create</a>
+                        </div>
+                    </div>
+                </th>
+            </tr>
+            <tr data-ng-hide="resources.length == 0">
+                <th>Name</th>
+                <th>Type</th>
+                <th>Uri</th>
+                <th>Owner</th>
+                <th>Scopes</th>
+                <th>Permissions</th>
+                <th>Actions</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr ng-repeat="resource in resources | filter:search | orderBy:'name'">
+                <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a></td>
+                <td>
+                    <span data-ng-show="resource.type">{{resource.type}}</span>
+                    <span data-ng-show="!resource.type">No type defined.</span>
+                </td>
+                <td>{{resource.uri}}</td>
+                <td>{{resource.owner.name}}</td>
+                <td>
+                    <span data-ng-show="!resource.scopes.length">No scopes assigned.</span>
+                    <span data-ng-show="resource.scopes.length > 0">
+                        <span ng-repeat="scope in resource.scopes">
+                            <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a>{{$last ? '' : ', '}}
+                        </span>
+                    </span>
+                </td>
+                <td>
+                    <span data-ng-show="!resource.policies.length">No permission assigned.</span>
+                    <span data-ng-show="resource.policies.length > 0">
+                        <span ng-repeat="policy in resource.policies">
+                            <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
+                        </span>
+                    </span>
+                </td>
+                <td class="kc-action-cell" style="vertical-align: middle">
+                    <button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(resource);">Create Permission</button>
+                </td>
+            </tr>
+            <tr data-ng-show="(resources | filter:search).length == 0">
+                <td class="text-muted" colspan="6" data-ng-show="search.name">No results</td>
+                <td class="text-muted" colspan="6" data-ng-hide="search.name">No resources available</td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
new file mode 100644
index 0000000..f215cac
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
@@ -0,0 +1,47 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
+        <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope">Scope</a></li>
+        <li data-ng-show="create">Add Scope</li>
+        <li data-ng-hide="create">{{scope.name}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Add Scope</h1>
+    <h1 data-ng-hide="create">{{scope.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create"
+                                                         data-ng-hide="changed" data-ng-click="remove()"></i></h1>
+
+    <form class="form-horizontal" name="clientForm" novalidate>
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Name </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="scope.name" autofocus>
+                </div>
+                <kc-tooltip>An unique name for this scope. The name can be used to uniquely identify a scope, useful when querying for a specific scope.</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">Icon URI </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="name" name="name" data-ng-model="scope.iconUri" autofocus>
+                </div>
+                <kc-tooltip>An URI pointing to an icon for this scope.</kc-tooltip>
+            </div>
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create">
+                <button kc-save data-ng-disabled="!changed">Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
new file mode 100644
index 0000000..42d6026
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-list.html
@@ -0,0 +1,41 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <kc-tabs-resource-server></kc-tabs-resource-server>
+
+    <table class="table table-striped table-bordered">
+        <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            <div class="input-group">
+                                <input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="pull-right">
+                            <a id="createScope" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/create">Create</a>
+                        </div>
+                    </div>
+                </th>
+            </tr>
+            <tr data-ng-hide="scopes.length == 0">
+                <th>Scope Name</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr ng-repeat="scope in scopes | filter:search | orderBy:'name'">
+                <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a></td>
+            </tr>
+            <tr data-ng-show="(scopes | filter:search).length == 0">
+                <td class="text-muted" colspan="3" data-ng-show="search.name">No results</td>
+                <td class="text-muted" colspan="3" data-ng-hide="search.name">No scopes available</td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index ef1f7b4..8e1f254 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -110,6 +110,13 @@
                     <input ng-model="client.serviceAccountsEnabled" name="serviceAccountsEnabled" id="serviceAccountsEnabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
                 </div>
             </div>
+            <div class="form-group" data-ng-show="protocol == 'openid-connect'">
+                <label class="col-md-2 control-label" for="authorizationServicesEnabled">Authorization Enabled</label>
+                <kc-tooltip>Enable/Disable fine-grained authorization support for a client</kc-tooltip>
+                <div class="col-md-6">
+                    <input ng-model="client.authorizationServicesEnabled" name="authorizationServicesEnabled" id="authorizationServicesEnabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+                </div>
+            </div>
             <div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
                 <label class="col-md-2 control-label" for="samlServerSignature">{{:: 'include-authnstatement' | translate}}</label>
                 <div class="col-sm-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
index 04a5478..ce1c61b 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
@@ -32,7 +32,7 @@
             </div>
 
             <div class="form-group">
-                <label class="col-md-2 contr    ol-label">{{:: 'endpoints' | translate}}</label>
+                <label class="col-md-2 control-label">{{:: 'endpoints' | translate}}</label>
                 <div class="col-md-6">
                     <a lass="form-control" ng-href="/auth/realms/{{realm.id}}/.well-known/openid-configuration" target="_blank">OpenID Endpoint Configuration</a>
                 </div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-authz-modal.html b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-authz-modal.html
new file mode 100644
index 0000000..18acf86
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-authz-modal.html
@@ -0,0 +1,11 @@
+<div class="modal-header">
+    <button type="button" class="close" ng-click="cancel()">
+        <span class="pficon pficon-close"></span>
+    </button>
+    <h4 class="modal-title">{{title}}</h4>
+</div>
+<div class="modal-body" ng-bind-html="message"></div>
+<div class="modal-footer">
+    <button type="button" data-ng-class="btns.cancel.cssClass" ng-click="cancel()">{{btns.cancel.label}}</button>
+    <button type="button" data-ng-class="btns.ok.cssClass" ng-click="ok()">{{btns.ok.label}}</button>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html
new file mode 100755
index 0000000..d03eefb
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html
@@ -0,0 +1,13 @@
+<div data-ng-controller="ClientTabCtrl">
+
+    <kc-tabs-client></kc-tabs-client>
+
+    <ul class="nav nav-tabs nav-tabs-pf"  data-ng-hide="create && !path[4]" style="margin-left: 15px">
+        <li ng-class="{active: !path[6]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/">Settings</a></li>
+        <li ng-class="{active: path[6] == 'resource'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource">Resources</a></li>
+        <li ng-class="{active: path[6] == 'scope'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope">Authorization Scopes</a></li>
+        <li ng-class="{active: path[6] == 'policy'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy">Policies</a></li>
+        <li ng-class="{active: path[6] == 'permission'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission">Permissions</a></li>
+        <li ng-class="{active: path[6] == 'evaluate'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/evaluate">Evaluate</a></li>
+    </ul>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
index 58388ff..63132af 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
@@ -6,7 +6,7 @@
         <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="removeClient()"></i>
     </h1>
 
-    <ul class="nav nav-tabs nav-tabs-pf"  data-ng-hide="create && !path[4]">
+    <ul class="nav nav-tabs"  data-ng-hide="create && !path[4]">
         <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'settings' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!client.publicClient && client.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">{{:: 'credentials' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
@@ -19,6 +19,7 @@
             <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/scope-mappings">{{:: 'scope' | translate}}</a>
             <kc-tooltip>{{:: 'scope.tooltip' | translate}}</kc-tooltip>
         </li>
+        <li ng-class="{active: path[4] == 'authz'}" data-ng-show="client.authorizationServicesEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">Authorization</a></li>
         <li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">{{:: 'revocation' | translate}}</a></li>
     <!--    <li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/identity-provider">Identity Provider</a></li> -->
         <li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!client.bearerOnly">
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
index 837407a..96d4780 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
@@ -36,6 +36,9 @@
                 <local-cache name="realmVersions">
                     <transaction mode="BATCH" locking="PESSIMISTIC"/>
                 </local-cache>
+                <local-cache name="authorization">
+                    <eviction max-entries="100" strategy="LRU"/>
+                </local-cache>
             </cache-container>
             <cache-container name="server" default-cache="default" module="org.wildfly.clustering.server">
                 <local-cache name="default">