keycloak-aplcache

Changes

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index 1ba9cc5..61f46f1 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -29,6 +29,7 @@ import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.OIDCHttpFacade;
 import org.keycloak.adapters.spi.HttpFacade.Request;
 import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
@@ -203,7 +204,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
-        return new AuthorizationContext() {
+        return new ClientAuthorizationContext(authzClient) {
             @Override
             public boolean hasPermission(String resourceName, String scopeName) {
                 return granted;
@@ -252,7 +253,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
-        return new AuthorizationContext(accessToken, this.paths);
+        return new ClientAuthorizationContext(accessToken, this.paths, authzClient);
     }
 
     private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index 679a33c..7f21eef 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -21,7 +21,9 @@ import org.jboss.logging.Logger;
 import org.keycloak.AuthorizationContext;
 import org.keycloak.adapters.KeycloakDeployment;
 import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
 import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
 import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
@@ -56,7 +58,12 @@ public class PolicyEnforcer {
     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.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()), new ClientAuthenticator() {
+            @Override
+            public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
+                ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, requestParams);
+            }
+        });
         this.pathMatcher = new PathMatcher(this.authzClient);
         this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
 
diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml
index 01c07da..f3f285d 100755
--- a/adapters/oidc/jetty/pom.xml
+++ b/adapters/oidc/jetty/pom.xml
@@ -34,6 +34,7 @@
         <module>jetty-core</module>
         <module>jetty8.1</module>
         <module>jetty9.2</module>
+        <module>jetty9.3</module>
     </modules>
 
     <profiles>
@@ -46,7 +47,6 @@
             </activation>
             <modules>
                 <module>jetty9.1</module>
-                <module>jetty9.3</module>
                 <module>jetty9.4</module>
             </modules>
         </profile>
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java
index 6ddf52c..d1e6664 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.adapters.saml;
 
+import org.keycloak.adapters.saml.SamlDeployment.IDP.SingleSignOnService;
 import org.jboss.logging.Logger;
 import org.keycloak.adapters.spi.AuthChallenge;
 import org.keycloak.adapters.spi.HttpFacade;
@@ -95,21 +96,22 @@ public abstract class AbstractInitiateLogin implements AuthChallenge {
             nameIDPolicyFormat =  JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
         }
 
+        SingleSignOnService sso = deployment.getIDP().getSingleSignOnService();
         SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
-                .destination(deployment.getIDP().getSingleSignOnService().getRequestBindingUrl())
+                .destination(sso.getRequestBindingUrl())
                 .issuer(issuerURL)
                 .forceAuthn(deployment.isForceAuthentication()).isPassive(deployment.isIsPassive())
                 .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
-        if (deployment.getIDP().getSingleSignOnService().getResponseBinding() != null) {
+        if (sso.getResponseBinding() != null) {
             String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
-            if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
+            if (sso.getResponseBinding() == SamlDeployment.Binding.POST) {
                 protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
             }
             authnRequestBuilder.protocolBinding(protocolBinding);
 
         }
-        if (deployment.getAssertionConsumerServiceUrl() != null) {
-            authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
+        if (sso.getAssertionConsumerServiceUrl() != null) {
+            authnRequestBuilder.assertionConsumerUrl(sso.getAssertionConsumerServiceUrl());
         }
         return authnRequestBuilder;
     }
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
index de95d87..76d7648 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
@@ -32,6 +32,7 @@ public class IDP implements Serializable {
         private String requestBinding;
         private String responseBinding;
         private String bindingUrl;
+        private String assertionConsumerServiceUrl;
         private boolean validateAssertionSignature;
 
         public boolean isSignRequest() {
@@ -81,6 +82,14 @@ public class IDP implements Serializable {
         public void setBindingUrl(String bindingUrl) {
             this.bindingUrl = bindingUrl;
         }
+
+        public String getAssertionConsumerServiceUrl() {
+            return assertionConsumerServiceUrl;
+        }
+
+        public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
+            this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
+        }
     }
 
     public static class SingleLogoutService implements Serializable {
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index 1a3dd04..256bfa2 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -72,6 +72,7 @@ public class ConfigXmlConstants {
     public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
     public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
     public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
+    public static final String ASSERTION_CONSUMER_SERVICE_URL_ATTR = "assertionConsumerServiceUrl";
 
     public static final String HTTP_CLIENT_ELEMENT = "HttpClient";
     public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname";
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index 7af71ba..b5f8aba 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -41,6 +41,7 @@ import java.security.cert.Certificate;
 import java.util.HashSet;
 import java.util.Set;
 import org.keycloak.adapters.cloned.HttpClientBuilder;
+import java.net.URI;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -156,6 +157,12 @@ public class DeploymentBuilder {
         if (sp.getIdp().getSingleSignOnService().getResponseBinding() != null) {
             sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
         }
+        if (sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl() != null) {
+            if (! sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl().endsWith("/saml")) {
+                throw new RuntimeException("AssertionConsumerServiceUrl must end with \"/saml\".");
+            }
+            sso.setAssertionConsumerServiceUrl(URI.create(sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl()));
+        }
         sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
         sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
         sso.setValidateAssertionSignature(sp.getIdp().getSingleSignOnService().isValidateAssertionSignature());
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
index be54223..839d4f6 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -118,6 +118,7 @@ public class IDPXmlParser extends AbstractParser {
         sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
         sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
         sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+        sso.setAssertionConsumerServiceUrl(getAttributeValue(element, ConfigXmlConstants.ASSERTION_CONSUMER_SERVICE_URL_ATTR));
         return sso;
     }
 
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
index a52cdc2..d92884f 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -31,6 +31,7 @@ import org.keycloak.adapters.saml.rotation.SamlDescriptorPublicKeyLocator;
 import org.keycloak.rotation.CompositeKeyLocator;
 import org.keycloak.rotation.HardcodedKeyLocator;
 import org.keycloak.rotation.KeyLocator;
+import java.net.URI;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -45,6 +46,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
         private Binding requestBinding;
         private Binding responseBinding;
         private String requestBindingUrl;
+        private URI assertionConsumerServiceUrl;
 
         @Override
         public boolean signRequest() {
@@ -76,6 +78,15 @@ public class DefaultSamlDeployment implements SamlDeployment {
             return requestBindingUrl;
         }
 
+        @Override
+        public URI getAssertionConsumerServiceUrl() {
+            return assertionConsumerServiceUrl;
+        }
+
+        public void setAssertionConsumerServiceUrl(URI assertionConsumerServiceUrl) {
+            this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
+        }
+
         public void setSignRequest(boolean signRequest) {
             this.signRequest = signRequest;
         }
@@ -277,7 +288,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
     private boolean turnOffChangeSessionIdOnLogin;
     private PrivateKey decryptionKey;
     private KeyPair signingKeyPair;
-    private String assertionConsumerServiceUrl;
     private Set<String> roleAttributeNames;
     private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
     private String principalAttributeName;
@@ -341,11 +351,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
     }
 
     @Override
-    public String getAssertionConsumerServiceUrl() {
-        return assertionConsumerServiceUrl;
-    }
-
-    @Override
     public Set<String> getRoleAttributeNames() {
         return roleAttributeNames;
     }
@@ -396,10 +401,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
         this.signingKeyPair = signingKeyPair;
     }
 
-    public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
-        this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
-    }
-
     public void setRoleAttributeNames(Set<String> roleAttributeNames) {
         this.roleAttributeNames = roleAttributeNames;
     }
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java
index 96cf6f8..7f40e5a 100644
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java
@@ -156,7 +156,15 @@ public class EcpAuthenticationHandler extends AbstractSamlAuthenticationHandler 
                 paosRequestHeader.setMustUnderstand(true);
                 paosRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next");
                 paosRequestHeader.addAttribute(envelope.createName("service"), JBossSAMLURIConstants.ECP_PROFILE.get());
-                paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), deployment.getAssertionConsumerServiceUrl());
+                paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), getResponseConsumerUrl());
+            }
+
+            private String getResponseConsumerUrl() {
+                return (deployment.getIDP() == null
+                  || deployment.getIDP().getSingleSignOnService() == null
+                  || deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl() == null
+                ) ? null
+                  : deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl().toString();
             }
         };
     }
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 4442177..a9df7e9 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -25,6 +25,7 @@ import java.security.PrivateKey;
 import java.util.Set;
 import org.apache.http.client.HttpClient;
 import org.keycloak.rotation.KeyLocator;
+import java.net.URI;
 
 /**
  * Represents SAML deployment configuration.
@@ -103,8 +104,24 @@ public interface SamlDeployment {
              */
             boolean validateAssertionSignature();
             Binding getRequestBinding();
+            /**
+             * SAML allows the client to request what binding type it wants authn responses to use. The default is
+             * that the client will not request a specific binding type for responses.
+             * @return
+             */
             Binding getResponseBinding();
+            /**
+             * Returns URL for the IDP login service that the client will send requests to.
+             * @return
+             */
             String getRequestBindingUrl();
+            /**
+             * Returns URI where the IdP should send the responses to. The default is
+             * that the client will not request a specific assertion consumer service URL.
+             * This property is typically accompanied by the ProtocolBinding attribute.
+             * @return
+             */
+            URI getAssertionConsumerServiceUrl();
         }
 
         public interface SingleLogoutService {
@@ -141,7 +158,6 @@ public interface SamlDeployment {
     KeyPair getSigningKeyPair();
     String getSignatureCanonicalizationMethod();
     SignatureAlgorithm getSignatureAlgorithm();
-    String getAssertionConsumerServiceUrl();
     String getLogoutPage();
 
     Set<String> getRoleAttributeNames();
diff --git a/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_8.xsd b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_8.xsd
new file mode 100644
index 0000000..a5169d3
--- /dev/null
+++ b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_8.xsd
@@ -0,0 +1,456 @@
+<?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.
+  -->
+
+<xs:schema version="1.0"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="urn:keycloak:saml:adapter"
+           targetNamespace="urn:keycloak:saml:adapter"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified">
+
+    <xs:element name="keycloak-saml-adapter" type="adapter-type"/>
+    <xs:complexType name="adapter-type">
+        <xs:annotation>
+            <xs:documentation>Keycloak SAML Adapter configuration file.</xs:documentation>
+        </xs:annotation>
+        <xs:all>
+            <xs:element name="SP" maxOccurs="1" minOccurs="0" type="sp-type">
+                <xs:annotation>
+                    <xs:documentation>Describes SAML service provider configuration.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:all>
+    </xs:complexType>
+
+    <xs:complexType name="sp-type">
+        <xs:all>
+            <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        List of service provider encryption and validation keys.
+
+                        If the IDP requires that the client application (SP) sign all of its requests and/or if the IDP will encrypt assertions, you must define the keys used to do this. For client signed documents you must define both the private and public key or certificate that will be used to sign documents. For encryption, you only have to define the private key that will be used to decrypt.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="PrincipalNameMapping" type="principal-name-mapping-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>When creating a Java Principal object that you obtain from methods like HttpServletRequest.getUserPrincipal(), you can define what name that is returned by the Principal.getName() method.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="RoleIdentifiers" type="role-identifiers-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Defines what SAML attributes within the assertion received from the user should be used as role identifiers within the Java EE Security Context for the user.
+                    By default Role attribute values are converted to Java EE roles. Some IDPs send roles via a member or memberOf attribute assertion. You can define one or more Attribute elements to specify which SAML attributes must be converted into roles.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="IDP" type="idp-type" minOccurs="1" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Describes configuration of SAML identity provider for this service provider.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:all>
+        <xs:attribute name="entityID" type="xs:string" use="required">
+                <xs:annotation>
+                    <xs:documentation>This is the identifier for this client. The IDP needs this value to determine who the client is that is communicating with it.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="sslPolicy" type="ssl-policy-type" use="optional">
+                <xs:annotation>
+                    <xs:documentation>SSL policy the adapter will enforce.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="nameIDPolicyFormat" type="xs:string" use="optional">
+                <xs:annotation>
+                    <xs:documentation>SAML clients can request a specific NameID Subject format. Fill in this value if you want a specific format. It must be a standard SAML format identifier, i.e. urn:oasis:names:tc:SAML:2.0:nameid-format:transient. By default, no special format is requested.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="logoutPage" type="xs:string" use="optional">
+                <xs:annotation>
+                    <xs:documentation>URL of the logout page.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceAuthentication" type="xs:boolean" use="optional">
+                <xs:annotation>
+                    <xs:documentation>SAML clients can request that a user is re-authenticated even if they are already logged in at the IDP. Default value is false.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="isPassive" type="xs:boolean" use="optional">
+                <xs:annotation>
+                    <xs:documentation>SAML clients can request that a user is never asked to authenticate even if they are not logged in at the IDP. Set this to true if you want this. Do not use together with forceAuthentication as they are opposite. Default value is false.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="turnOffChangeSessionIdOnLogin" type="xs:boolean" use="optional">
+                <xs:annotation>
+                    <xs:documentation>The session id is changed by default on a successful login on some platforms to plug a security attack vector. Change this to true to disable this. It is recommended you do not turn it off. Default value is false.</xs:documentation>
+                </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+
+    <xs:complexType name="keys-type">
+        <xs:sequence>
+            <xs:element name="Key" type="key-type" minOccurs="1" maxOccurs="unbounded">
+                <xs:annotation>
+                    <xs:documentation>Describes a single key used for signing or encryption.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="key-type">
+        <xs:all>
+            <xs:element name="KeyStore" maxOccurs="1" minOccurs="0" type="key-store-type">
+                <xs:annotation>
+                    <xs:documentation>Java keystore to load keys and certificates from.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="PrivateKeyPem" type="xs:string" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Private key (PEM format)</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="PublicKeyPem" type="xs:string" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Public key (PEM format)</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="CertificatePem" type="xs:string" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Certificate key (PEM format)</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:all>
+        <xs:attribute name="signing" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Flag defining whether the key should be used for signing.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encryption" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Flag defining whether the key should be used for encryption</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="key-store-type">
+        <xs:all>
+            <xs:element name="PrivateKey" maxOccurs="1" minOccurs="0" type="private-key-type">
+                <xs:annotation>
+                    <xs:documentation>Private key declaration</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="Certificate" type="certificate-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Certificate declaration</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:all>
+        <xs:attribute name="file" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>File path to the key store.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="resource" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>WAR resource path to the key store. This is a path used in method call to ServletContext.getResourceAsStream().</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="password" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>The password of the key store.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="private-key-type">
+        <xs:attribute name="alias" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>Alias that points to the key or cert within the keystore.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="password" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>Keystores require an additional password to access private keys. In the PrivateKey element you must define this password within a password attribute.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="certificate-type">
+        <xs:attribute name="alias" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>Alias that points to the key or cert within the keystore.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="principal-name-mapping-type">
+        <xs:attribute name="policy" type="principal-name-mapping-policy-type" use="required">
+            <xs:annotation>
+                <xs:documentation>Policy used to populate value of Java Principal object obtained from methods like HttpServletRequest.getUserPrincipal().</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="attribute" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>Name of the SAML assertion attribute to use within.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:simpleType name="principal-name-mapping-policy-type">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="FROM_NAME_ID">
+                <xs:annotation>
+                    <xs:documentation>This policy just uses whatever the SAML subject value is. This is the default setting</xs:documentation>
+                </xs:annotation>
+            </xs:enumeration>
+            <xs:enumeration value="FROM_ATTRIBUTE">
+                <xs:annotation>
+                    <xs:documentation>This will pull the value from one of the attributes declared in the SAML assertion received from the server. You'll need to specify the name of the SAML assertion attribute to use within the attribute XML attribute.</xs:documentation>
+                </xs:annotation>
+            </xs:enumeration>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="ssl-policy-type">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="ALL">
+                <xs:annotation>
+                    <xs:documentation>All requests must come in via HTTPS.</xs:documentation>
+                </xs:annotation>
+            </xs:enumeration>
+            <xs:enumeration value="EXTERNAL">
+                <xs:annotation>
+                    <xs:documentation>Only non-private IP addresses must come over the wire via HTTPS.</xs:documentation>
+                </xs:annotation>
+            </xs:enumeration>
+            <xs:enumeration value="NONE">
+                <xs:annotation>
+                    <xs:documentation>no requests are required to come over via HTTPS.</xs:documentation>
+                </xs:annotation>
+            </xs:enumeration>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="signature-algorithm-type">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="RSA_SHA1"/>
+            <xs:enumeration value="RSA_SHA256"/>
+            <xs:enumeration value="RSA_SHA512"/>
+            <xs:enumeration value="DSA_SHA1"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="binding-type">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="POST"/>
+            <xs:enumeration value="REDIRECT"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="role-identifiers-type">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="Attribute" maxOccurs="unbounded" minOccurs="0" type="attribute-type">
+                <xs:annotation>
+                    <xs:documentation>Specifies SAML attribute to be converted into roles.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:choice>
+    </xs:complexType>
+    <xs:complexType name="attribute-type">
+        <xs:attribute name="name" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>Specifies name of the SAML attribute to be converted into roles.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="idp-type">
+        <xs:sequence minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="SingleSignOnService" maxOccurs="1" minOccurs="1" type="sign-on-type">
+                <xs:annotation>
+                    <xs:documentation>Configuration of the login SAML endpoint of the IDP.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="SingleLogoutService" type="logout-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Configuration of the logout SAML endpoint of the IDP</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>The Keys sub element of IDP is only used to define the certificate or public key to use to verify documents signed by the IDP.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="HttpClient" type="http-client-type" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>Configuration of HTTP client used for automatic obtaining of certificates containing public keys for IDP signature verification via SAML descriptor of the IDP.</xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:sequence>
+        <xs:attribute name="entityID" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>issuer ID of the IDP.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="signaturesRequired" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>If set to true, the client adapter will sign every document it sends to the IDP. Also, the client will expect that the IDP will be signing any documents sent to it. This switch sets the default for all request and response types.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="signatureAlgorithm" type="signature-algorithm-type" use="optional">
+            <xs:annotation>
+                <xs:documentation>Signature algorithm that the IDP expects signed documents to use. Defaults to RSA_SHA256</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the signature canonicalization method that the IDP expects signed documents to use. The default value is https://www.w3.org/2001/10/xml-exc-c14n# and should be good for most IDPs.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encryption" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation></xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="sign-on-type">
+        <xs:attribute name="signRequest" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client expect the IDP to sign the assertion response document sent back from an auhtn request? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="validateAssertionSignature" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client expect the IDP to sign the individual assertions sent back from an auhtn request? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="requestBinding" type="binding-type" use="optional">
+            <xs:annotation>
+                <xs:documentation>SAML binding type used for communicating with the IDP. The default value is POST, but you can set it to REDIRECT as well.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="responseBinding" type="binding-type" use="optional">
+            <xs:annotation>
+                <xs:documentation>SAML allows the client to request what binding type it wants authn responses to use. This value maps to ProtocolBinding attribute in SAML AuthnRequest. The default is that the client will not request a specific binding type for responses.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bindingUrl" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>This is the URL for the IDP login service that the client will send requests to.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="assertionConsumerServiceUrl" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>URL of the assertion consumer service (ACS) where the IDP login service should send responses to. By default it is unset, relying on the IdP settings. When set, it must end in "/saml". This property is typically accompanied by the responseBinding attribute.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+
+    <xs:complexType name="logout-type">
+        <xs:attribute name="signRequest" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="signResponse" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client sign logout responses it sends to the IDP requests? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client expect signed logout request documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
+            <xs:annotation>
+                <xs:documentation>Should the client expect signed logout response documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="requestBinding" type="binding-type" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the SAML binding type used for communicating SAML requests to the IDP. The default value is POST.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="responseBinding" type="binding-type" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the SAML binding type used for communicating SAML responses to the IDP. The default value is POST.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="postBindingUrl" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the URL for the IDP's logout service when using the POST binding. This setting is REQUIRED if using the POST binding.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="redirectBindingUrl" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the URL for the IDP's logout service when using the REDIRECT binding. This setting is REQUIRED if using the REDIRECT binding.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+
+    <xs:complexType name="http-client-type">
+        <xs:attribute name="allowAnyHostname" type="xs:boolean" use="optional" default="false">
+            <xs:annotation>
+                <xs:documentation>If the the IDP server requires HTTPS and this config option is set to true the IDP's certificate
+                    is validated via the truststore, but host name validation is not done. This setting should only be used during
+                    development and never in production as it will partly disable verification of SSL certificates.
+                    This seting may be useful in test environments. The default value is false.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="clientKeystore" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>This is the file path to a keystore file. This keystore contains client certificate 
+                    for two-way SSL when the adapter makes HTTPS requests to the IDP server.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="clientKeystorePassword" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>Password for the client keystore and for the client's key.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionPoolSize" type="xs:int" use="optional" default="10">
+            <xs:annotation>
+                <xs:documentation>Defines number of pooled connections.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="disableTrustManager" type="xs:boolean" use="optional" default="false">
+            <xs:annotation>
+                <xs:documentation>If the the IDP server requires HTTPS and this config option is set to true you do not have to specify a truststore.
+                    This setting should only be used during development and never in production as it will disable verification of SSL certificates.
+                    The default value is false.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="proxyUrl" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>URL to HTTP proxy to use for HTTP connections.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="truststore" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>The value is the file path to a keystore file. If you prefix the path with classpath:,
+                    then the truststore will be obtained from the deployment's classpath instead. Used for outgoing 
+                    HTTPS communications to the IDP server. Client making HTTPS requests need
+                    a way to verify the host of the server they are talking to. This is what the trustore does.
+                    The keystore contains one or more trusted host certificates or certificate authorities.
+                    You can create this truststore by extracting the public certificate of the IDP's SSL keystore.
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="truststorePassword" type="xs:string" use="optional">
+            <xs:annotation>
+                <xs:documentation>Password for the truststore keystore.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+
+</xs:schema>
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
index 0221af6..5ed1306 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
@@ -53,13 +53,17 @@ public class AuthzClient {
     }
 
     public static AuthzClient create(Configuration configuration) {
-        return new AuthzClient(configuration);
+        return new AuthzClient(configuration, configuration.getClientAuthenticator());
+    }
+
+    public static AuthzClient create(Configuration configuration, ClientAuthenticator authenticator) {
+        return new AuthzClient(configuration, authenticator);
     }
 
     private final ServerConfiguration serverConfiguration;
     private final Configuration deployment;
 
-    private AuthzClient(Configuration configuration) {
+    private AuthzClient(Configuration configuration, ClientAuthenticator authenticator) {
         if (configuration == null) {
             throw new IllegalArgumentException("Client configuration can not be null.");
         }
@@ -72,7 +76,9 @@ public class AuthzClient {
 
         configurationUrl += "/realms/" + configuration.getRealm() + "/.well-known/uma-configuration";
 
-        this.http = new Http(configuration);
+        this.deployment = configuration;
+
+        this.http = new Http(configuration, authenticator != null ? authenticator : configuration.getClientAuthenticator());
 
         try {
             this.serverConfiguration = this.http.<ServerConfiguration>get(URI.create(configurationUrl))
@@ -83,8 +89,10 @@ public class AuthzClient {
         }
 
         this.http.setServerConfiguration(this.serverConfiguration);
+    }
 
-        this.deployment = configuration;
+    private AuthzClient(Configuration configuration) {
+        this(configuration, null);
     }
 
     public ProtectionResource protection() {
@@ -106,7 +114,7 @@ public class AuthzClient {
     public AccessTokenResponse obtainAccessToken() {
         return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
                 .authentication()
-                    .oauth2ClientCredentials()
+                    .client()
                 .response()
                     .json(AccessTokenResponse.class)
                 .execute();
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java
new file mode 100644
index 0000000..73bcd9f
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java
@@ -0,0 +1,45 @@
+/*
+ * 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.Map;
+
+import org.keycloak.AuthorizationContext;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientAuthorizationContext extends AuthorizationContext {
+
+    private final AuthzClient client;
+
+    public ClientAuthorizationContext(AccessToken authzToken, Map<String, PolicyEnforcerConfig.PathConfig> paths, AuthzClient client) {
+        super(authzToken, paths);
+        this.client = client;
+    }
+
+    public ClientAuthorizationContext(AuthzClient client) {
+        this.client = client;
+    }
+
+    public AuthzClient getClient() {
+        return client;
+    }
+}
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
index 536b188..fc24f0e 100644
--- 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
@@ -48,7 +48,7 @@ public class ProtectionResource {
     public TokenIntrospectionResponse introspectRequestingPartyToken(String rpt) {
         return this.http.<TokenIntrospectionResponse>post("/protocol/openid-connect/token/introspect")
                 .authentication()
-                    .oauth2ClientCredentials()
+                    .client()
                 .form()
                     .param("token_type_hint", "requesting_party_token")
                     .param("token", rpt)
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
index 8bd9c07..f72e6b7 100644
--- 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
@@ -18,6 +18,7 @@
 package org.keycloak.authorization.client.util;
 
 import org.apache.http.client.methods.RequestBuilder;
+import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 
@@ -29,10 +30,12 @@ import java.net.URI;
 public class Http {
 
     private final Configuration configuration;
+    private final ClientAuthenticator authenticator;
     private ServerConfiguration serverConfiguration;
 
-    public Http(Configuration configuration) {
+    public Http(Configuration configuration, ClientAuthenticator authenticator) {
         this.configuration = configuration;
+        this.authenticator = authenticator;
     }
 
     public <R> HttpMethod<R> get(String path) {
@@ -60,7 +63,7 @@ public class Http {
     }
 
     private <R> HttpMethod<R> method(RequestBuilder builder) {
-        return new HttpMethod(this.configuration, builder);
+        return new HttpMethod(this.configuration, authenticator, builder);
     }
 
     public void setServerConfiguration(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
index 10d450e..9a7e51a 100644
--- 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
@@ -17,6 +17,12 @@
  */
 package org.keycloak.authorization.client.util;
 
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
@@ -27,33 +33,30 @@ 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.ClientAuthenticator;
 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 ClientAuthenticator authenticator;
     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<String, String>(), new HashMap<String, String>());
+    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder) {
+        this(configuration, authenticator, builder, new HashMap<String, String>(), new HashMap<String, String>());
     }
 
-    public HttpMethod(Configuration configuration, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
+    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
         this.configuration = configuration;
         this.httpClient = configuration.getHttpClient();
+        this.authenticator = authenticator;
         this.builder = builder;
         this.params = params;
         this.headers = headers;
@@ -121,7 +124,7 @@ public class HttpMethod<R> {
     }
 
     public HttpMethodAuthenticator<R> authentication() {
-        return new HttpMethodAuthenticator<R>(this);
+        return new HttpMethodAuthenticator<R>(this, authenticator);
     }
 
     public HttpMethod<R> param(String name, String value) {
@@ -136,7 +139,7 @@ public class HttpMethod<R> {
     }
 
     public HttpMethod<R> form() {
-        return new HttpMethod<R>(this.configuration, this.builder, this.params, this.headers) {
+        return new HttpMethod<R>(this.configuration, authenticator, this.builder, this.params, this.headers) {
             @Override
             protected void preExecute(RequestBuilder builder) {
                 if (params != null) {
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
index d67090a..8807d39 100644
--- 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
@@ -18,6 +18,7 @@
 package org.keycloak.authorization.client.util;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authorization.client.ClientAuthenticator;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -25,26 +26,24 @@ import org.keycloak.OAuth2Constants;
 public class HttpMethodAuthenticator<R> {
 
     private final HttpMethod<R> method;
+    private final ClientAuthenticator authenticator;
 
-    public HttpMethodAuthenticator(HttpMethod<R> method) {
+    public HttpMethodAuthenticator(HttpMethod<R> method, ClientAuthenticator authenticator) {
         this.method = method;
+        this.authenticator = authenticator;
     }
 
-    public HttpMethod<R> oauth2ClientCredentials() {
+    public HttpMethod<R> client() {
         this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS);
-        configureClientCredentials();
+        authenticator.configureClientCredentials(this.method.params, this.method.headers);
         return this.method;
     }
 
     public HttpMethod<R> oauth2ResourceOwnerPassword(String userName, String password) {
+        client();
         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/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
index 73443d1..0ed8763 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
@@ -23,18 +23,17 @@ import java.util.List;
 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.representations.idm.authorization.AggregatePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class AggregatePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
+public class AggregatePolicyProviderFactory implements PolicyProviderFactory<AggregatePolicyRepresentation> {
 
     private AggregatePolicyProvider provider = new AggregatePolicyProvider();
 
@@ -54,25 +53,35 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory<Pol
     }
 
     @Override
-    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
+    public PolicyProvider create(KeycloakSession session) {
         return null;
     }
 
     @Override
-    public PolicyProvider create(KeycloakSession session) {
-        return null;
+    public void onCreate(Policy policy, AggregatePolicyRepresentation representation, AuthorizationProvider authorization) {
+        verifyCircularReference(policy, new ArrayList<>());
     }
 
     @Override
-    public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+    public void onUpdate(Policy policy, AggregatePolicyRepresentation representation, AuthorizationProvider authorization) {
         verifyCircularReference(policy, new ArrayList<>());
     }
 
     @Override
-    public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+    public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
         verifyCircularReference(policy, new ArrayList<>());
     }
 
+    @Override
+    public AggregatePolicyRepresentation toRepresentation(Policy policy, AggregatePolicyRepresentation representation) {
+        return representation;
+    }
+
+    @Override
+    public Class<AggregatePolicyRepresentation> getRepresentationType() {
+        return AggregatePolicyRepresentation.class;
+    }
+
     private void verifyCircularReference(Policy policy, List<String> ids) {
         if (!policy.getType().equals("aggregate")) {
             return;
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
index 5c778d8..ffd92bf 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
@@ -1,6 +1,6 @@
 package org.keycloak.authorization.policy.provider.client;
 
-import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
+import java.util.function.Function;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
@@ -9,24 +9,29 @@ import org.keycloak.authorization.policy.evaluation.EvaluationContext;
 import org.keycloak.authorization.policy.provider.PolicyProvider;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
 
 public class ClientPolicyProvider implements PolicyProvider {
 
+    private final Function<Policy, ClientPolicyRepresentation> representationFunction;
+
+    public ClientPolicyProvider(Function<Policy, ClientPolicyRepresentation> representationFunction) {
+        this.representationFunction = representationFunction;
+    }
+
     @Override
     public void evaluate(Evaluation evaluation) {
-        Policy policy = evaluation.getPolicy();
-        EvaluationContext context = evaluation.getContext();
-        String[] clients = getClients(policy);
+        ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
         AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
         RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
+        EvaluationContext context = evaluation.getContext();
+
+        for (String client : representation.getClients()) {
+            ClientModel clientModel = realm.getClientById(client);
 
-        if (clients.length > 0) {
-            for (String client : clients) {
-                ClientModel clientModel = realm.getClientById(client);
-                if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
-                    evaluation.grant();
-                    return;
-                }
+            if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
+                evaluation.grant();
+                return;
             }
         }
     }
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
index a0e7874..d061357 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
@@ -2,14 +2,17 @@ package org.keycloak.authorization.policy.provider.client;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 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.authorization.store.ResourceServerStore;
@@ -17,12 +20,15 @@ import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
 import org.keycloak.models.RealmModel.ClientRemovedEvent;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.util.JsonSerialization;
 
-public class ClientPolicyProviderFactory implements PolicyProviderFactory {
+public class ClientPolicyProviderFactory implements PolicyProviderFactory<ClientPolicyRepresentation> {
 
-    private ClientPolicyProvider provider = new ClientPolicyProvider();
+    private ClientPolicyProvider provider = new ClientPolicyProvider(policy -> toRepresentation(policy, new ClientPolicyRepresentation()));
 
     @Override
     public String getName() {
@@ -40,8 +46,29 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
     }
 
     @Override
-    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
-        return null;
+    public ClientPolicyRepresentation toRepresentation(Policy policy, ClientPolicyRepresentation representation) {
+        representation.setClients(new HashSet<>(Arrays.asList(getClients(policy))));
+        return representation;
+    }
+
+    @Override
+    public Class<ClientPolicyRepresentation> getRepresentationType() {
+        return ClientPolicyRepresentation.class;
+    }
+
+    @Override
+    public void onCreate(Policy policy, ClientPolicyRepresentation representation, AuthorizationProvider authorization) {
+        updateClients(policy, representation.getClients(), authorization);
+    }
+
+    @Override
+    public void onUpdate(Policy policy, ClientPolicyRepresentation representation, AuthorizationProvider authorization) {
+        updateClients(policy, representation.getClients(), authorization);
+    }
+
+    @Override
+    public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+        updateClients(policy, new HashSet<>(Arrays.asList(getClients(policy))), authorization);
     }
 
     @Override
@@ -101,7 +128,41 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
         return "client";
     }
 
-    static String[] getClients(Policy policy) {
+    private void updateClients(Policy policy, Set<String> clients, AuthorizationProvider authorization) {
+        RealmModel realm = authorization.getKeycloakSession().getContext().getRealm();
+
+        if (clients == null || clients.isEmpty()) {
+            throw new RuntimeException("No client provided.");
+        }
+
+        Set<String> updatedClients = new HashSet<>();
+
+        for (String id : clients) {
+            ClientModel client = realm.getClientByClientId(id);
+
+            if (client == null) {
+                client = realm.getClientById(id);
+            }
+
+            if (client == null) {
+                throw new RuntimeException("Error while updating policy [" + policy.getName()  + "]. Client [" + id + "] could not be found.");
+            }
+
+            updatedClients.add(client.getId());
+        }
+
+        try {
+            Map<String, String> config = policy.getConfig();
+
+            config.put("clients", JsonSerialization.writeValueAsString(updatedClients));
+
+            policy.setConfig(config);
+        } catch (IOException cause) {
+            throw new RuntimeException("Failed to serialize clients", cause);
+        }
+    }
+
+    private String[] getClients(Policy policy) {
         String clients = policy.getConfig().get("clients");
 
         if (clients != null) {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
index 617727f..1a1ed34 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
@@ -1,30 +1,27 @@
 package org.keycloak.authorization.policy.provider.js;
 
-import java.util.function.Supplier;
+import java.util.Map;
 
-import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
 
 import org.keycloak.Config;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Policy;
 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.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class JSPolicyProviderFactory implements PolicyProviderFactory {
+public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRepresentation> {
 
-    private JSPolicyProvider provider = new JSPolicyProvider(new Supplier<ScriptEngine>() {
-        @Override
-        public ScriptEngine get() {
-            return new ScriptEngineManager().getEngineByName("nashorn");
-        }
-    });
+    private static final String ENGINE = "nashorn";
+
+    private JSPolicyProvider provider = new JSPolicyProvider(() -> new ScriptEngineManager().getEngineByName(ENGINE));
 
     @Override
     public String getName() {
@@ -42,13 +39,40 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory {
     }
 
     @Override
-    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
+    public PolicyProvider create(KeycloakSession session) {
         return null;
     }
 
     @Override
-    public PolicyProvider create(KeycloakSession session) {
-        return null;
+    public JSPolicyRepresentation toRepresentation(Policy policy, JSPolicyRepresentation representation) {
+        representation.setCode(policy.getConfig().get("code"));
+        return representation;
+    }
+
+    @Override
+    public Class<JSPolicyRepresentation> getRepresentationType() {
+        return JSPolicyRepresentation.class;
+    }
+
+    @Override
+    public void onCreate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) {
+        updatePolicy(policy, representation.getCode());
+    }
+
+    @Override
+    public void onUpdate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) {
+        updatePolicy(policy, representation.getCode());
+    }
+
+    @Override
+    public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+        updatePolicy(policy, representation.getConfig().get("code"));
+    }
+
+    private void updatePolicy(Policy policy, String code) {
+        Map<String, String> config = policy.getConfig();
+        config.put("code", code);
+        policy.setConfig(config);
     }
 
     @Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
index 4aafedd..e88eed6 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
@@ -17,9 +17,9 @@
  */
 package org.keycloak.authorization.policy.provider.role;
 
-import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
-
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.identity.Identity;
@@ -29,43 +29,43 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class RolePolicyProvider implements PolicyProvider {
 
+    private final Function<Policy, RolePolicyRepresentation> representationFunction;
+
+    public RolePolicyProvider(Function<Policy, RolePolicyRepresentation> representationFunction) {
+        this.representationFunction = representationFunction;
+    }
+
     @Override
     public void evaluate(Evaluation evaluation) {
         Policy policy = evaluation.getPolicy();
-        Map<String, Object>[] roleIds = getRoles(policy);
+        Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy).getRoles();
         AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
         RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
+        Identity identity = evaluation.getContext().getIdentity();
 
-        if (roleIds.length > 0) {
-            Identity identity = evaluation.getContext().getIdentity();
+        for (RolePolicyRepresentation.RoleDefinition roleDefinition : roleIds) {
+            RoleModel role = realm.getRoleById(roleDefinition.getId());
 
-            for (Map<String, Object> current : roleIds) {
-                RoleModel role = realm.getRoleById((String) current.get("id"));
+            if (role != null) {
+                boolean hasRole = hasRole(identity, role, realm);
 
-                if (role != null) {
-                    boolean hasRole = hasRole(identity, role, realm);
-
-                    if (!hasRole && Boolean.valueOf(isRequired(current))) {
-                        evaluation.deny();
-                        return;
-                    } else if (hasRole) {
-                        evaluation.grant();
-                    }
+                if (!hasRole && roleDefinition.isRequired()) {
+                    evaluation.deny();
+                    return;
+                } else if (hasRole) {
+                    evaluation.grant();
                 }
             }
         }
     }
 
-    private boolean isRequired(Map<String, Object> current) {
-        return (boolean) current.getOrDefault("required", false);
-    }
-
     private boolean hasRole(Identity identity, RoleModel role, RealmModel realm) {
         String roleName = role.getName();
         if (role.isClientRole()) {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
index d5d3917..03ea156 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
@@ -23,7 +23,6 @@ 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.authorization.store.ResourceServerStore;
@@ -53,7 +52,7 @@ import java.util.Set;
  */
 public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
 
-    private RolePolicyProvider provider = new RolePolicyProvider();
+    private RolePolicyProvider provider = new RolePolicyProvider(policy -> toRepresentation(policy, new RolePolicyRepresentation()));
 
     @Override
     public String getName() {
@@ -71,19 +70,14 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
     }
 
     @Override
-    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
-        return null;
-    }
-
-    @Override
     public PolicyProvider create(KeycloakSession session) {
-        return new RolePolicyProvider();
+        return provider;
     }
 
     @Override
     public RolePolicyRepresentation toRepresentation(Policy policy, RolePolicyRepresentation representation) {
         try {
-            representation.setRoles(JsonSerialization.readValue(policy.getConfig().get("roles"), Set.class));
+            representation.setRoles(new HashSet<>(Arrays.asList(JsonSerialization.readValue(policy.getConfig().get("roles"), RolePolicyRepresentation.RoleDefinition[].class))));
         } catch (IOException cause) {
             throw new RuntimeException("Failed to deserialize roles", cause);
         }
@@ -119,65 +113,63 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
     }
 
     private void updateRoles(Policy policy, AuthorizationProvider authorization, Set<RolePolicyRepresentation.RoleDefinition> roles) {
-        try {
-            RealmModel realm = authorization.getRealm();
-            Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
-
-            if (roles != null) {
-                for (RolePolicyRepresentation.RoleDefinition definition : roles) {
-                    String roleName = definition.getId();
-                    String clientId = null;
-                    int clientIdSeparator = roleName.indexOf("/");
-
-                    if (clientIdSeparator != -1) {
-                        clientId = roleName.substring(0, clientIdSeparator);
-                        roleName = roleName.substring(clientIdSeparator + 1);
-                    }
-
-                    RoleModel role;
+        RealmModel realm = authorization.getRealm();
+        Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
 
-                    if (clientId == null) {
-                        role = realm.getRole(roleName);
-
-                        if (role == null) {
-                            role = realm.getRoleById(roleName);
-                        }
-                    } else {
-                        ClientModel client = realm.getClientByClientId(clientId);
+        if (roles != null) {
+            for (RolePolicyRepresentation.RoleDefinition definition : roles) {
+                String roleName = definition.getId();
+                String clientId = null;
+                int clientIdSeparator = roleName.indexOf("/");
+
+                if (clientIdSeparator != -1) {
+                    clientId = roleName.substring(0, clientIdSeparator);
+                    roleName = roleName.substring(clientIdSeparator + 1);
+                }
 
-                        if (client == null) {
-                            throw new RuntimeException("Client with id [" + clientId + "] not found.");
-                        }
+                RoleModel role;
 
-                        role = client.getRole(roleName);
-                    }
+                if (clientId == null) {
+                    role = realm.getRole(roleName);
 
-                    // fallback to find any client role with the given name
                     if (role == null) {
-                        String finalRoleName = roleName;
-                        role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
-                                .findFirst().orElse(null);
+                        role = realm.getRoleById(roleName);
                     }
+                } else {
+                    ClientModel client = realm.getClientByClientId(clientId);
 
-                    if (role == null) {
-                        throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
+                    if (client == null) {
+                        throw new RuntimeException("Client with id [" + clientId + "] not found.");
                     }
 
-                    definition.setId(role.getId());
+                    role = client.getRole(roleName);
+                }
 
-                    updatedRoles.add(definition);
+                // fallback to find any client role with the given name
+                if (role == null) {
+                    String finalRoleName = roleName;
+                    role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
+                            .findFirst().orElse(null);
                 }
-                try {
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
+
+                if (role == null) {
+                    throw new RuntimeException("Error while updating policy [" + policy.getName()  + "]. Role [" + roleName + "] could not be found.");
                 }
+
+                definition.setId(role.getId());
+
+                updatedRoles.add(definition);
             }
+        }
 
+        try {
             Map<String, String> config = policy.getConfig();
+
             config.put("roles", JsonSerialization.writeValueAsString(updatedRoles));
+
             policy.setConfig(config);
         } catch (IOException cause) {
-            throw new RuntimeException("Failed to deserialize roles", cause);
+            throw new RuntimeException("Failed to serialize roles", cause);
         }
     }
 
@@ -253,7 +245,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
         return "role";
     }
 
-    static Map<String, Object>[] getRoles(Policy policy) {
+    private Map<String, Object>[] getRoles(Policy policy) {
         String roles = policy.getConfig().get("roles");
 
         if (roles != null) {
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
index 7ce4c6e..6a383e3 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
@@ -33,21 +33,20 @@ public class TimePolicyProvider implements PolicyProvider {
     static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss";
 
     private final SimpleDateFormat dateFormat;
-    private final Date currentDate;
 
     public TimePolicyProvider() {
         this.dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
-        this.currentDate = new Date();
     }
 
     @Override
     public void evaluate(Evaluation evaluation) {
         Policy policy = evaluation.getPolicy();
+        Date actualDate = new Date();
 
         try {
             String notBefore = policy.getConfig().get("nbf");
             if (notBefore != null && !"".equals(notBefore)) {
-                if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) {
+                if (actualDate.before(this.dateFormat.parse(format(notBefore)))) {
                     evaluation.deny();
                     return;
                 }
@@ -55,17 +54,17 @@ public class TimePolicyProvider implements PolicyProvider {
 
             String notOnOrAfter = policy.getConfig().get("noa");
             if (notOnOrAfter != null && !"".equals(notOnOrAfter)) {
-                if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
+                if (actualDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
                     evaluation.deny();
                     return;
                 }
             }
 
-            if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth", policy)
-                    || isInvalid(Calendar.MONTH, "month", policy)
-                    || isInvalid(Calendar.YEAR, "year", policy)
-                    || isInvalid(Calendar.HOUR_OF_DAY, "hour", policy)
-                    || isInvalid(Calendar.MINUTE, "minute", policy)) {
+            if (isInvalid(actualDate, Calendar.DAY_OF_MONTH, "dayMonth", policy)
+                    || isInvalid(actualDate, Calendar.MONTH, "month", policy)
+                    || isInvalid(actualDate, Calendar.YEAR, "year", policy)
+                    || isInvalid(actualDate, Calendar.HOUR_OF_DAY, "hour", policy)
+                    || isInvalid(actualDate, Calendar.MINUTE, "minute", policy)) {
                 evaluation.deny();
                 return;
             }
@@ -76,10 +75,10 @@ public class TimePolicyProvider implements PolicyProvider {
         }
     }
 
-    private boolean isInvalid(int timeConstant, String configName, Policy policy) {
+    private boolean isInvalid(Date actualDate, int timeConstant, String configName, Policy policy) {
         Calendar calendar = Calendar.getInstance();
 
-        calendar.setTime(this.currentDate);
+        calendar.setTime(actualDate);
 
         int dateField = calendar.get(timeConstant);
 
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
index 920cf45..a3958b9 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
@@ -1,22 +1,22 @@
 package org.keycloak.authorization.policy.provider.time;
 
 import java.text.SimpleDateFormat;
+import java.util.Map;
 
 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.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class TimePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
+public class TimePolicyProviderFactory implements PolicyProviderFactory<TimePolicyRepresentation> {
 
     private TimePolicyProvider provider = new TimePolicyProvider();
 
@@ -36,45 +36,57 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory<PolicyRe
     }
 
     @Override
-    public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
-        return new TimePolicyAdminResource();
-    }
-
-    @Override
     public PolicyProvider create(KeycloakSession session) {
         return null;
     }
 
     @Override
-    public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
-        validateConfig(policy);
+    public void onCreate(Policy policy, TimePolicyRepresentation representation, AuthorizationProvider authorization) {
+        updatePolicy(policy, representation);
     }
 
-    private void validateConfig(Policy policy) {
-        String nbf = policy.getConfig().get("nbf");
-        String noa = policy.getConfig().get("noa");
+    @Override
+    public void onUpdate(Policy policy, TimePolicyRepresentation representation, AuthorizationProvider authorization) {
+        updatePolicy(policy, representation);
+    }
 
-        if (nbf != null && noa != null) {
-            validateFormat(nbf);
-            validateFormat(noa);
-        }
+    @Override
+    public void onRemove(Policy policy, AuthorizationProvider authorization) {
     }
 
     @Override
-    public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
-        validateConfig(policy);
+    public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+        policy.setConfig(representation.getConfig());
     }
 
     @Override
-    public void onRemove(Policy policy, AuthorizationProvider authorization) {
+    public Class<TimePolicyRepresentation> getRepresentationType() {
+        return TimePolicyRepresentation.class;
     }
 
-    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 + "]");
-        }
+    @Override
+    public TimePolicyRepresentation toRepresentation(Policy policy, TimePolicyRepresentation representation) {
+        Map<String, String> config = policy.getConfig();
+
+        representation.setDayMonth(config.get("dayMonth"));
+        representation.setDayMonthEnd(config.get("dayMonthEnd"));
+
+        representation.setMonth(config.get("month"));
+        representation.setMonthEnd(config.get("monthEnd"));
+
+        representation.setYear(config.get("year"));
+        representation.setYearEnd(config.get("yearEnd"));
+
+        representation.setHour(config.get("hour"));
+        representation.setHourEnd(config.get("hourEnd"));
+
+        representation.setMinute(config.get("minute"));
+        representation.setMinuteEnd(config.get("minuteEnd"));
+
+        representation.setNotBefore(config.get("nbf"));
+        representation.setNotOnOrAfter(config.get("noa"));
+
+        return representation;
     }
 
     @Override
@@ -96,4 +108,44 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory<PolicyRe
     public String getId() {
         return "time";
     }
+
+    private void updatePolicy(Policy policy, TimePolicyRepresentation representation) {
+        String nbf = representation.getNotBefore();
+        String noa = representation.getNotOnOrAfter();
+
+        if (nbf != null && noa != null) {
+            validateFormat(nbf);
+            validateFormat(noa);
+        }
+
+        Map<String, String> config = policy.getConfig();
+
+        config.compute("nbf", (s, s2) -> nbf != null ? nbf : null);
+        config.compute("noa", (s, s2) -> noa != null ? noa : null);
+
+        config.compute("dayMonth", (s, s2) -> representation.getDayMonth() != null ? representation.getDayMonth() : null);
+        config.compute("dayMonthEnd", (s, s2) -> representation.getDayMonthEnd() != null ? representation.getDayMonthEnd() : null);
+
+        config.compute("month", (s, s2) -> representation.getMonth() != null ? representation.getMonth() : null);
+        config.compute("monthEnd", (s, s2) -> representation.getMonthEnd() != null ? representation.getMonthEnd() : null);
+
+        config.compute("year", (s, s2) -> representation.getYear() != null ? representation.getYear() : null);
+        config.compute("yearEnd", (s, s2) -> representation.getYearEnd() != null ? representation.getYearEnd() : null);
+
+        config.compute("hour", (s, s2) -> representation.getHour() != null ? representation.getHour() : null);
+        config.compute("hourEnd", (s, s2) -> representation.getHourEnd() != null ? representation.getHourEnd() : null);
+
+        config.compute("minute", (s, s2) -> representation.getMinute() != null ? representation.getMinute() : null);
+        config.compute("minuteEnd", (s, s2) -> representation.getMinuteEnd() != null ? representation.getMinuteEnd() : null);
+
+        policy.setConfig(config);
+    }
+
+    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/user/UserPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
index 2f77106..f891257 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
@@ -17,30 +17,34 @@
  */
 package org.keycloak.authorization.policy.provider.user;
 
+import java.util.function.Function;
+
 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;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class UserPolicyProvider implements PolicyProvider {
 
+    private final Function<Policy, UserPolicyRepresentation> representationFunction;
+
+    public UserPolicyProvider(Function<Policy, UserPolicyRepresentation> representationFunction) {
+        this.representationFunction = representationFunction;
+    }
+
     @Override
     public void evaluate(Evaluation evaluation) {
-        Policy policy = evaluation.getPolicy();
         EvaluationContext context = evaluation.getContext();
-        String[] userIds = getUsers(policy);
-
-        if (userIds.length > 0) {
-            for (String userId : userIds) {
-                if (context.getIdentity().getId().equals(userId)) {
-                    evaluation.grant();
-                    break;
-                }
+        UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
+
+        for (String userId : representation.getUsers()) {
+            if (context.getIdentity().getId().equals(userId)) {
+                evaluation.grant();
+                break;
             }
         }
     }
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
index 0fd54df..e4bbaf9 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.keycloak.Config;
 import org.keycloak.authorization.AuthorizationProvider;
@@ -49,7 +50,7 @@ import org.keycloak.util.JsonSerialization;
  */
 public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
 
-    private UserPolicyProvider provider = new UserPolicyProvider();
+    private UserPolicyProvider provider = new UserPolicyProvider((Function<Policy, UserPolicyRepresentation>) policy -> toRepresentation(policy, new UserPolicyRepresentation()));
 
     @Override
     public String getName() {
@@ -110,42 +111,40 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
     }
 
     private void updateUsers(Policy policy, AuthorizationProvider authorization, Set<String> users) {
-        try {
-            KeycloakSession session = authorization.getKeycloakSession();
-            RealmModel realm = authorization.getRealm();
-            UserProvider userProvider = session.users();
-            Set<String> updatedUsers = new HashSet<>();
-
-            if (users != null) {
-                try {
-                    for (String userId : users) {
-                        UserModel user = null;
+        KeycloakSession session = authorization.getKeycloakSession();
+        RealmModel realm = authorization.getRealm();
+        UserProvider userProvider = session.users();
+        Set<String> updatedUsers = new HashSet<>();
 
-                        try {
-                            user = userProvider.getUserByUsername(userId, realm);
-                        } catch (Exception ignore) {
-                        }
+        if (users != null) {
+            for (String userId : users) {
+                UserModel user = null;
 
-                        if (user == null) {
-                            user = userProvider.getUserById(userId, realm);
-                        }
+                try {
+                    user = userProvider.getUserByUsername(userId, realm);
+                } catch (Exception ignore) {
+                }
 
-                        if (user == null) {
-                            throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
-                        }
+                if (user == null) {
+                    user = userProvider.getUserById(userId, realm);
+                }
 
-                        updatedUsers.add(user.getId());
-                    }
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
+                if (user == null) {
+                    throw new RuntimeException("Error while updating policy [" + policy.getName()  + "]. User [" + userId + "] could not be found.");
                 }
+
+                updatedUsers.add(user.getId());
             }
+        }
 
+        try {
             Map<String, String> config = policy.getConfig();
+
             config.put("users", JsonSerialization.writeValueAsString(updatedUsers));
+
             policy.setConfig(config);
         } catch (IOException cause) {
-            throw new RuntimeException("Failed to deserialize roles", cause);
+            throw new RuntimeException("Failed to serialize users", cause);
         }
     }
 
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
index a9bdbb0..ad0b722 100644
--- 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
@@ -17,13 +17,14 @@
 package org.keycloak.authorization.policy.provider.drools;
 
 import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
-import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
 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.MediaType;
 import javax.ws.rs.core.Response;
 
 /**
@@ -39,24 +40,24 @@ public class DroolsPolicyAdminResource implements PolicyProviderAdminService {
 
     @Path("/resolveModules")
     @POST
-    @Consumes("application/json")
+    @Consumes(MediaType.APPLICATION_JSON)
     @Produces("application/json")
-    public Response resolveModules(PolicyRepresentation policy) {
+    public Response resolveModules(RulePolicyRepresentation 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();
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response resolveSessions(RulePolicyRepresentation policy) {
+        return Response.ok(getContainer(policy).getKieSessionNamesInKieBase(policy.getModuleName())).build();
     }
 
-    private KieContainer getContainer(PolicyRepresentation policy) {
-        String groupId = policy.getConfig().get("mavenArtifactGroupId");
-        String artifactId = policy.getConfig().get("mavenArtifactId");
-        String version = policy.getConfig().get("mavenArtifactVersion");
+    private KieContainer getContainer(RulePolicyRepresentation policy) {
+        String groupId = policy.getArtifactGroupId();
+        String artifactId = policy.getArtifactId();
+        String version = policy.getArtifactVersion();
         return this.factory.getKieContainer(groupId, artifactId, version);
     }
 }
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
index 8bbed33..1fb3c1f 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
@@ -13,8 +13,8 @@ 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.representations.idm.authorization.AbstractPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
 import org.kie.api.KieServices;
 import org.kie.api.KieServices.Factory;
 import org.kie.api.runtime.KieContainer;
@@ -22,7 +22,7 @@ import org.kie.api.runtime.KieContainer;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
+public class DroolsPolicyProviderFactory implements PolicyProviderFactory<RulePolicyRepresentation> {
 
     private KieServices ks;
     private final Map<String, DroolsPolicy> containers = Collections.synchronizedMap(new HashMap<>());
@@ -61,12 +61,14 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
     }
 
     @Override
-    public void onCreate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
+    public void onCreate(Policy policy, RulePolicyRepresentation representation, AuthorizationProvider authorization) {
+        updateConfig(policy, representation);
         update(policy);
     }
 
     @Override
-    public void onUpdate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
+    public void onUpdate(Policy policy, RulePolicyRepresentation representation, AuthorizationProvider authorization) {
+        updateConfig(policy, representation);
         update(policy);
     }
 
@@ -81,6 +83,23 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
     }
 
     @Override
+    public RulePolicyRepresentation toRepresentation(Policy policy, RulePolicyRepresentation representation) {
+        representation.setArtifactGroupId(policy.getConfig().get("mavenArtifactGroupId"));
+        representation.setArtifactId(policy.getConfig().get("mavenArtifactId"));
+        representation.setArtifactVersion(policy.getConfig().get("mavenArtifactVersion"));
+        representation.setScannerPeriod(policy.getConfig().get("scannerPeriod"));
+        representation.setScannerPeriodUnit(policy.getConfig().get("scannerPeriodUnit"));
+        representation.setSessionName(policy.getConfig().get("sessionName"));
+        representation.setModuleName(policy.getConfig().get("moduleName"));
+        return representation;
+    }
+
+    @Override
+    public Class<RulePolicyRepresentation> getRepresentationType() {
+        return RulePolicyRepresentation.class;
+    }
+
+    @Override
     public void init(Config.Scope config) {
         this.ks = Factory.get();
     }
@@ -100,6 +119,20 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
         return "rules";
     }
 
+    private void updateConfig(Policy policy, RulePolicyRepresentation representation) {
+        Map<String, String> config = policy.getConfig();
+
+        config.put("mavenArtifactGroupId", representation.getArtifactGroupId());
+        config.put("mavenArtifactId", representation.getArtifactId());
+        config.put("mavenArtifactVersion", representation.getArtifactVersion());
+        config.put("scannerPeriod", representation.getScannerPeriod());
+        config.put("scannerPeriodUnit", representation.getScannerPeriodUnit());
+        config.put("sessionName", representation.getSessionName());
+        config.put("moduleName", representation.getModuleName());
+
+        policy.setConfig(config);
+    }
+
     void update(Policy policy) {
         remove(policy);
         this.containers.put(policy.getId(), new DroolsPolicy(this.ks, policy));
diff --git a/common/src/main/java/org/keycloak/common/util/KeyUtils.java b/common/src/main/java/org/keycloak/common/util/KeyUtils.java
index 4514e4f..37e2b2a 100644
--- a/common/src/main/java/org/keycloak/common/util/KeyUtils.java
+++ b/common/src/main/java/org/keycloak/common/util/KeyUtils.java
@@ -40,8 +40,8 @@ public class KeyUtils {
     private KeyUtils() {
     }
 
-    public static SecretKey loadSecretKey(String secret) {
-        return new SecretKeySpec(secret.getBytes(), "HmacSHA256");
+    public static SecretKey loadSecretKey(byte[] secret) {
+        return new SecretKeySpec(secret, "HmacSHA256");
     }
 
     public static KeyPair generateRsaKeyPair(int keysize) {
diff --git a/common/src/test/java/org/keycloak/common/util/KeyUtilsTest.java b/common/src/test/java/org/keycloak/common/util/KeyUtilsTest.java
new file mode 100644
index 0000000..5e0abf5
--- /dev/null
+++ b/common/src/test/java/org/keycloak/common/util/KeyUtilsTest.java
@@ -0,0 +1,24 @@
+package org.keycloak.common.util;
+
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class KeyUtilsTest {
+
+    @Test
+    public void loadSecretKey() throws Exception {
+        byte[] secretBytes = new byte[32];
+        ThreadLocalRandom.current().nextBytes(secretBytes);
+        SecretKeySpec expected = new SecretKeySpec(secretBytes, "HmacSHA256");
+        SecretKey actual = KeyUtils.loadSecretKey(secretBytes);
+        assertEquals(expected.getAlgorithm(), actual.getAlgorithm());
+        assertArrayEquals(expected.getEncoded(), actual.getEncoded());
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
index e0be800..ddac66d 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
@@ -88,6 +88,10 @@ public class AbstractPolicyRepresentation {
         return policies;
     }
 
+    public void setPolicies(Set<String> policies) {
+        this.policies = policies;
+    }
+
     public void addPolicy(String... id) {
         if (this.policies == null) {
             this.policies = new HashSet<>();
@@ -99,6 +103,10 @@ public class AbstractPolicyRepresentation {
         return resources;
     }
 
+    public void setResources(Set<String> resources) {
+        this.resources = resources;
+    }
+
     public void addResource(String id) {
         if (this.resources == null) {
             this.resources = new HashSet<>();
@@ -110,6 +118,10 @@ public class AbstractPolicyRepresentation {
         return scopes;
     }
 
+    public void setScopes(Set<String> scopes) {
+        this.scopes = scopes;
+    }
+
     public void addScope(String... id) {
         if (this.scopes == null) {
             this.scopes = new HashSet<>();
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AggregatePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AggregatePolicyRepresentation.java
new file mode 100644
index 0000000..6ac1238
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AggregatePolicyRepresentation.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.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyRepresentation extends AbstractPolicyRepresentation {
+
+    @Override
+    public String getType() {
+        return "aggregate";
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ClientPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ClientPolicyRepresentation.java
new file mode 100644
index 0000000..d922a8f
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ClientPolicyRepresentation.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.representations.idm.authorization;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientPolicyRepresentation extends AbstractPolicyRepresentation {
+
+    private Set<String> clients;
+
+    @Override
+    public String getType() {
+        return "client";
+    }
+
+    public Set<String> getClients() {
+        return clients;
+    }
+
+    public void setClients(Set<String> clients) {
+        this.clients = clients;
+    }
+
+    public void addClient(String... id) {
+        if (this.clients == null) {
+            this.clients = new HashSet<>();
+        }
+        this.clients.addAll(Arrays.asList(id));
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java
new file mode 100644
index 0000000..1321792
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.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.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyRepresentation extends AbstractPolicyRepresentation {
+
+    private String code;
+
+    @Override
+    public String getType() {
+        return "js";
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java
index 2a89710..a4f2973 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java
@@ -26,6 +26,11 @@ public class RolePolicyRepresentation extends AbstractPolicyRepresentation {
 
     private Set<RoleDefinition> roles;
 
+    @Override
+    public String getType() {
+        return "role";
+    }
+
     public Set<RoleDefinition> getRoles() {
         return roles;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/RulePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/RulePolicyRepresentation.java
new file mode 100644
index 0000000..f24ecd4
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/RulePolicyRepresentation.java
@@ -0,0 +1,92 @@
+/*
+ * 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.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RulePolicyRepresentation extends AbstractPolicyRepresentation {
+
+    private String artifactGroupId;
+    private String artifactId;
+    private String artifactVersion;
+    private String moduleName;
+    private String sessionName;
+    private String scannerPeriod;
+    private String scannerPeriodUnit;
+
+    @Override
+    public String getType() {
+        return "rules";
+    }
+
+    public String getArtifactGroupId() {
+        return artifactGroupId;
+    }
+
+    public void setArtifactGroupId(String artifactGroupId) {
+        this.artifactGroupId = artifactGroupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getArtifactVersion() {
+        return artifactVersion;
+    }
+
+    public void setArtifactVersion(String artifactVersion) {
+        this.artifactVersion = artifactVersion;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public void setModuleName(String moduleName) {
+        this.moduleName = moduleName;
+    }
+
+    public String getSessionName() {
+        return sessionName;
+    }
+
+    public void setSessionName(String sessionName) {
+        this.sessionName = sessionName;
+    }
+
+    public String getScannerPeriod() {
+        return scannerPeriod;
+    }
+
+    public void setScannerPeriod(String scannerPeriod) {
+        this.scannerPeriod = scannerPeriod;
+    }
+
+    public String getScannerPeriodUnit() {
+        return scannerPeriodUnit;
+    }
+
+    public void setScannerPeriodUnit(String scannerPeriodUnit) {
+        this.scannerPeriodUnit = scannerPeriodUnit;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java
new file mode 100644
index 0000000..8b8e66a
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java
@@ -0,0 +1,137 @@
+/*
+ * 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.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyRepresentation extends AbstractPolicyRepresentation {
+
+    private String notBefore;
+    private String notOnOrAfter;
+    private String dayMonth;
+    private String dayMonthEnd;
+    private String month;
+    private String monthEnd;
+    private String year;
+    private String yearEnd;
+    private String hour;
+    private String hourEnd;
+    private String minute;
+    private String minuteEnd;
+
+    @Override
+    public String getType() {
+        return "time";
+    }
+
+    public String getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(String notBefore) {
+        this.notBefore = notBefore;
+    }
+
+    public String getNotOnOrAfter() {
+        return notOnOrAfter;
+    }
+
+    public void setNotOnOrAfter(String notOnOrAfter) {
+        this.notOnOrAfter = notOnOrAfter;
+    }
+
+    public String getDayMonth() {
+        return dayMonth;
+    }
+
+    public void setDayMonth(String dayMonth) {
+        this.dayMonth = dayMonth;
+    }
+
+    public String getDayMonthEnd() {
+        return dayMonthEnd;
+    }
+
+    public void setDayMonthEnd(String dayMonthEnd) {
+        this.dayMonthEnd = dayMonthEnd;
+    }
+
+    public String getMonth() {
+        return month;
+    }
+
+    public void setMonth(String month) {
+        this.month = month;
+    }
+
+    public String getMonthEnd() {
+        return monthEnd;
+    }
+
+    public void setMonthEnd(String monthEnd) {
+        this.monthEnd = monthEnd;
+    }
+
+    public String getYear() {
+        return year;
+    }
+
+    public void setYear(String year) {
+        this.year = year;
+    }
+
+    public String getYearEnd() {
+        return yearEnd;
+    }
+
+    public void setYearEnd(String yearEnd) {
+        this.yearEnd = yearEnd;
+    }
+
+    public String getHour() {
+        return hour;
+    }
+
+    public void setHour(String hour) {
+        this.hour = hour;
+    }
+
+    public String getHourEnd() {
+        return hourEnd;
+    }
+
+    public void setHourEnd(String hourEnd) {
+        this.hourEnd = hourEnd;
+    }
+
+    public String getMinute() {
+        return minute;
+    }
+
+    public void setMinute(String minute) {
+        this.minute = minute;
+    }
+
+    public String getMinuteEnd() {
+        return minuteEnd;
+    }
+
+    public void setMinuteEnd(String minuteEnd) {
+        this.minuteEnd = minuteEnd;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java
index dc38e65..239e55f 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java
@@ -26,6 +26,11 @@ public class UserPolicyRepresentation extends AbstractPolicyRepresentation {
 
     private Set<String> users;
 
+    @Override
+    public String getType() {
+        return "user";
+    }
+
     public Set<String> getUsers() {
         return users;
     }
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml
index d2a8706..ab9bfa9 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml
@@ -15,71 +15,65 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<!--  See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
 <config>
-   <subsystems name="load-balancer">
-      <!-- Each subsystem to be included relative to the src/main/resources directory -->
-      <subsystem>logging.xml</subsystem>
-      <subsystem>io.xml</subsystem>
-      <subsystem supplement="domain">jmx.xml</subsystem>
-      <subsystem>naming.xml</subsystem>
-      <subsystem>remoting.xml</subsystem>
-      <subsystem>request-controller.xml</subsystem>
-      <subsystem>security.xml</subsystem>
-      <subsystem>security-manager.xml</subsystem>
-   </subsystems>
-   <subsystems name="auth-server-standalone">
-      <!-- Each subsystem to be included relative to the src/main/resources directory -->
-      <subsystem>logging.xml</subsystem>
-      <subsystem>bean-validation.xml</subsystem>
-      <subsystem supplement="default">keycloak-datasources.xml</subsystem>
-      <subsystem>ee.xml</subsystem>
-      <subsystem>ejb3.xml</subsystem>
-      <subsystem>io.xml</subsystem>
-      <subsystem>keycloak-infinispan.xml</subsystem>
-      <subsystem>jaxrs.xml</subsystem>
-      <subsystem>jca.xml</subsystem>
-      <subsystem>jdr.xml</subsystem>
-      <subsystem supplement="domain">jmx.xml</subsystem>
-      <subsystem>jpa.xml</subsystem>
-      <subsystem>jsf.xml</subsystem>
-      <subsystem>mail.xml</subsystem>
-      <subsystem>naming.xml</subsystem>
-      <subsystem>remoting.xml</subsystem>
-      <subsystem>request-controller.xml</subsystem>
-      <subsystem>security.xml</subsystem>
-      <subsystem>security-manager.xml</subsystem>
-      <subsystem>transactions.xml</subsystem>
-      <subsystem>undertow.xml</subsystem>
-      <subsystem>keycloak-server.xml</subsystem>
-   </subsystems>
-
-   <subsystems name="auth-server-clustered">
-      <!-- Each subsystem to be included relative to the src/main/resources directory -->
-      <subsystem>logging.xml</subsystem>
-      <subsystem>bean-validation.xml</subsystem>
-      <subsystem supplement="domain">keycloak-datasources.xml</subsystem>
-      <subsystem>ee.xml</subsystem>
-      <subsystem supplement="ha">ejb3.xml</subsystem>
-      <subsystem>io.xml</subsystem>
-      <subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
-      <subsystem>jaxrs.xml</subsystem>
-      <subsystem>jca.xml</subsystem>
-      <subsystem>jdr.xml</subsystem>
-      <subsystem>jgroups.xml</subsystem>
-      <subsystem supplement="domain">jmx.xml</subsystem>
-      <subsystem>jpa.xml</subsystem>
-      <subsystem>jsf.xml</subsystem>
-      <subsystem>mail.xml</subsystem>
-      <subsystem>mod_cluster.xml</subsystem>
-      <subsystem>naming.xml</subsystem>
-      <subsystem>remoting.xml</subsystem>
-      <subsystem>request-controller.xml</subsystem>
-      <subsystem>security.xml</subsystem>
-      <subsystem>security-manager.xml</subsystem>
-      <subsystem>transactions.xml</subsystem>
-      <subsystem supplement="ha">undertow.xml</subsystem>
-      <subsystem>keycloak-server.xml</subsystem>
-   </subsystems>
+    <subsystems name="auth-server-standalone">
+        <subsystem>logging.xml</subsystem>
+        <subsystem>bean-validation.xml</subsystem>
+        <subsystem>core-management.xml</subsystem>
+        <subsystem supplement="default">keycloak-datasources.xml</subsystem>
+        <subsystem>ee.xml</subsystem>
+        <subsystem>ejb3.xml</subsystem>
+        <subsystem>io.xml</subsystem>
+        <subsystem>keycloak-infinispan.xml</subsystem>
+        <subsystem>jaxrs.xml</subsystem>
+        <subsystem>jca.xml</subsystem>
+        <subsystem>jdr.xml</subsystem>
+        <subsystem supplement="domain">jmx.xml</subsystem>
+        <subsystem>jpa.xml</subsystem>
+        <subsystem>jsf.xml</subsystem>
+        <subsystem>mail.xml</subsystem>
+        <subsystem>naming.xml</subsystem>
+        <subsystem>remoting.xml</subsystem>
+        <subsystem>request-controller.xml</subsystem>
+        <subsystem supplement="domain-wildfly">elytron.xml</subsystem>
+        <subsystem>security.xml</subsystem>
+        <subsystem>security-manager.xml</subsystem>
+        <subsystem>transactions.xml</subsystem>
+        <subsystem>undertow.xml</subsystem>
+        <subsystem>keycloak-server.xml</subsystem>
+    </subsystems>
+    <subsystems name="auth-server-clustered">
+        <!-- Each subsystem to be included relative to the src/main/resources directory -->
+        <subsystem>logging.xml</subsystem>
+        <subsystem>bean-validation.xml</subsystem>
+        <subsystem>core-management.xml</subsystem>
+        <subsystem supplement="domain">keycloak-datasources.xml</subsystem>
+        <subsystem>ee.xml</subsystem>
+        <subsystem supplement="ha">ejb3.xml</subsystem>
+        <subsystem>io.xml</subsystem>
+        <subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
+        <subsystem>jaxrs.xml</subsystem>
+        <subsystem>jca.xml</subsystem>
+        <subsystem>jdr.xml</subsystem>
+        <subsystem>jgroups.xml</subsystem>
+        <subsystem supplement="domain">jmx.xml</subsystem>
+        <subsystem>jpa.xml</subsystem>
+        <subsystem>jsf.xml</subsystem>
+        <subsystem>mail.xml</subsystem>
+        <subsystem>mod_cluster.xml</subsystem>
+        <subsystem>naming.xml</subsystem>
+        <subsystem>remoting.xml</subsystem>
+        <subsystem>request-controller.xml</subsystem>
+        <subsystem supplement="domain-wildfly">elytron.xml</subsystem>
+        <subsystem>security.xml</subsystem>
+        <subsystem>security-manager.xml</subsystem>
+        <subsystem>transactions.xml</subsystem>
+        <subsystem supplement="ha">undertow.xml</subsystem>
+        <subsystem>keycloak-server.xml</subsystem>
+    </subsystems>
+    <subsystems name="load-balancer">
+        <subsystem>logging.xml</subsystem>
+        <subsystem>io.xml</subsystem>
+        <subsystem>undertow-load-balancer.xml</subsystem>
+    </subsystems>
 </config>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml
index e7b5885..5774706 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml
@@ -17,7 +17,7 @@
   ~ limitations under the License.
   -->
 
-<domain xmlns="urn:jboss:domain:4.0">
+<domain xmlns="urn:jboss:domain:5.0">
 
     <extensions>
         <?EXTENSIONS?>
@@ -60,31 +60,6 @@
          -->
         <profile name="load-balancer">
             <?SUBSYSTEMS socket-binding-group="load-balancer-sockets"?>
-            <subsystem xmlns="urn:jboss:domain:undertow:3.0">
-                <buffer-cache name="default"/>
-                <server name="default-server">
-                    <http-listener name="default" socket-binding="http" redirect-socket="https"/>
-                    <host name="default-host" alias="localhost">
-                        <location name="/" handler="lb-handler"/>
-                        <filter-ref name="server-header"/>
-                        <filter-ref name="x-powered-by-header"/>
-                    </host>
-                </server>
-                <servlet-container name="default">
-                    <jsp-config/>
-                    <websockets/>
-                </servlet-container>
-                <handlers>
-                    <reverse-proxy name="lb-handler">
-                        <host name="host1" outbound-socket-binding="remote-host1" scheme="ajp" path="/" instance-id="myroute1"/>
-                        <host name="host2" outbound-socket-binding="remote-host2" scheme="ajp" path="/" instance-id="myroute2"/>
-                    </reverse-proxy>
-                </handlers>
-                <filters>
-                    <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
-                    <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
-                </filters>
-            </subsystem>
         </profile>
     </profiles>
 
@@ -96,12 +71,8 @@
          These default configurations require the binding specification to be done in host.xml.
     -->
     <interfaces>
-        <interface name="management">
-            <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
-        </interface>
-        <interface name="public">
-            <inet-address value="${jboss.bind.address:127.0.0.1}"/>
-        </interface>
+        <interface name="management"/>
+        <interface name="public"/>
         <?INTERFACES?>
     </interfaces>
 
@@ -114,32 +85,25 @@
         </socket-binding-group>
         <!-- load-balancer-sockets should be removed in production systems and replaced with a better softare or hardare based one -->
         <socket-binding-group name="load-balancer-sockets" default-interface="public">
-            <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
-            <socket-binding name="http" port="${jboss.http.port:8080}"/>
-            <socket-binding name="https" port="${jboss.https.port:8443}"/>
-            <outbound-socket-binding name="remote-host1">
-                <remote-destination host="localhost" port="8159"/>
-            </outbound-socket-binding>
-            <outbound-socket-binding name="remote-host2">
-                <remote-destination host="localhost" port="8259"/>
-            </outbound-socket-binding>
+            <!-- Needed for server groups using the 'load-balancer' profile  -->
             <?SOCKET-BINDINGS?>
         </socket-binding-group>
     </socket-binding-groups>
 
     <server-groups>
-        <!-- load-balancer-group should be removed in production systems and replaced with a better softare or hardare based one -->
-        <server-group name="load-balancer-group" profile="load-balancer">
+        <server-group name="auth-server-group" profile="auth-server-clustered">
             <jvm name="default">
                 <heap size="64m" max-size="512m"/>
             </jvm>
-            <socket-binding-group ref="load-balancer-sockets"/>
+            <socket-binding-group ref="ha-sockets"/>
         </server-group>
-        <server-group name="auth-server-group" profile="auth-server-clustered">
+
+        <!-- load-balancer-group should be removed in production systems and replaced with a better softare or hardare based one -->
+        <server-group name="load-balancer-group" profile="load-balancer">
             <jvm name="default">
                 <heap size="64m" max-size="512m"/>
             </jvm>
-            <socket-binding-group ref="ha-sockets"/>
+            <socket-binding-group ref="load-balancer-sockets"/>
         </server-group>
     </server-groups>
 
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml
index a5c9afb..6a4dba4 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml
@@ -23,7 +23,7 @@
   via host-slave.xml
 -->
 
-<host name="master" xmlns="urn:jboss:domain:4.0">
+<host name="master" xmlns="urn:jboss:domain:5.0">
     <extensions>
         <?EXTENSIONS?>
     </extensions>
@@ -40,6 +40,11 @@
                 </authorization>
             </security-realm>
             <security-realm name="ApplicationRealm">
+                <server-identities>
+                    <ssl>
+                        <keystore path="application.keystore" relative-to="jboss.domain.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
+                    </ssl>
+                </server-identities>
                 <authentication>
                     <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
                     <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
@@ -54,8 +59,8 @@
                 <json-formatter name="json-formatter"/>
             </formatters>
             <handlers>
-                <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
-                <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+                <file-handler name="host-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.domain.data.dir"/>
+                <file-handler name="server-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
             </handlers>
             <logger log-boot="true" log-read-only="false" enabled="false">
                 <handlers>
@@ -72,7 +77,8 @@
             <native-interface security-realm="ManagementRealm">
                 <socket interface="management" port="${jboss.management.native.port:9999}"/>
             </native-interface>
-            <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
+            <http-interface security-realm="ManagementRealm">
+                <http-upgrade enabled="true" />
                 <socket interface="management" port="${jboss.management.http.port:9990}"/>
             </http-interface>
         </management-interfaces>
@@ -80,6 +86,8 @@
 
     <domain-controller>
         <local/>
+        <!-- Alternative remote domain controller configuration with a host and port -->
+        <!-- <remote protocol="remote" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm"/> -->
     </domain-controller>
 
     <interfaces>
@@ -99,6 +107,8 @@
             <heap size="64m" max-size="256m"/>
             <jvm-options>
                 <option value="-server"/>
+                <option value="-XX:MetaspaceSize=96m"/>
+                <option value="-XX:MaxMetaspaceSize=256m"/>
             </jvm-options>
         </jvm>
     </jvms>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml
index f5d89ee..095fcc4 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml
@@ -22,7 +22,7 @@
   is also started by this host controller file.  The other instance must be started
   via host-slave.xml
 -->
-<host name="master" xmlns="urn:jboss:domain:4.0">
+<host name="master" xmlns="urn:jboss:domain:5.0">
     <extensions>
         <?EXTENSIONS?>
     </extensions>
@@ -39,6 +39,11 @@
                 </authorization>
             </security-realm>
             <security-realm name="ApplicationRealm">
+                <server-identities>
+                    <ssl>
+                        <keystore path="application.keystore" relative-to="jboss.domain.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
+                    </ssl>
+                </server-identities>
                 <authentication>
                     <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
                     <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
@@ -53,8 +58,8 @@
                 <json-formatter name="json-formatter"/>
             </formatters>
             <handlers>
-                <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
-                <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+                <file-handler name="host-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.domain.data.dir"/>
+                <file-handler name="server-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
             </handlers>
             <logger log-boot="true" log-read-only="false" enabled="false">
                 <handlers>
@@ -71,7 +76,8 @@
             <native-interface security-realm="ManagementRealm">
                 <socket interface="management" port="${jboss.management.native.port:9999}"/>
             </native-interface>
-            <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
+            <http-interface security-realm="ManagementRealm">
+                <http-upgrade enabled="true" />
                 <socket interface="management" port="${jboss.management.http.port:9990}"/>
             </http-interface>
         </management-interfaces>
@@ -98,6 +104,8 @@
             <heap size="64m" max-size="256m"/>
             <jvm-options>
                 <option value="-server"/>
+                <option value="-XX:MetaspaceSize=96m"/>
+                <option value="-XX:MaxMetaspaceSize=256m"/>
             </jvm-options>
         </jvm>
     </jvms>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml
index f8695d7..99648c0 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml
@@ -17,7 +17,7 @@
   ~ limitations under the License.
   -->
 
-<host xmlns="urn:jboss:domain:4.0">
+<host xmlns="urn:jboss:domain:5.0">
     <extensions>
         <?EXTENSIONS?>
     </extensions>
@@ -27,7 +27,7 @@
             <security-realm name="ManagementRealm">
                 <server-identities>
                     <!-- Replace this with either a base64 password of your own, or use a vault with a vault expression -->
-                    <secret value="c2xhdmVfdXNlcl9wYXNzd29yZA=="/>
+                    <secret value="c2xhdmVfdXMzcl9wYXNzd29yZA=="/>
                 </server-identities>
 
                 <authentication>
@@ -39,6 +39,11 @@
                 </authorization>
             </security-realm>
             <security-realm name="ApplicationRealm">
+                <server-identities>
+                    <ssl>
+                        <keystore path="application.keystore" relative-to="jboss.domain.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
+                    </ssl>
+                </server-identities>
                 <authentication>
                     <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
                     <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
@@ -53,8 +58,8 @@
                 <json-formatter name="json-formatter"/>
             </formatters>
             <handlers>
-                <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
-                <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+                <file-handler name="host-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.domain.data.dir"/>
+                <file-handler name="server-file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
             </handlers>
             <logger log-boot="true" log-read-only="false" enabled="false">
                 <handlers>
@@ -69,15 +74,15 @@
         </audit-log>
         <management-interfaces>
             <native-interface security-realm="ManagementRealm">
-                <socket interface="management" port="${jboss.management.native.port:3456}"/>
+                <socket interface="management" port="${jboss.management.native.port:9999}"/>
             </native-interface>
         </management-interfaces>
     </management>
 
     <domain-controller>
-        <remote security-realm="ManagementRealm">
+        <remote username="$local" security-realm="ManagementRealm">
             <discovery-options>
-                <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address:127.0.0.1}" port="${jboss.domain.master.port:9999}"/>
+                <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}"/>
             </discovery-options>
         </remote>
     </domain-controller>
@@ -99,6 +104,8 @@
             <heap size="64m" max-size="256m"/>
             <jvm-options>
                 <option value="-server"/>
+                <option value="-XX:MetaspaceSize=96m"/>
+                <option value="-XX:MaxMetaspaceSize=256m"/>
             </jvm-options>
         </jvm>
     </jvms>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml
index ada31ff..67bc4cd 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml
@@ -19,6 +19,8 @@
 <!--  See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
 <config>
     <subsystems>
+        <subsystem>core-management.xml</subsystem>
         <subsystem>jmx.xml</subsystem>
+        <subsystem supplement="host">elytron.xml</subsystem>
     </subsystems>
 </config>
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
index 428ba07..a9a53f4 100644
--- 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
@@ -17,19 +17,18 @@
  */
 package org.keycloak.example.photoz.admin;
 
-import org.keycloak.example.photoz.entity.Album;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 import javax.inject.Inject;
 import javax.persistence.EntityManager;
 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;
+
+import org.keycloak.example.photoz.entity.Album;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -37,14 +36,9 @@ import java.util.List;
 @Path("/admin/album")
 public class AdminAlbumService {
 
-    public static final String SCOPE_ADMIN_ALBUM_MANAGE = "urn:photoz.com:scopes:album:admin:manage";
-
     @Inject
     private EntityManager entityManager;
 
-    @Context
-    private HttpHeaders headers;
-
     @GET
     @Produces("application/json")
     public Response findAll() {
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 7dd6b24..129a11a 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -1,15 +1,14 @@
 package org.keycloak.example.photoz.album;
 
+import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.ClientAuthorizationContext;
 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 org.keycloak.example.photoz.util.Transaction;
-import org.keycloak.representations.adapters.config.AdapterConfig;
-import org.keycloak.util.JsonSerialization;
 
 import javax.inject.Inject;
 import javax.persistence.EntityManager;
@@ -35,7 +34,6 @@ import java.util.Set;
 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";
 
     @Inject
@@ -44,12 +42,6 @@ public class AlbumService {
     @Context
     private HttpServletRequest request;
 
-    private AuthzClient authzClient;
-
-    public AlbumService() {
-
-    }
-
     @POST
     @Consumes("application/json")
     public Response create(Album newAlbum) {
@@ -142,17 +134,14 @@ public class AlbumService {
     }
 
     private AuthzClient getAuthzClient() {
-        if (this.authzClient == null) {
-            try {
-                AdapterConfig adapterConfig = JsonSerialization.readValue(this.request.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"), AdapterConfig.class);
-                Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), null);
-
-                this.authzClient = AuthzClient.create(configuration);
-            } catch (Exception e) {
-                throw new RuntimeException("Could not create authorization client.", e);
-            }
-        }
+        return getAuthorizationContext().getClient();
+    }
+
+    private ClientAuthorizationContext getAuthorizationContext() {
+        return ClientAuthorizationContext.class.cast(getKeycloakSecurityContext().getAuthorizationContext());
+    }
 
-        return this.authzClient;
+    private KeycloakSecurityContext getKeycloakSecurityContext() {
+        return KeycloakSecurityContext.class.cast(request.getAttribute(KeycloakSecurityContext.class.getName()));
     }
 }
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java
index c7b8185..54fe747 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java
@@ -30,7 +30,7 @@ public class LDAPStorageMapperSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePoliciesResource.java
new file mode 100644
index 0000000..67f5d31
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePoliciesResource.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.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface AggregatePoliciesResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(AggregatePolicyRepresentation representation);
+
+    @Path("{id}")
+    AggregatePolicyResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    AggregatePolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePolicyResource.java
new file mode 100644
index 0000000..fc49314
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AggregatePolicyResource.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.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface AggregatePolicyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    AggregatePolicyRepresentation toRepresentation();
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    void update(AggregatePolicyRepresentation representation);
+
+    @DELETE
+    void remove();
+
+    @Path("/associatedPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> associatedPolicies();
+
+    @Path("/dependentPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> dependentPolicies();
+
+    @Path("/resources")
+    @GET
+    @Produces("application/json")
+    @NoCache
+    List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPoliciesResource.java
new file mode 100644
index 0000000..3fd7778
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPoliciesResource.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.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ClientPoliciesResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(ClientPolicyRepresentation representation);
+
+    @Path("{id}")
+    ClientPolicyResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    ClientPolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPolicyResource.java
new file mode 100644
index 0000000..a944935
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientPolicyResource.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.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ClientPolicyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    ClientPolicyRepresentation toRepresentation();
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    void update(ClientPolicyRepresentation representation);
+
+    @DELETE
+    void remove();
+
+    @Path("/associatedPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> associatedPolicies();
+
+    @Path("/dependentPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> dependentPolicies();
+
+    @Path("/resources")
+    @GET
+    @Produces("application/json")
+    @NoCache
+    List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java
new file mode 100644
index 0000000..a6788eb
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.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.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface JSPoliciesResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(JSPolicyRepresentation representation);
+
+    @Path("{id}")
+    JSPolicyResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    JSPolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java
new file mode 100644
index 0000000..433d71e
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java
@@ -0,0 +1,70 @@
+/*
+ * 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.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface JSPolicyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    JSPolicyRepresentation toRepresentation();
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    void update(JSPolicyRepresentation representation);
+
+    @DELETE
+    void remove();
+
+    @Path("/associatedPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> associatedPolicies();
+
+    @Path("/dependentPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> dependentPolicies();
+
+    @Path("/resources")
+    @GET
+    @Produces("application/json")
+    @NoCache
+    List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
index 433a112..a0af5d4 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
@@ -70,8 +70,23 @@ public interface PoliciesResource {
     PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
 
     @Path("role")
-    RolePoliciesResource roles();
+    RolePoliciesResource role();
 
     @Path("user")
-    UserPoliciesResource users();
+    UserPoliciesResource user();
+
+    @Path("js")
+    JSPoliciesResource js();
+
+    @Path("time")
+    TimePoliciesResource time();
+
+    @Path("aggregate")
+    AggregatePoliciesResource aggregate();
+
+    @Path("rules")
+    RulePoliciesResource rule();
+
+    @Path("client")
+    ClientPoliciesResource client();
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePoliciesResource.java
new file mode 100644
index 0000000..6e05432
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePoliciesResource.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.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RulePoliciesResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(RulePolicyRepresentation representation);
+
+    @Path("{id}")
+    RulePolicyResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    RulePolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePolicyResource.java
new file mode 100644
index 0000000..9efe30b
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RulePolicyResource.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.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RulePolicyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    RulePolicyRepresentation toRepresentation();
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    void update(RulePolicyRepresentation representation);
+
+    @DELETE
+    void remove();
+
+    @Path("/associatedPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> associatedPolicies();
+
+    @Path("/dependentPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> dependentPolicies();
+
+    @Path("/resources")
+    @GET
+    @Produces("application/json")
+    @NoCache
+    List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java
new file mode 100644
index 0000000..326bebe
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.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.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TimePoliciesResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(TimePolicyRepresentation representation);
+
+    @Path("{id}")
+    TimePolicyResource findById(@PathParam("id") String id);
+
+    @Path("/search")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    TimePolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java
new file mode 100644
index 0000000..560c06a
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.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.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface TimePolicyResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    TimePolicyRepresentation toRepresentation();
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    void update(TimePolicyRepresentation representation);
+
+    @DELETE
+    void remove();
+
+    @Path("/associatedPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> associatedPolicies();
+
+    @Path("/dependentPolicies")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    List<PolicyRepresentation> dependentPolicies();
+
+    @Path("/resources")
+    @GET
+    @Produces("application/json")
+    @NoCache
+    List<ResourceRepresentation> resources();
+
+}
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
index d89389f..4adadb6 100644
--- 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
@@ -30,7 +30,7 @@ public class JpaEntitySpi implements Spi {
 
 	@Override
 	public boolean isInternal() {
-		return false;
+		return true;
 	}
 
 	@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
index c2b7b02..a29ab69 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
@@ -45,6 +45,7 @@ import java.util.Collection;
         @NamedQuery(name="userConsentByUserAndClient", query="select consent from UserConsentEntity consent where consent.user.id = :userId and consent.clientId = :clientId"),
         @NamedQuery(name="userConsentsByUser", query="select consent from UserConsentEntity consent where consent.user.id = :userId"),
         @NamedQuery(name="deleteUserConsentsByRealm", query="delete from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId)"),
+        @NamedQuery(name="deleteUserConsentsByRealmAndLink", query="delete from UserConsentEntity consent where consent.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
         @NamedQuery(name="deleteUserConsentsByUser", query="delete from UserConsentEntity consent where consent.user = :user"),
         @NamedQuery(name="deleteUserConsentsByClient", query="delete from UserConsentEntity consent where consent.clientId = :clientId"),
 })
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java
index bcf5890..4c0dd5d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java
@@ -36,6 +36,7 @@ import java.io.Serializable;
         @NamedQuery(name="deleteUserConsentProtMappersByRealm", query=
                 "delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"),
         @NamedQuery(name="deleteUserConsentProtMappersByUser", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"),
+        @NamedQuery(name="deleteUserConsentProtMappersByRealmAndLink", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
         @NamedQuery(name="deleteUserConsentProtMappersByProtocolMapper", query="delete from UserConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId)"),
         @NamedQuery(name="deleteUserConsentProtMappersByClient", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId))"),
 })
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java
index 8ae6252..95d5f3e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentRoleEntity.java
@@ -34,6 +34,7 @@ import java.io.Serializable;
  */
 @NamedQueries({
         @NamedQuery(name="deleteUserConsentRolesByRealm", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId))"),
+        @NamedQuery(name="deleteUserConsentRolesByRealmAndLink", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
         @NamedQuery(name="deleteUserConsentRolesByUser", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"),
         @NamedQuery(name="deleteUserConsentRolesByRole", query="delete from UserConsentRoleEntity grantedRole where grantedRole.roleId = :roleId)"),
         @NamedQuery(name="deleteUserConsentRolesByClient", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId)"),
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 83bfac4..b9352c0 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
@@ -23,6 +23,7 @@ import org.keycloak.component.ComponentModel;
 import org.keycloak.credential.CredentialModel;
 import org.keycloak.credential.UserCredentialStore;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
@@ -284,9 +285,25 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
 
         Collection<UserConsentProtocolMapperEntity> grantedProtocolMapperEntities = entity.getGrantedProtocolMappers();
         if (grantedProtocolMapperEntities != null) {
+
+            ClientTemplateModel clientTemplate = null;
+            if (client.useTemplateMappers()) {
+                clientTemplate = client.getClientTemplate();
+            }
+
             for (UserConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) {
                 ProtocolMapperModel protocolMapper = client.getProtocolMapperById(grantedProtMapper.getProtocolMapperId());
-                model.addGrantedProtocolMapper(protocolMapper );
+
+                // Fallback to client template
+                if (protocolMapper == null) {
+                    if (clientTemplate != null) {
+                        protocolMapper = clientTemplate.getProtocolMapperById(grantedProtMapper.getProtocolMapperId());
+                    }
+                }
+
+                if (protocolMapper != null) {
+                    model.addGrantedProtocolMapper(protocolMapper);
+                }
             }
         }
 
@@ -410,6 +427,18 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
                 .setParameter("realmId", realm.getId())
                 .setParameter("link", storageProviderId)
                 .executeUpdate();
+        num = em.createNamedQuery("deleteUserConsentProtMappersByRealmAndLink")
+                .setParameter("realmId", realm.getId())
+                .setParameter("link", storageProviderId)
+                .executeUpdate();
+        num = em.createNamedQuery("deleteUserConsentRolesByRealmAndLink")
+                .setParameter("realmId", realm.getId())
+                .setParameter("link", storageProviderId)
+                .executeUpdate();
+        num = em.createNamedQuery("deleteUserConsentsByRealmAndLink")
+                .setParameter("realmId", realm.getId())
+                .setParameter("link", storageProviderId)
+                .executeUpdate();
         num = em.createNamedQuery("deleteUsersByRealmAndLink")
                 .setParameter("realmId", realm.getId())
                 .setParameter("link", storageProviderId)
diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
index ec4fc28..916f7f3 100755
--- a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
+++ b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
@@ -68,6 +68,11 @@ public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuil
         return this;
     }
 
+    public SAML2AuthnRequestBuilder assertionConsumerUrl(URI assertionConsumerUrl) {
+        this.authnRequestType.setAssertionConsumerServiceURL(assertionConsumerUrl);
+        return this;
+    }
+
     public SAML2AuthnRequestBuilder forceAuthn(boolean forceAuthn) {
         this.authnRequestType.setForceAuthn(forceAuthn);
         return this;
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java
index d8e6131..e018d97 100644
--- a/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java
@@ -28,7 +28,7 @@ public class ClientAuthenticatorSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java
index 94fc21d..1c1a46f 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java
@@ -28,7 +28,7 @@ public class FormActionSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
index 296f097..9ceac87 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
@@ -28,7 +28,7 @@ public class FormAuthenticatorSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java
index 537227d..7e03572 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java
@@ -28,7 +28,7 @@ public class RequiredActionSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java
index 85fdcce..8c32a86 100644
--- a/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java
@@ -29,7 +29,7 @@ public class IdentityProviderSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java
index 805aa7b..54a246d 100755
--- a/server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java
@@ -29,7 +29,7 @@ public class SocialProviderSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/credential/CredentialSpi.java b/server-spi-private/src/main/java/org/keycloak/credential/CredentialSpi.java
index c627750..beeac96 100644
--- a/server-spi-private/src/main/java/org/keycloak/credential/CredentialSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/credential/CredentialSpi.java
@@ -28,7 +28,7 @@ public class CredentialSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/PasswordHashSpi.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/PasswordHashSpi.java
index bdee135..1cf4758 100644
--- a/server-spi-private/src/main/java/org/keycloak/credential/hash/PasswordHashSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/PasswordHashSpi.java
@@ -28,7 +28,7 @@ public class PasswordHashSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java b/server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java
index 9b125f0..ddcce4d 100644
--- a/server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java
@@ -28,7 +28,7 @@ public class EventListenerSpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java
index 22da4f8..b405d42 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java
@@ -40,6 +40,7 @@ public class MigrateTo3_1_0 implements Migration {
 
                 Map<String, String> browserSecurityHeaders = new HashMap<>(realm.getBrowserSecurityHeaders());
                 browserSecurityHeaders.put("xRobotsTag", "none");
+                browserSecurityHeaders.put("xXSSProtection", "1; mode=block");
 
                 realm.setBrowserSecurityHeaders(Collections.unmodifiableMap(browserSecurityHeaders));
             }
diff --git a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
index f384f7b..40274fe 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
@@ -35,12 +35,14 @@ public class BrowserSecurityHeaders {
         headerMap.put("contentSecurityPolicy", "Content-Security-Policy");
         headerMap.put("xContentTypeOptions", "X-Content-Type-Options");
         headerMap.put("xRobotsTag", "X-Robots-Tag");
+        headerMap.put("xXSSProtection", "X-XSS-Protection");
 
         Map<String, String> dh = new HashMap<>();
         dh.put("xFrameOptions", "SAMEORIGIN");
         dh.put("contentSecurityPolicy", "frame-src 'self'");
         dh.put("xContentTypeOptions", "nosniff");
         dh.put("xRobotsTag", "none");
+        dh.put("xXSSProtection", "1; mode=block");
 
         defaultHeaders = Collections.unmodifiableMap(dh);
         headerAttributeMap = Collections.unmodifiableMap(headerMap);
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 695fe38..b454460 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -19,7 +19,6 @@ package org.keycloak.models.utils;
 
 import org.keycloak.broker.social.SocialIdentityProvider;
 import org.keycloak.broker.social.SocialIdentityProviderFactory;
-import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.CertificateUtils;
 import org.keycloak.common.util.KeyUtils;
 import org.keycloak.common.util.PemUtils;
@@ -75,14 +74,14 @@ public final class KeycloakModelUtils {
         return UUID.randomUUID().toString();
     }
 
-    public static String generateSecret() {
+    public static byte[] generateSecret() {
         return generateSecret(32);
     }
 
-    public static String generateSecret(int bytes) {
+    public static byte[] generateSecret(int bytes) {
         byte[] buf = new byte[bytes];
         new SecureRandom().nextBytes(buf);
-        return Base64Url.encode(buf);
+        return buf;
     }
 
     public static PublicKey getPublicKey(String publicKeyPem) {
diff --git a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java
index 97ad19a..201e96e 100644
--- a/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java
+++ b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java
@@ -28,7 +28,7 @@ public class PasswordPolicySpi implements Spi {
 
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
index f98f8fe..3d536dd 100755
--- a/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
+++ b/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -18,6 +18,7 @@
 package org.keycloak.services.managers;
 
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.Time;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
@@ -227,7 +228,7 @@ public class ClientSessionCode {
 
     private static String generateCode(ClientSessionModel clientSession) {
         try {
-            String actionId = KeycloakModelUtils.generateSecret();
+            String actionId = Base64Url.encode(KeycloakModelUtils.generateSecret());
 
             StringBuilder sb = new StringBuilder();
             sb.append(actionId);
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
index 80fb84a..665fe8f 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
@@ -65,9 +65,9 @@ public class AbstractPermissionService {
         return request.stream().map(request1 -> {
             String resourceSetId = request1.getResourceSetId();
             String resourceSetName = request1.getResourceSetName();
-            boolean resourceNotProvider = resourceSetId == null && resourceSetName == null;
+            boolean resourceNotProvided = resourceSetId == null && resourceSetName == null;
 
-            if (resourceNotProvider) {
+            if (resourceNotProvided) {
                 if ((request1.getScopes() == null || request1.getScopes().isEmpty())) {
                     throw new ErrorResponseException("invalid_resource_set_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
                 }
@@ -75,7 +75,7 @@ public class AbstractPermissionService {
 
             Resource resource = null;
 
-            if (!resourceNotProvider) {
+            if (!resourceNotProvided) {
                 if (resourceSetId != null) {
                     resource = storeFactory.getResourceStore().findById(resourceSetId, resourceServer.getId());
                 } else {
diff --git a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java
index cbd9035..a989ac3 100644
--- a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java
+++ b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.keys;
 
+import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.KeyUtils;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.jose.jws.AlgorithmType;
@@ -47,7 +48,7 @@ public class GeneratedHmacKeyProvider implements HmacKeyProvider {
         if (model.hasNote(SecretKey.class.getName())) {
             secretKey = model.getNote(SecretKey.class.getName());
         } else {
-            secretKey = KeyUtils.loadSecretKey(model.get(Attributes.SECRET_KEY));
+            secretKey = KeyUtils.loadSecretKey(Base64Url.decode(model.get(Attributes.SECRET_KEY)));
             model.setNote(SecretKey.class.getName(), secretKey);
         }
     }
diff --git a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java
index 7207eab..3a72517 100644
--- a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java
+++ b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java
@@ -81,8 +81,8 @@ public class GeneratedHmacKeyProviderFactory extends AbstractHmacKeyProviderFact
 
     private void generateSecret(ComponentModel model, int size) {
         try {
-            String secret = KeycloakModelUtils.generateSecret(size);
-            model.put(Attributes.SECRET_KEY, secret);
+            byte[] secret = KeycloakModelUtils.generateSecret(size);
+            model.put(Attributes.SECRET_KEY, Base64Url.encode(secret));
 
             String kid = KeycloakModelUtils.generateId();
             model.put(Attributes.KID_KEY, kid);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index d1750d2..20d86c0 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -267,9 +267,7 @@ public class SamlProtocol implements LoginProtocol {
 
         if (logoutPostUrl == null || logoutPostUrl.trim().isEmpty()) {
             // if we don't have a redirect uri either, return true and default to the admin url + POST binding
-            if (logoutRedirectUrl == null || logoutRedirectUrl.trim().isEmpty())
-                return true;
-            return false;
+            return (logoutRedirectUrl == null || logoutRedirectUrl.trim().isEmpty());
         }
 
         if (samlClient.forcePostBinding()) {
@@ -282,11 +280,8 @@ public class SamlProtocol implements LoginProtocol {
         if (SAML_POST_BINDING.equals(bindingType))
             return true;
 
-        if (logoutRedirectUrl == null || logoutRedirectUrl.trim().isEmpty())
-            return true; // we don't have a redirect binding url, so use post binding
-
-        return false; // redirect binding
-
+        // true if we don't have a redirect binding url, so use post binding, false for redirect binding
+        return (logoutRedirectUrl == null || logoutRedirectUrl.trim().isEmpty());
     }
 
     protected String getNameIdFormat(SamlClient samlClient, ClientSessionModel clientSession) {
@@ -529,15 +524,20 @@ public class SamlProtocol implements LoginProtocol {
         if (!(client instanceof ClientModel))
             return null;
         try {
-            if (isLogoutPostBindingForClient(clientSession)) {
-                String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
+            boolean postBinding = isLogoutPostBindingForClient(clientSession);
+            String bindingUri = getLogoutServiceUrl(uriInfo, client, postBinding ? SAML_POST_BINDING : SAML_REDIRECT_BINDING);
+            if (bindingUri == null) {
+                logger.warnf("Failed to logout client %s, skipping this client.  Please configure the logout service url in the admin console for your client applications.", client.getClientId());
+                return null;
+            }
+
+            if (postBinding) {
                 SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
                 // This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add <samlp:Extensions> element
                 JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
                 return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
             } else {
                 logger.debug("frontchannel redirect binding");
-                String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
                 SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
                 if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
                     KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
@@ -620,7 +620,7 @@ public class SamlProtocol implements LoginProtocol {
         SamlClient samlClient = new SamlClient(client);
         String logoutUrl = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
         if (logoutUrl == null) {
-            logger.warnv("Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: {1}", client.getClientId());
+            logger.warnf("Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: %s", client.getClientId());
             return;
         }
         SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(logoutUrl, clientSession, client);
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java
index 50a0699..5086be9 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java
@@ -27,7 +27,7 @@ import org.keycloak.provider.Spi;
 public class ClientRegistrationSpi implements Spi {
     @Override
     public boolean isInternal() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index bd28e20..0134229 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -359,7 +359,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
     }
 
     private boolean isInternal(ProviderFactory<?> factory) {
-        return factory.getClass().getPackage().getName().startsWith("org.keycloak");
+        String packageName = factory.getClass().getPackage().getName();
+        return packageName.startsWith("org.keycloak") && !packageName.startsWith("org.keycloak.examples");
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java
index f5a1b27..cc8abfb 100755
--- a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java
@@ -22,6 +22,7 @@ import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.AbstractOAuthClient;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.common.ClientConnection;
+import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.KeycloakUriBuilder;
 import org.keycloak.common.util.UriUtils;
 import org.keycloak.models.ClientModel;
@@ -133,7 +134,7 @@ public abstract class AbstractSecuredLocalService {
         if (cookie != null) {
             stateChecker = cookie.getValue();
         } else {
-            stateChecker = KeycloakModelUtils.generateSecret();
+            stateChecker = Base64Url.encode(KeycloakModelUtils.generateSecret());
             String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
             boolean secureOnly = realm.getSslRequired().isRequired(clientConnection);
             CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
index 645afaf..0e1d46e 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
@@ -296,7 +296,7 @@ public class AdminConsole {
             authUrl = authUrl.substring(0, authUrl.length() - 1);
 
             map.put("authUrl", authUrl);
-            map.put("consoleBaseUrl", Urls.adminConsoleRoot(baseUri, realm.getId()));
+            map.put("consoleBaseUrl", Urls.adminConsoleRoot(baseUri, realm.getName()));
             map.put("resourceUrl", Urls.themeRoot(baseUri) + "/admin/" + theme.getName());
             map.put("masterRealm", Config.getAdminRealm());
             map.put("resourceVersion", Version.RESOURCES_VERSION);
diff --git a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
index 48d2650..8710614 100755
--- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
@@ -19,6 +19,7 @@ package org.keycloak.services.resources;
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.common.ClientConnection;
+import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.MimeTypeUtil;
 import org.keycloak.models.BrowserSecurityHeaders;
 import org.keycloak.models.KeycloakSession;
@@ -246,7 +247,7 @@ public class WelcomeResource {
         if (stateChecker != null) {
             return stateChecker;
         } else {
-            stateChecker = KeycloakModelUtils.generateSecret();
+            stateChecker = Base64Url.encode(KeycloakModelUtils.generateSecret());
             String cookiePath = uriInfo.getPath();
             boolean secureOnly = uriInfo.getRequestUri().getScheme().equalsIgnoreCase("https");
             CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index 059c84d..21eb047 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -35,32 +35,32 @@ import java.net.URI;
  */
 public class Urls {
 
-    public static URI adminConsoleRoot(URI baseUri, String realmId) {
-        return UriBuilder.fromUri(baseUri).path(AdminRoot.class).path("{realm}/console/").build(realmId);
+    public static URI adminConsoleRoot(URI baseUri, String realmName) {
+        return UriBuilder.fromUri(baseUri).path(AdminRoot.class).path("{realm}/console/").build(realmName);
     }
 
-    public static URI accountApplicationsPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "applicationsPage").build(realmId);
+    public static URI accountApplicationsPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "applicationsPage").build(realmName);
     }
 
     public static UriBuilder accountBase(URI baseUri) {
         return realmBase(baseUri).path(RealmsResource.class, "getAccountService");
     }
 
-    public static URI accountPage(URI baseUri, String realmId) {
-        return accountPageBuilder(baseUri).build(realmId);
+    public static URI accountPage(URI baseUri, String realmName) {
+        return accountPageBuilder(baseUri).build(realmName);
     }
 
     public static UriBuilder accountPageBuilder(URI baseUri) {
         return accountBase(baseUri).path(AccountService.class, "accountPage");
     }
 
-    public static URI accountPasswordPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "passwordPage").build(realmId);
+    public static URI accountPasswordPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "passwordPage").build(realmName);
     }
 
-    public static URI accountFederatedIdentityPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "federatedIdentityPage").build(realmId);
+    public static URI accountFederatedIdentityPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "federatedIdentityPage").build(realmName);
     }
 
     public static URI accountFederatedIdentityUpdate(URI baseUri, String realmName) {
@@ -116,45 +116,45 @@ public class Urls {
                 .build(realmName);
     }
 
-    public static URI accountTotpPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "totpPage").build(realmId);
+    public static URI accountTotpPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "totpPage").build(realmName);
     }
 
-    public static URI accountTotpRemove(URI baseUri, String realmId, String stateChecker) {
+    public static URI accountTotpRemove(URI baseUri, String realmName, String stateChecker) {
         return accountBase(baseUri).path(AccountService.class, "processTotpRemove")
                 .queryParam("stateChecker", stateChecker)
-                .build(realmId);
+                .build(realmName);
     }
 
-    public static URI accountLogPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "logPage").build(realmId);
+    public static URI accountLogPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "logPage").build(realmName);
     }
 
-    public static URI accountSessionsPage(URI baseUri, String realmId) {
-        return accountBase(baseUri).path(AccountService.class, "sessionsPage").build(realmId);
+    public static URI accountSessionsPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountService.class, "sessionsPage").build(realmName);
     }
 
-    public static URI accountSessionsLogoutPage(URI baseUri, String realmId, String stateChecker) {
+    public static URI accountSessionsLogoutPage(URI baseUri, String realmName, String stateChecker) {
         return accountBase(baseUri).path(AccountService.class, "processSessionsLogout")
                 .queryParam("stateChecker", stateChecker)
-                .build(realmId);
+                .build(realmName);
     }
 
-    public static URI accountRevokeClientPage(URI baseUri, String realmId) {
+    public static URI accountRevokeClientPage(URI baseUri, String realmName) {
         return accountBase(baseUri).path(AccountService.class, "processRevokeGrant")
-                .build(realmId);
+                .build(realmName);
     }
 
-    public static URI accountLogout(URI baseUri, URI redirectUri, String realmId) {
-        return realmLogout(baseUri).queryParam("redirect_uri", redirectUri).build(realmId);
+    public static URI accountLogout(URI baseUri, URI redirectUri, String realmName) {
+        return realmLogout(baseUri).queryParam("redirect_uri", redirectUri).build(realmName);
     }
 
-    public static URI loginActionUpdatePassword(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "updatePassword").build(realmId);
+    public static URI loginActionUpdatePassword(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "updatePassword").build(realmName);
     }
 
-    public static URI loginActionUpdateTotp(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "updateTotp").build(realmId);
+    public static URI loginActionUpdateTotp(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "updateTotp").build(realmName);
     }
 
     public static UriBuilder requiredActionBase(URI baseUri) {
@@ -162,20 +162,20 @@ public class Urls {
     }
 
 
-    public static URI loginActionUpdateProfile(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "updateProfile").build(realmId);
+    public static URI loginActionUpdateProfile(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "updateProfile").build(realmName);
     }
 
-    public static URI loginActionEmailVerification(URI baseUri, String realmId) {
-        return loginActionEmailVerificationBuilder(baseUri).build(realmId);
+    public static URI loginActionEmailVerification(URI baseUri, String realmName) {
+        return loginActionEmailVerificationBuilder(baseUri).build(realmName);
     }
 
     public static UriBuilder loginActionEmailVerificationBuilder(URI baseUri) {
         return loginActionsBase(baseUri).path(LoginActionsService.class, "emailVerification");
     }
 
-    public static URI loginResetCredentials(URI baseUri, String realmId) {
-        return loginResetCredentialsBuilder(baseUri).build(realmId);
+    public static URI loginResetCredentials(URI baseUri, String realmName) {
+        return loginResetCredentialsBuilder(baseUri).build(realmName);
     }
 
     public static UriBuilder executeActionsBuilder(URI baseUri) {
@@ -186,44 +186,44 @@ public class Urls {
         return loginActionsBase(baseUri).path(LoginActionsService.RESET_CREDENTIALS_PATH);
     }
 
-    public static URI loginUsernameReminder(URI baseUri, String realmId) {
-        return loginUsernameReminderBuilder(baseUri).build(realmId);
+    public static URI loginUsernameReminder(URI baseUri, String realmName) {
+        return loginUsernameReminderBuilder(baseUri).build(realmName);
     }
 
     public static UriBuilder loginUsernameReminderBuilder(URI baseUri) {
         return loginActionsBase(baseUri).path(LoginActionsService.class, "usernameReminder");
     }
 
-    public static String realmIssuer(URI baseUri, String realmId) {
-        return realmBase(baseUri).path("{realm}").build(realmId).toString();
+    public static String realmIssuer(URI baseUri, String realmName) {
+        return realmBase(baseUri).path("{realm}").build(realmName).toString();
     }
 
     public static UriBuilder realmBase(URI baseUri) {
         return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
     }
 
-    public static URI realmLoginPage(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "authenticate").build(realmId);
+    public static URI realmLoginPage(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "authenticate").build(realmName);
     }
 
     private static UriBuilder realmLogout(URI baseUri) {
         return tokenBase(baseUri).path(OIDCLoginProtocolService.class, "logout");
     }
 
-    public static URI realmRegisterAction(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "processRegister").build(realmId);
+    public static URI realmRegisterAction(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "processRegister").build(realmName);
     }
 
-    public static URI realmRegisterPage(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "registerPage").build(realmId);
+    public static URI realmRegisterPage(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "registerPage").build(realmName);
     }
 
-    public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) {
-        return tokenBase(baseUri).path(OIDCLoginProtocolService.class, "installedAppUrnCallback").build(realmId);
+    public static URI realmInstalledAppUrnCallback(URI baseUri, String realmName) {
+        return tokenBase(baseUri).path(OIDCLoginProtocolService.class, "installedAppUrnCallback").build(realmName);
     }
 
-    public static URI realmOauthAction(URI baseUri, String realmId) {
-        return loginActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmId);
+    public static URI realmOauthAction(URI baseUri, String realmName) {
+        return loginActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmName);
     }
 
     public static URI firstBrokerLoginProcessor(URI baseUri, String realmName) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
index 3f8e854..a9546ed 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
@@ -63,6 +63,7 @@ import org.keycloak.testsuite.pages.AccountPasswordPage;
 import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.OAuthGrantPage;
 import org.keycloak.testsuite.pages.RegisterPage;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.LDAPRule;
@@ -153,6 +154,9 @@ public class LDAPProvidersIntegrationTest {
     @WebResource
     protected AccountPasswordPage changePasswordPage;
 
+    @WebResource
+    protected OAuthGrantPage grantPage;
+
 //    @Test
 //    @Ignore
 //    public void runit() throws Exception {
@@ -316,8 +320,18 @@ public class LDAPProvidersIntegrationTest {
     }
 
     @Test
-    public void deleteFederationLink() {
-        loginLdap();
+    public void deleteFederationLink() throws Exception {
+        // KEYCLOAK-4789: Login in client, which requires consent
+        oauth.clientId("third-party");
+        loginPage.open();
+        loginPage.login("johnkeycloak", "Password1");
+
+        grantPage.assertCurrent();
+        grantPage.accept();
+
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
         {
             KeycloakSession session = keycloakRule.startSession();
             try {
@@ -349,6 +363,9 @@ public class LDAPProvidersIntegrationTest {
                 keycloakRule.stopSession(session, true);
             }
         }
+
+        oauth.clientId("test-app");
+
         loginLdap();
 
     }
diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md
index edaf038..1c519b1 100644
--- a/testsuite/integration-arquillian/HOW-TO-RUN.md
+++ b/testsuite/integration-arquillian/HOW-TO-RUN.md
@@ -165,10 +165,10 @@ Assumed you downloaded `jboss-fuse-karaf-6.3.0.redhat-229.zip`
 ### DB migration test
 
 This test will:
- - start Keycloak 1.9.8
+ - start Keycloak 1.9.8 (replace with the other version if needed)
  - import realm and some data to MySQL DB
  - stop Keycloak 1.9.8
- - start latest KEycloak, which automatically updates DB from 1.9.8
+ - start latest Keycloak, which automatically updates DB from 1.9.8
  - Do some test that data are correct
  
 
@@ -191,7 +191,41 @@ This test will:
       -Dkeycloak.connectionsJpa.url=jdbc:mysql://$DB_HOST/keycloak \
       -Dkeycloak.connectionsJpa.user=keycloak \
       -Dkeycloak.connectionsJpa.password=keycloak
+      
+### DB migration test with manual mode
+      
+Same test as above, but it uses manual migration mode. During startup of the new Keycloak server, Liquibase won't automatically perform DB update, but it 
+just exports the needed SQL into the script. This SQL script then needs to be manually executed against the DB.
 
+1) Prepare MySQL DB (Same as above)
+
+2) Run the test (Update according to your DB connection, versions etc). This step will end with failure, but that's expected:
+
+    mvn -f testsuite/integration-arquillian/pom.xml \
+      clean install \
+      -Pauth-server-wildfly,jpa,clean-jpa,auth-server-migration \
+      -Dtest=MigrationTest \
+      -Dmigration.mode=manual \
+      -Dmigrated.auth.server.version=1.9.8.Final \
+      -Djdbc.mvn.groupId=mysql \
+      -Djdbc.mvn.version=5.1.29 \
+      -Djdbc.mvn.artifactId=mysql-connector-java \
+      -Dkeycloak.connectionsJpa.url=jdbc:mysql://$DB_HOST/keycloak \
+      -Dkeycloak.connectionsJpa.user=keycloak \
+      -Dkeycloak.connectionsJpa.password=keycloak
+      
+3) Manually execute the SQL script against your DB. With Mysql, you can use this command (KEYCLOAK_SRC points to the directory with the Keycloak codebase):
+       
+    mysql -h $DB_HOST -u keycloak -pkeycloak < $KEYCLOAK_SRC/testsuite/integration-arquillian/tests/base/target/containers/auth-server-wildfly/keycloak-database-update.sql       
+
+4) Finally run the migration test, which will verify that DB migration was successful. This should end with success:
+ 
+    mvn -f testsuite/integration-arquillian/tests/base/pom.xml \
+      clean install \
+      -Pauth-server-wildfly \
+      -Dskip.add.user.json=true \
+      -Dmigrated.auth.server.version=1.9.8.Final \
+      -Dtest=MigrationTest   
 
 ### JSON export/import migration test
 This will start latest Keycloak and import the realm JSON file, which was previously exported from Keycloak 1.9.8.Final
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
index b0aeb5d..bb21e50 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
@@ -112,14 +112,17 @@
     },
     {
       "clientId": "photoz-restful-api",
-      "secret": "secret",
       "enabled": true,
       "baseUrl": "/photoz-restful-api",
       "authorizationServicesEnabled" : true,
       "redirectUris": [
         "/photoz-restful-api/*"
       ],
-      "webOrigins" : ["*"]
+      "webOrigins" : ["*"],
+      "clientAuthenticatorType": "client-jwt",
+      "attributes" : {
+        "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg=="
+      }
     }
   ]
 }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index cd4fdba..a1230d8 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -1,6 +1,8 @@
 package org.keycloak.example.photoz.album;
 
+import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.authorization.client.Configuration;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ScopeRepresentation;
@@ -38,7 +40,6 @@ public class AlbumService {
     private static volatile long nextId = 0;
 
     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";
 
     @Inject
@@ -47,12 +48,6 @@ public class AlbumService {
     @Context
     private HttpServletRequest request;
 
-    private AuthzClient authzClient;
-
-    public AlbumService() {
-
-    }
-
     @POST
     @Consumes("application/json")
     public Response create(Album newAlbum, @QueryParam("user") String username) {
@@ -148,17 +143,14 @@ public class AlbumService {
     }
 
     private AuthzClient getAuthzClient() {
-        if (this.authzClient == null) {
-            try {
-                AdapterConfig adapterConfig = JsonSerialization.readValue(this.request.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"), AdapterConfig.class);
-                Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), null);
-
-                this.authzClient = AuthzClient.create(configuration);
-            } catch (Exception e) {
-                throw new RuntimeException("Could not create authorization client.", e);
-            }
-        }
+        return getAuthorizationContext().getClient();
+    }
+
+    private ClientAuthorizationContext getAuthorizationContext() {
+        return ClientAuthorizationContext.class.cast(getKeycloakSecurityContext().getAuthorizationContext());
+    }
 
-        return this.authzClient;
+    private KeycloakSecurityContext getKeycloakSecurityContext() {
+        return KeycloakSecurityContext.class.cast(request.getAttribute(KeycloakSecurityContext.class.getName()));
     }
-}
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks
new file mode 100644
index 0000000..399be7a
Binary files /dev/null and b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks differ
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index a3ac697..5b41d26 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -6,7 +6,14 @@
   "resource": "photoz-restful-api",
   "bearer-only" : true,
   "credentials": {
-    "secret": "secret"
+    "jwt": {
+      "client-key-password": "password",
+      "client-keystore-file": "classpath:keystore.jks",
+      "client-keystore-password": "password",
+      "client-key-alias": "secure-portal",
+      "token-timeout": 10,
+      "client-keystore-type": "jks"
+    }
   },
   "policy-enforcer": {
     "user-managed-access" : {},
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
index cef6f00..a5299bf 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
@@ -115,6 +115,11 @@
                     {
                         "name": "Pattern 11",
                         "typedScopes": []
+                    },
+                    {
+                        "name": "Pattern 12",
+                        "uri": "/realm_uri",
+                        "typedScopes": []
                     }
                 ],
                 "policies": [
@@ -256,6 +261,16 @@
                             "resources": "[\"Pattern 11\"]",
                             "applyPolicies": "[\"Default Policy\"]"
                         }
+                    },
+                    {
+                        "name": "Pattern 12 Permission",
+                        "type": "resource",
+                        "logic": "POSITIVE",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Pattern 12\"]",
+                            "applyPolicies": "[\"Default Policy\"]"
+                        }
                     }
                 ],
                 "scopes": []
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
index d8742d3..1dfcd7b 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
@@ -56,6 +56,10 @@
             {
                 "name": "Pattern 11",
                 "path": "/api/{version}/{resource}"
+            },
+            {
+                "name": "Pattern 12",
+                "path": "/keycloak_json_uri"
             }
         ]
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeAcsServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeAcsServlet.java
new file mode 100644
index 0000000..a1da272
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeAcsServlet.java
@@ -0,0 +1,39 @@
+/*
+ * 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 java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class EmployeeAcsServlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "employee-acs";
+
+    @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/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
index 6ca5135..e583608 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
@@ -178,7 +178,7 @@ public class AuthServerTestEnricher {
         }
     }
 
-    public void runPreMigrationTask(@Observes(precedence = 2) StartSuiteContainers event) {
+    public void runPreMigrationTask(@Observes(precedence = 2) StartSuiteContainers event) throws Exception {
         if (suiteContext.isAuthServerMigrationEnabled()) {
             log.info("\n\n### Run preMigration task on keycloak " + System.getProperty("migrated.auth.server.version", "- previous") + " ###\n\n");
             suiteContext.getMigrationContext().runPreMigrationTask();
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 3b4f48d..2f1f841 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
@@ -111,12 +111,15 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
                 if (authServerSslRequired) {
                     modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "8080", System.getProperty("auth.server.https.port"));
                     modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "http", "https");
+                    modifyDocElementAttribute(doc, "SingleSignOnService", "assertionConsumerServiceUrl", "8081", System.getProperty("app.server.http.port"));
+                    modifyDocElementAttribute(doc, "SingleSignOnService", "assertionConsumerServiceUrl", "http", "https");
                     modifyDocElementAttribute(doc, "SingleLogoutService", "postBindingUrl", "8080", System.getProperty("auth.server.https.port"));
                     modifyDocElementAttribute(doc, "SingleLogoutService", "postBindingUrl", "http", "https");
                     modifyDocElementAttribute(doc, "SingleLogoutService", "redirectBindingUrl", "8080", System.getProperty("auth.server.https.port"));
                     modifyDocElementAttribute(doc, "SingleLogoutService", "redirectBindingUrl", "http", "https");
                 } else {
                     modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "8080", System.getProperty("auth.server.http.port"));
+                    modifyDocElementAttribute(doc, "SingleSignOnService", "assertionConsumerServiceUrl", "8081", System.getProperty("app.server.http.port"));
                     modifyDocElementAttribute(doc, "SingleLogoutService", "postBindingUrl", "8080", System.getProperty("auth.server.http.port"));
                     modifyDocElementAttribute(doc, "SingleLogoutService", "redirectBindingUrl", "8080", System.getProperty("auth.server.http.port"));
                 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/migration/MigrationContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/migration/MigrationContext.java
index 18eea83..aa62f8a 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/migration/MigrationContext.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/migration/MigrationContext.java
@@ -17,8 +17,16 @@
 
 package org.keycloak.testsuite.arquillian.migration;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
 import org.jboss.logging.Logger;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.common.util.StreamUtil;
 import org.keycloak.testsuite.util.OAuthClient;
 
 /**
@@ -28,19 +36,36 @@ public class MigrationContext {
 
     public static final Logger logger = Logger.getLogger(MigrationContext.class);
 
-    private String offlineToken;
 
-    public String getOfflineToken() {
-        return offlineToken;
+    public String loadOfflineToken() throws Exception {
+        String file = getOfflineTokenLocation();
+        logger.infof("Reading previously saved offline token from the file: %s", file);
+
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            String offlineToken = StreamUtil.readString(fis);
+
+            File f = new File(file);
+            f.delete();
+            logger.infof("Deleted file with offline token: %s", file);
+
+            return offlineToken;
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
     }
 
 
     // Do some actions on the old container
-    public void runPreMigrationTask() {
-        requestOfflineToken();
+    public void runPreMigrationTask() throws Exception {
+        String offlineToken = requestOfflineToken();
+        saveOfflineToken(offlineToken);
     }
 
-    private void requestOfflineToken() {
+    private String requestOfflineToken() {
         logger.info("Requesting offline token on the old container");
         try {
             OAuthClient oauth = new OAuthClient();
@@ -49,10 +74,33 @@ public class MigrationContext {
             oauth.realm("Migration");
             oauth.clientId("migration-test-client");
             OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("b2c07929-69e3-44c6-8d7f-76939000b3e4", "migration-test-user", "admin");
-            offlineToken = tokenResponse.getRefreshToken();
+            return tokenResponse.getRefreshToken();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
+
+    private void saveOfflineToken(String offlineToken) throws Exception {
+        String file = getOfflineTokenLocation();
+        logger.infof("Saving offline token to file: %s", file);
+
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));
+            writer.print(offlineToken);
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+
+    // Needs to save offline token inside "basedir". There are issues with saving into directory "target" as it's cleared among restarts and
+    // using "mvn install" instead of "mvn clean install" doesn't work ATM. Improve if needed...
+    private String getOfflineTokenLocation() {
+        return System.getProperty("basedir") + "/offline-token.txt";
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/AbstractMultipleSelect2.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/AbstractMultipleSelect2.java
new file mode 100644
index 0000000..3c4dda8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/AbstractMultipleSelect2.java
@@ -0,0 +1,154 @@
+/*
+ * 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.console.page.fragment;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractMultipleSelect2<R> {
+
+    @Root
+    private WebElement root;
+
+    @Drone
+    private WebDriver driver;
+
+    @FindBy(xpath = ".//input[contains(@class,'select2-input')]")
+    private WebElement search;
+
+    @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+    private List<WebElement> result;
+
+    public void update(Set<R> values) {
+        Set<R> selection = getSelected();
+
+        for (R value : values) {
+            if (!selection.contains(value)) {
+                select(value);
+            }
+        }
+
+        for (R selected : selection) {
+            boolean isSelected = false;
+
+            for (R value : values) {
+                if (selected.equals(value)) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                deselect(selected);
+            }
+        }
+    }
+
+    public void select(R value) {
+        root.click();
+        WaitUtils.pause(500);
+
+        String id = identity().apply(value);
+
+        Actions actions = new Actions(driver);
+        actions.sendKeys(id).perform();
+        WaitUtils.pause(500);
+
+        if (result.isEmpty()) {
+            actions.sendKeys(Keys.ESCAPE).perform();
+            return;
+        }
+
+        for (WebElement result : result) {
+            if (result.getText().equalsIgnoreCase(id)) {
+                result.click();
+                return;
+            }
+        }
+    }
+
+    protected abstract Function<R, String> identity();
+
+    public Set<R> getSelected() {
+        Set<R> values = new HashSet<>();
+
+        for (WebElement selected : getSelectedElements()) {
+            R value = representation().apply(selected);
+
+            if (value != null) {
+                values.add(value);
+            }
+        }
+
+        return values;
+    }
+
+    protected abstract List<WebElement> getSelectedElements();
+
+    protected abstract Function<WebElement, R> representation();
+
+    public void deselect(R value) {
+        onDeselect(value);
+    }
+
+    protected void onDeselect(R value) {
+        for (WebElement selected : getSelectedElements()) {
+            if (deselect().apply(selected, value)) {
+                return;
+            }
+        }
+    }
+
+    protected BiFunction<WebElement, R, Boolean> deselect() {
+        return (selected, value) -> {
+            WebElement selection = selected.findElements(By.tagName("div")).get(0);
+            if (identity().apply(value).equals(selection.getText())) {
+                WebElement element = selected.findElement(By.xpath(".//a[contains(@class,'select2-search-choice-close')]"));
+                JavascriptExecutor executor = (JavascriptExecutor) driver;
+                executor.executeScript("arguments[0].click();", element);
+                WaitUtils.pause(500);
+                return true;
+            }
+            return false;
+        };
+    }
+
+    protected WebElement getRoot() {
+        return root;
+    }
+
+    protected WebDriver getDriver() {
+        return driver;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/MultipleStringSelect2.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/MultipleStringSelect2.java
new file mode 100644
index 0000000..89582c8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/MultipleStringSelect2.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 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.console.page.fragment;
+
+import java.util.List;
+import java.util.function.Function;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class MultipleStringSelect2 extends AbstractMultipleSelect2<String> {
+
+    @Override
+    protected Function<String, String> identity() {
+        return r -> r.toString();
+    }
+
+    @Override
+    protected List<WebElement> getSelectedElements() {
+        return getRoot().findElements(By.xpath(".//li[contains(@class,'select2-search-choice')]"));
+    }
+
+    @Override
+    protected Function<WebElement, String> representation() {
+        return webElement -> {
+            List<WebElement> element = webElement.findElements(By.tagName("div"));
+
+            if (element.isEmpty()) {
+                return null;
+            }
+
+            String value = element.get(0).getText();
+
+            return "".equals(value) ? null : value;
+        };
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/SingleStringSelect2.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/SingleStringSelect2.java
new file mode 100644
index 0000000..6621622
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/SingleStringSelect2.java
@@ -0,0 +1,64 @@
+/*
+ * 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.console.page.fragment;
+
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class SingleStringSelect2 extends AbstractMultipleSelect2<String> {
+
+    @Override
+    protected Function<String, String> identity() {
+        return r -> r.toString();
+    }
+
+    @Override
+    protected List<WebElement> getSelectedElements() {
+        return getRoot().findElements(By.xpath(".//span[contains(@class,'select2-chosen')]"));
+    }
+
+    @Override
+    protected Function<WebElement, String> representation() {
+        return webElement -> {
+            String value = webElement.getText();
+            return "".equals(value) ? null : value;
+        };
+    }
+
+    @Override
+    protected BiFunction<WebElement, String, Boolean> deselect() {
+        return (selected, value) -> {
+            if (identity().apply(value).equals(selected.getText())) {
+                WebElement element = selected.findElement(By.xpath(".//a[contains(@class,'select2-search-choice-close')]"));
+                JavascriptExecutor executor = (JavascriptExecutor) getDriver();
+                executor.executeScript("arguments[0].click();", element);
+                WaitUtils.pause(500);
+                return true;
+            }
+            return false;
+        };
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
index d61b077..5c3f1f1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
@@ -103,7 +103,7 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
         return getClientResource(RESOURCE_SERVER_ID).authorization();
     }
 
-    private ClientResource getClientResource(String clientId) {
+    protected ClientResource getClientResource(String clientId) {
         ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
         ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
         return clients.get(resourceServer.getId());
@@ -199,7 +199,7 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
 
         assertFalse(policy.getUsers().isEmpty());
 
-        getAuthorizationResource().policies().users().create(policy);
+        getAuthorizationResource().policies().user().create(policy);
     }
 
     protected interface ExceptionRunnable {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
index 3aef537..b37e9e6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
@@ -26,15 +26,19 @@ import java.util.List;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
-import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.ClientPoliciesResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RoleScopeResource;
+import org.keycloak.admin.client.resource.RolesResource;
 import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.admin.client.resource.UsersResource;
-import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
 import org.keycloak.testsuite.util.WaitUtils;
 
 /**
@@ -205,4 +209,99 @@ public abstract class AbstractServletAuthzFunctionalAdapterTest extends Abstract
             assertTrue(hasText("This is public resource that should be accessible without login."));
         });
     }
+
+    @Test
+    public void testRequiredRole() throws Exception {
+        performTests(() -> {
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertFalse(wasDenied());
+
+            RolesResource rolesResource = getClientResource(RESOURCE_SERVER_ID).roles();
+
+            rolesResource.create(new RoleRepresentation("required-role", "", false));
+
+            RolePolicyRepresentation policy = new RolePolicyRepresentation();
+
+            policy.setName("Required Role Policy");
+            policy.addRole("user_premium", false);
+            policy.addRole("required-role", false);
+
+            RolePoliciesResource rolePolicy = getAuthorizationResource().policies().role();
+
+            rolePolicy.create(policy);
+            policy = rolePolicy.findByName(policy.getName());
+
+            updatePermissionPolicies("Premium Resource Permission", policy.getName());
+
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertFalse(wasDenied());
+
+            policy.getRoles().clear();
+            policy.addRole("user_premium", false);
+            policy.addRole("required-role", true);
+
+            rolePolicy.findById(policy.getId()).update(policy);
+
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertTrue(wasDenied());
+
+            UsersResource users = realmsResouce().realm(REALM_NAME).users();
+            UserRepresentation user = users.search("jdoe").get(0);
+
+            RoleScopeResource roleScopeResource = users.get(user.getId()).roles().clientLevel(getClientResource(RESOURCE_SERVER_ID).toRepresentation().getId());
+            RoleRepresentation requiredRole = rolesResource.get("required-role").toRepresentation();
+            roleScopeResource.add(Arrays.asList(requiredRole));
+
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertFalse(wasDenied());
+
+            policy.getRoles().clear();
+            policy.addRole("user_premium", false);
+            policy.addRole("required-role", false);
+
+            rolePolicy.findById(policy.getId()).update(policy);
+
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertFalse(wasDenied());
+
+            roleScopeResource.remove(Arrays.asList(requiredRole));
+
+            login("jdoe", "jdoe");
+            navigateToUserPremiumPage();
+            assertFalse(wasDenied());
+        });
+    }
+
+    @Test
+    public void testOnlySpecificClient() throws Exception {
+        performTests(() -> {
+            login("jdoe", "jdoe");
+            assertFalse(wasDenied());
+
+            ClientPolicyRepresentation policy = new ClientPolicyRepresentation();
+
+            policy.setName("Only Client Policy");
+            policy.addClient("admin-cli");
+
+            ClientPoliciesResource policyResource = getAuthorizationResource().policies().client();
+            policyResource.create(policy);
+            policy = policyResource.findByName(policy.getName());
+
+            updatePermissionPolicies("Protected Resource Permission", policy.getName());
+
+            login("jdoe", "jdoe");
+            assertTrue(wasDenied());
+
+            policy.addClient("servlet-authz-app");
+            policyResource.findById(policy.getId()).update(policy);
+
+            login("jdoe", "jdoe");
+            assertFalse(wasDenied());
+        });
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
index aaeee4f..2661185 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
@@ -290,12 +290,14 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
             login("alice", "alice");
 
             navigateTo("/resource/a/i/b/c/d/e");
+            assertFalse(wasDenied());
             navigateTo("/resource/a/i/b/c/");
             assertFalse(wasDenied());
 
             updatePermissionPolicies("Pattern 10 Permission", "Deny Policy");
             login("alice", "alice");
             navigateTo("/resource/a/i/b/c/d/e");
+            assertTrue(wasDenied());
             navigateTo("/resource/a/i/b/c/d");
             assertTrue(wasDenied());
 
@@ -350,6 +352,34 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
         });
     }
 
+    @Test
+    public void testPriorityOfURIForResource() {
+        performTests(() -> {
+            login("alice", "alice");
+            navigateTo("/realm_uri");
+            assertTrue(wasDenied());
+            navigateTo("/keycloak_json_uri");
+            assertFalse(wasDenied());
+
+            updatePermissionPolicies("Pattern 12 Permission", "Deny Policy");
+
+            login("alice", "alice");
+            navigateTo("/realm_uri");
+            assertTrue(wasDenied());
+            navigateTo("/keycloak_json_uri");
+            assertTrue(wasDenied());
+
+            updatePermissionPolicies("Pattern 12 Permission", "Default Policy");
+
+            login("alice", "alice");
+            navigateTo("/realm_uri");
+            assertTrue(wasDenied());
+            navigateTo("/keycloak_json_uri");
+            assertFalse(wasDenied());
+        });
+    }
+
+
     private void navigateTo(String path) {
         this.driver.navigate().to(getResourceServerUrl() + path);
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
index d89fe67..ce92fb8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
@@ -18,6 +18,7 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
     public void checkRoles() {
         badClientSalesPostSigServletPage.checkRoles(true);
         badRealmSalesPostSigServletPage.checkRoles(true);
+        employeeAcsServletPage.checkRoles(true);
         employeeSigServletPage.checkRoles(true);
         employeeSigFrontServletPage.checkRoles(true);
         salesMetadataServletPage.checkRoles(true);
@@ -48,6 +49,7 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
     public void uncheckRoles() {
         badClientSalesPostSigServletPage.checkRoles(false);
         badRealmSalesPostSigServletPage.checkRoles(false);
+        employeeAcsServletPage.checkRoles(false);
         employee2ServletPage.checkRoles(false);
         employeeSigServletPage.checkRoles(false);
         employeeSigFrontServletPage.checkRoles(false);
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 a199636..2795a2d 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
@@ -57,36 +57,14 @@ import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
-import org.keycloak.testsuite.adapter.page.BadAssertionSalesPostSig;
-import org.keycloak.testsuite.adapter.page.BadClientSalesPostSigServlet;
-import org.keycloak.testsuite.adapter.page.BadRealmSalesPostSigServlet;
-import org.keycloak.testsuite.adapter.page.DifferentCookieNameServlet;
-import org.keycloak.testsuite.adapter.page.Employee2Servlet;
-import org.keycloak.testsuite.adapter.page.EmployeeServlet;
-import org.keycloak.testsuite.adapter.page.EmployeeSigFrontServlet;
-import org.keycloak.testsuite.adapter.page.EmployeeSigPostNoIdpKeyServlet;
-import org.keycloak.testsuite.adapter.page.EmployeeSigRedirNoIdpKeyServlet;
-import org.keycloak.testsuite.adapter.page.EmployeeSigRedirOptNoIdpKeyServlet;
-import org.keycloak.testsuite.adapter.page.EmployeeSigServlet;
-import org.keycloak.testsuite.adapter.page.InputPortal;
-import org.keycloak.testsuite.adapter.page.MissingAssertionSig;
-import org.keycloak.testsuite.adapter.page.SAMLServlet;
-import org.keycloak.testsuite.adapter.page.SalesMetadataServlet;
-import org.keycloak.testsuite.adapter.page.SalesPost2Servlet;
-import org.keycloak.testsuite.adapter.page.SalesPostAssertionAndResponseSig;
-import org.keycloak.testsuite.adapter.page.SalesPostEncServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostPassiveServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostSigEmailServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostSigPersistentServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostSigServlet;
-import org.keycloak.testsuite.adapter.page.SalesPostSigTransientServlet;
+import org.keycloak.testsuite.adapter.page.*;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.auth.page.login.Login;
 import org.keycloak.testsuite.auth.page.login.SAMLIDPInitiatedLogin;
 import org.keycloak.testsuite.page.AbstractPage;
 import org.keycloak.testsuite.util.*;
 
+import org.keycloak.testsuite.util.SamlClient.Binding;
 import org.openqa.selenium.By;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -129,6 +107,7 @@ import static org.keycloak.testsuite.util.IOUtil.loadXML;
 import static org.keycloak.testsuite.util.IOUtil.modifyDocElementAttribute;
 import static org.keycloak.testsuite.util.Matchers.bodyHC;
 import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
+import static org.keycloak.testsuite.util.SamlClient.Binding.POST;
 import static org.keycloak.testsuite.util.SamlClient.idpInitiatedLogin;
 import static org.keycloak.testsuite.util.SamlClient.login;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
@@ -145,6 +124,9 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
     protected BadRealmSalesPostSigServlet badRealmSalesPostSigServletPage;
 
     @Page
+    protected EmployeeAcsServlet employeeAcsServletPage;
+
+    @Page
     protected Employee2Servlet employee2ServletPage;
 
     @Page
@@ -227,6 +209,11 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         return samlServletDeployment(BadRealmSalesPostSigServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
     }
 
+    @Deployment(name = EmployeeAcsServlet.DEPLOYMENT_NAME)
+    protected static WebArchive employeeAssertionConsumerServiceUrlSet() {
+        return samlServletDeployment(EmployeeAcsServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
     @Deployment(name = Employee2Servlet.DEPLOYMENT_NAME)
     protected static WebArchive employee2() {
         return samlServletDeployment(Employee2Servlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
@@ -467,6 +454,20 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         testSuccessfulAndUnauthorizedLogin(employeeSigServletPage, testRealmSAMLRedirectLoginPage);
     }
 
+    @Test
+    public void employeeAcsTest() {
+        SAMLDocumentHolder samlResponse = new SamlClient(employeeAcsServletPage.buildUri()).getSamlResponse(Binding.POST, (client, context, strategy) -> {
+            strategy.setRedirectable(false);
+            return client.execute(new HttpGet(employeeAcsServletPage.buildUri()), context);
+        });
+
+        assertThat(samlResponse.getSamlObject(), instanceOf(AuthnRequestType.class));
+        assertThat(((AuthnRequestType) samlResponse.getSamlObject()).getAssertionConsumerServiceURL(), notNullValue());
+        assertThat(((AuthnRequestType) samlResponse.getSamlObject()).getAssertionConsumerServiceURL().getPath(), is("/employee-acs/a/different/endpoint/for/saml"));
+
+        assertSuccessfulLogin(employeeAcsServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
+    }
+
     private static final KeyPair NEW_KEY_PAIR = KeyUtils.generateRsaKeyPair(1024);
     private static final String NEW_KEY_PRIVATE_KEY_PEM = PemUtils.encodeKey(NEW_KEY_PAIR.getPrivate());
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AggregatePolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AggregatePolicyManagementTest.java
new file mode 100644
index 0000000..230b4ee
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AggregatePolicyManagementTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.admin.client.authorization;
+
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AggregatePoliciesResource;
+import org.keycloak.admin.client.resource.AggregatePolicyResource;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyManagementTest extends AbstractPolicyManagementTest {
+
+    @Test
+    public void testCreate() {
+        AuthorizationResource authorization = getClient().authorization();
+        AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+        representation.setName("Aggregate Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+        assertCreated(authorization, representation);
+    }
+
+    @Test
+    public void testUpdate() {
+        AuthorizationResource authorization = getClient().authorization();
+        AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+        representation.setName("Update Aggregate Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+        assertCreated(authorization, representation);
+
+        representation.setName("changed");
+        representation.setDescription("changed");
+        representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+        representation.setLogic(Logic.POSITIVE);
+        representation.getPolicies().clear();
+        representation.addPolicy("Only Kolo Policy");
+
+        AggregatePoliciesResource policies = authorization.policies().aggregate();
+        AggregatePolicyResource policy = policies.findById(representation.getId());
+
+        policy.update(representation);
+        assertRepresentation(representation, policy);
+    }
+
+    @Test
+    public void testDelete() {
+        AuthorizationResource authorization = getClient().authorization();
+        AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+        representation.setName("Test Delete Policy");
+        representation.addPolicy("Only Marta Policy");
+
+        AggregatePoliciesResource policies = authorization.policies().aggregate();
+        Response response = policies.create(representation);
+        AggregatePolicyRepresentation created = response.readEntity(AggregatePolicyRepresentation.class);
+
+        policies.findById(created.getId()).remove();
+
+        AggregatePolicyResource removed = policies.findById(created.getId());
+
+        try {
+            removed.toRepresentation();
+            fail("Policy not removed");
+        } catch (NotFoundException ignore) {
+
+        }
+    }
+
+    private void assertCreated(AuthorizationResource authorization, AggregatePolicyRepresentation representation) {
+        AggregatePoliciesResource permissions = authorization.policies().aggregate();
+        Response response = permissions.create(representation);
+        AggregatePolicyRepresentation created = response.readEntity(AggregatePolicyRepresentation.class);
+        AggregatePolicyResource permission = permissions.findById(created.getId());
+        assertRepresentation(representation, permission);
+    }
+
+    private void assertRepresentation(AggregatePolicyRepresentation representation, AggregatePolicyResource policy) {
+        AggregatePolicyRepresentation actual = policy.toRepresentation();
+        assertRepresentation(representation, actual, () -> policy.resources(), () -> Collections.emptyList(), () -> policy.associatedPolicies());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java
new file mode 100644
index 0000000..87848d9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientPoliciesResource;
+import org.keycloak.admin.client.resource.ClientPolicyResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientPolicyManagementTest extends AbstractPolicyManagementTest {
+
+    @Override
+    protected RealmBuilder createTestRealm() {
+        return super.createTestRealm()
+                .client(ClientBuilder.create().clientId("Client A"))
+                .client(ClientBuilder.create().clientId("Client B"))
+                .client(ClientBuilder.create().clientId("Client C"));
+    }
+
+    @Test
+    public void testCreate() {
+        AuthorizationResource authorization = getClient().authorization();
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName("Realm Client Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addClient("Client A");
+        representation.addClient("Client B");
+
+        assertCreated(authorization, representation);
+    }
+
+    @Test
+    public void testUpdate() {
+        AuthorizationResource authorization = getClient().authorization();
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName("Update Test Client Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addClient("Client A");
+        representation.addClient("Client B");
+        representation.addClient("Client C");
+
+        assertCreated(authorization, representation);
+
+        representation.setName("changed");
+        representation.setDescription("changed");
+        representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+        representation.setLogic(Logic.POSITIVE);
+        representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client A")).collect(Collectors.toSet()));
+
+        ClientPoliciesResource policies = authorization.policies().client();
+        ClientPolicyResource permission = policies.findById(representation.getId());
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+
+        representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client C")).collect(Collectors.toSet()));
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+    }
+
+    @Test
+    public void testDelete() {
+        AuthorizationResource authorization = getClient().authorization();
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName("Test Delete Permission");
+        representation.addClient("Client A");
+
+        ClientPoliciesResource policies = authorization.policies().client();
+        Response response = policies.create(representation);
+        ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+
+        policies.findById(created.getId()).remove();
+
+        ClientPolicyResource removed = policies.findById(created.getId());
+
+        try {
+            removed.toRepresentation();
+            fail("Permission not removed");
+        } catch (NotFoundException ignore) {
+
+        }
+    }
+
+    @Test
+    public void testGenericConfig() {
+        AuthorizationResource authorization = getClient().authorization();
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName("Test Generic Config Permission");
+        representation.addClient("Client A");
+
+        ClientPoliciesResource policies = authorization.policies().client();
+        Response response = policies.create(representation);
+        ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+
+        PolicyResource policy = authorization.policies().policy(created.getId());
+        PolicyRepresentation genericConfig = policy.toRepresentation();
+
+        assertNotNull(genericConfig.getConfig());
+        assertNotNull(genericConfig.getConfig().get("clients"));
+
+        ClientRepresentation user = getRealm().clients().findByClientId("Client A").get(0);
+
+        assertTrue(genericConfig.getConfig().get("clients").contains(user.getId()));
+    }
+
+    private void assertCreated(AuthorizationResource authorization, ClientPolicyRepresentation representation) {
+        ClientPoliciesResource permissions = authorization.policies().client();
+        Response response = permissions.create(representation);
+        ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+        ClientPolicyResource permission = permissions.findById(created.getId());
+        assertRepresentation(representation, permission);
+    }
+
+    private void assertRepresentation(ClientPolicyRepresentation representation, ClientPolicyResource permission) {
+        ClientPolicyRepresentation actual = permission.toRepresentation();
+        assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+        assertEquals(representation.getClients().size(), actual.getClients().size());
+        assertEquals(0, actual.getClients().stream().filter(clientId -> !representation.getClients().stream()
+                .filter(userName -> getClientName(clientId).equalsIgnoreCase(userName))
+                .findFirst().isPresent())
+                .count());
+    }
+
+    private String getClientName(String id) {
+        return getRealm().clients().get(id).toRepresentation().getClientId();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
index 4b2168f..a308ec0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
@@ -28,6 +28,7 @@ import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.testsuite.util.UserBuilder;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -43,6 +44,8 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
         RoleRepresentation role = new RoleRepresentation();
         role.setName("admin");
         clientResource.roles().create(role);
+
+        testRealmResource().users().create(UserBuilder.create().username("alice").build());
     }
 
     @After
@@ -72,6 +75,6 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
 
         authorizationResource.importSettings(toImport);
 
-        assertEquals(13, authorizationResource.policies().policies().size());
+        assertEquals(15, authorizationResource.policies().policies().size());
     }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java
new file mode 100644
index 0000000..f6aefd7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.JSPoliciesResource;
+import org.keycloak.admin.client.resource.JSPolicyResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyManagementTest extends AbstractPolicyManagementTest {
+
+    @Test
+    public void testCreate() {
+        AuthorizationResource authorization = getClient().authorization();
+        JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+        representation.setName("JS Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.setCode("$evaluation.grant();");
+
+        assertCreated(authorization, representation);
+    }
+
+    @Test
+    public void testUpdate() {
+        AuthorizationResource authorization = getClient().authorization();
+        JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+        representation.setName("Update JS Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.setCode("$evaluation.grant();");
+
+        assertCreated(authorization, representation);
+
+        representation.setName("changed");
+        representation.setDescription("changed");
+        representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+        representation.setLogic(Logic.POSITIVE);
+        representation.setCode("$evaluation.deny()");
+
+        JSPoliciesResource policies = authorization.policies().js();
+        JSPolicyResource permission = policies.findById(representation.getId());
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+    }
+
+    @Test
+    public void testDelete() {
+        AuthorizationResource authorization = getClient().authorization();
+        JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+        representation.setName("Test Delete Policy");
+        representation.setCode("$evaluation.grant()");
+
+        JSPoliciesResource policies = authorization.policies().js();
+        Response response = policies.create(representation);
+        JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class);
+
+        policies.findById(created.getId()).remove();
+
+        JSPolicyResource removed = policies.findById(created.getId());
+
+        try {
+            removed.toRepresentation();
+            fail("Permission not removed");
+        } catch (NotFoundException ignore) {
+
+        }
+    }
+
+    private void assertCreated(AuthorizationResource authorization, JSPolicyRepresentation representation) {
+        JSPoliciesResource permissions = authorization.policies().js();
+        Response response = permissions.create(representation);
+        JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class);
+        JSPolicyResource permission = permissions.findById(created.getId());
+        assertRepresentation(representation, permission);
+    }
+
+    private void assertRepresentation(JSPolicyRepresentation representation, JSPolicyResource permission) {
+        JSPolicyRepresentation actual = permission.toRepresentation();
+        assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+        assertEquals(representation.getCode(), actual.getCode());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
index 1deed22..71ef5e8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
@@ -35,7 +35,7 @@ import org.keycloak.representations.idm.authorization.ResourcePermissionRepresen
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class ResourcePermissionManagementTest extends AbstractPermissionManagementTest {
+public class ResourcePermissionManagementTest extends AbstractPolicyManagementTest {
 
     @Test
     public void testCreateResourcePermission() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java
index 1b4a701..f066c71 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java
@@ -24,7 +24,6 @@ import static org.junit.Assert.fail;
 import java.util.Collections;
 import java.util.stream.Collectors;
 
-import javax.management.relation.Role;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 
@@ -47,7 +46,7 @@ import org.keycloak.testsuite.util.RolesBuilder;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
+public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
 
     @Override
     protected RealmBuilder createTestRealm() {
@@ -120,7 +119,7 @@ public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setLogic(Logic.POSITIVE);
         representation.setRoles(representation.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().equals("Resource A")).collect(Collectors.toSet()));
 
-        RolePoliciesResource policies = authorization.policies().roles();
+        RolePoliciesResource policies = authorization.policies().role();
         RolePolicyResource permission = policies.findById(representation.getId());
 
         permission.update(representation);
@@ -147,7 +146,7 @@ public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setName("Test Delete Permission");
         representation.addRole("Role A", false);
 
-        RolePoliciesResource policies = authorization.policies().roles();
+        RolePoliciesResource policies = authorization.policies().role();
         Response response = policies.create(representation);
         RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
 
@@ -171,7 +170,7 @@ public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setName("Test Generic Config  Permission");
         representation.addRole("Role A", false);
 
-        RolePoliciesResource policies = authorization.policies().roles();
+        RolePoliciesResource policies = authorization.policies().role();
         Response response = policies.create(representation);
         RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
 
@@ -187,7 +186,7 @@ public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
     }
 
     private void assertCreated(AuthorizationResource authorization, RolePolicyRepresentation representation) {
-        RolePoliciesResource permissions = authorization.policies().roles();
+        RolePoliciesResource permissions = authorization.policies().role();
         Response response = permissions.create(representation);
         RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
         RolePolicyResource permission = permissions.findById(created.getId());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RulesPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RulesPolicyManagementTest.java
new file mode 100644
index 0000000..69767b9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RulesPolicyManagementTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.RulePoliciesResource;
+import org.keycloak.admin.client.resource.RulePolicyResource;
+import org.keycloak.common.Version;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RulesPolicyManagementTest extends AbstractPolicyManagementTest {
+
+    @Test
+    public void testCreate() {
+        assertCreated(getClient().authorization(), createDefaultRepresentation("Rule Policy"));
+    }
+
+    @Test
+    public void testUpdate() {
+        AuthorizationResource authorization = getClient().authorization();
+        RulePolicyRepresentation representation = createDefaultRepresentation("Update Rule Policy");
+
+        assertCreated(authorization, representation);
+
+        representation.setName("changed");
+        representation.setDescription("changed");
+        representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+        representation.setLogic(Logic.POSITIVE);
+        representation.setScannerPeriod("12");
+        representation.setScannerPeriodUnit("Days");
+        representation.setModuleName("PhotozAuthzContextualPolicy");
+        representation.setSessionName("MainContextualSession");
+
+        RulePoliciesResource policies = authorization.policies().rule();
+        RulePolicyResource policy = policies.findById(representation.getId());
+
+        policy.update(representation);
+
+        assertRepresentation(representation, policy);
+    }
+
+    @Test
+    public void testDelete() {
+        AuthorizationResource authorization = getClient().authorization();
+        RulePolicyRepresentation representation = createDefaultRepresentation("Delete Rule Policy");
+
+        RulePoliciesResource policies = authorization.policies().rule();
+        Response response = policies.create(representation);
+        RulePolicyRepresentation created = response.readEntity(RulePolicyRepresentation.class);
+
+        policies.findById(created.getId()).remove();
+
+        RulePolicyResource removed = policies.findById(created.getId());
+
+        try {
+            removed.toRepresentation();
+            fail("Policy not removed");
+        } catch (NotFoundException ignore) {
+
+        }
+    }
+
+    private RulePolicyRepresentation createDefaultRepresentation(String name) {
+        RulePolicyRepresentation representation = new RulePolicyRepresentation();
+
+        representation.setName(name);
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.setArtifactGroupId("org.keycloak");
+        representation.setArtifactId("photoz-authz-policy");
+        representation.setArtifactVersion(Version.VERSION);
+        representation.setModuleName("PhotozAuthzOwnerPolicy");
+        representation.setSessionName("MainOwnerSession");
+        representation.setScannerPeriod("1");
+        representation.setScannerPeriodUnit("Minutes");
+
+        return representation;
+    }
+
+    private void assertCreated(AuthorizationResource authorization, RulePolicyRepresentation representation) {
+        RulePoliciesResource permissions = authorization.policies().rule();
+        Response response = permissions.create(representation);
+        RulePolicyRepresentation created = response.readEntity(RulePolicyRepresentation.class);
+        RulePolicyResource permission = permissions.findById(created.getId());
+        assertRepresentation(representation, permission);
+    }
+
+    private void assertRepresentation(RulePolicyRepresentation expected, RulePolicyResource policy) {
+        RulePolicyRepresentation actual = policy.toRepresentation();
+        assertRepresentation(expected, actual, () -> policy.resources(), () -> Collections.emptyList(), () -> policy.associatedPolicies());
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+        assertEquals(expected.getArtifactGroupId(), actual.getArtifactGroupId());
+        assertEquals(expected.getArtifactId(), actual.getArtifactId());
+        assertEquals(expected.getArtifactVersion(), actual.getArtifactVersion());
+        assertEquals(expected.getModuleName(), actual.getModuleName());
+        assertEquals(expected.getSessionName(), actual.getSessionName());
+        assertEquals(expected.getScannerPeriod(), actual.getScannerPeriod());
+        assertEquals(expected.getScannerPeriodUnit(), actual.getScannerPeriodUnit());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java
index a833668..5db4817 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java
@@ -33,7 +33,7 @@ import org.keycloak.representations.idm.authorization.ScopePermissionRepresentat
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class ScopePermissionManagementTest extends AbstractPermissionManagementTest {
+public class ScopePermissionManagementTest extends AbstractPolicyManagementTest {
 
     @Test
     public void testCreateResourceScopePermission() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java
new file mode 100644
index 0000000..6095363
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.TimePoliciesResource;
+import org.keycloak.admin.client.resource.TimePolicyResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyManagementTest extends AbstractPolicyManagementTest {
+
+    @Test
+    public void testCreate() {
+        AuthorizationResource authorization = getClient().authorization();
+        assertCreated(authorization, createRepresentation("Time Policy"));
+    }
+
+    @Test
+    public void testUpdate() {
+        AuthorizationResource authorization = getClient().authorization();
+        TimePolicyRepresentation representation = createRepresentation("Update Time Policy");
+
+        assertCreated(authorization, representation);
+
+        representation.setName("changed");
+        representation.setDescription("changed");
+        representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+        representation.setLogic(Logic.POSITIVE);
+        representation.setDayMonth("11");
+        representation.setDayMonthEnd("22");
+        representation.setMonth("7");
+        representation.setMonthEnd("9");
+        representation.setYear("2019");
+        representation.setYearEnd("2030");
+        representation.setHour("15");
+        representation.setHourEnd("23");
+        representation.setMinute("55");
+        representation.setMinuteEnd("58");
+        representation.setNotBefore("2019-01-01 00:00:00");
+        representation.setNotOnOrAfter("2019-02-03 00:00:00");
+
+        TimePoliciesResource policies = authorization.policies().time();
+        TimePolicyResource permission = policies.findById(representation.getId());
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+
+        representation.setDayMonth(null);
+        representation.setDayMonthEnd(null);
+        representation.setMonth(null);
+        representation.setMonthEnd(null);
+        representation.setYear(null);
+        representation.setYearEnd(null);
+        representation.setHour(null);
+        representation.setHourEnd(null);
+        representation.setMinute(null);
+        representation.setMinuteEnd(null);
+        representation.setNotBefore(null);
+        representation.setNotOnOrAfter("2019-02-03 00:00:00");
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+
+        representation.setNotOnOrAfter(null);
+        representation.setHour("2");
+
+        permission.update(representation);
+        assertRepresentation(representation, permission);
+    }
+
+    @Test
+    public void testDelete() {
+        AuthorizationResource authorization = getClient().authorization();
+        TimePolicyRepresentation representation = createRepresentation("Test Delete Policy");
+        TimePoliciesResource policies = authorization.policies().time();
+        Response response = policies.create(representation);
+        TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class);
+
+        policies.findById(created.getId()).remove();
+
+        TimePolicyResource removed = policies.findById(created.getId());
+
+        try {
+            removed.toRepresentation();
+            fail("Permission not removed");
+        } catch (NotFoundException ignore) {
+
+        }
+    }
+
+    private TimePolicyRepresentation createRepresentation(String name) {
+        TimePolicyRepresentation representation = new TimePolicyRepresentation();
+
+        representation.setName(name);
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.setDayMonth("1");
+        representation.setDayMonthEnd("2");
+        representation.setMonth("3");
+        representation.setMonthEnd("4");
+        representation.setYear("5");
+        representation.setYearEnd("6");
+        representation.setHour("7");
+        representation.setHourEnd("8");
+        representation.setMinute("9");
+        representation.setMinuteEnd("10");
+        representation.setNotBefore("2017-01-01 00:00:00");
+        representation.setNotOnOrAfter("2017-02-01 00:00:00");
+        return representation;
+    }
+
+    private void assertCreated(AuthorizationResource authorization, TimePolicyRepresentation representation) {
+        TimePoliciesResource permissions = authorization.policies().time();
+        Response response = permissions.create(representation);
+        TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class);
+        TimePolicyResource permission = permissions.findById(created.getId());
+        assertRepresentation(representation, permission);
+    }
+
+    private void assertRepresentation(TimePolicyRepresentation representation, TimePolicyResource permission) {
+        TimePolicyRepresentation actual = permission.toRepresentation();
+        assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+        assertEquals(representation.getDayMonth(), actual.getDayMonth());
+        assertEquals(representation.getDayMonthEnd(), actual.getDayMonthEnd());
+        assertEquals(representation.getMonth(), actual.getMonth());
+        assertEquals(representation.getMonthEnd(), actual.getMonthEnd());
+        assertEquals(representation.getYear(), actual.getYear());
+        assertEquals(representation.getYearEnd(), actual.getYearEnd());
+        assertEquals(representation.getHour(), actual.getHour());
+        assertEquals(representation.getHourEnd(), actual.getHourEnd());
+        assertEquals(representation.getMinute(), actual.getMinute());
+        assertEquals(representation.getMinuteEnd(), actual.getMinuteEnd());
+        assertEquals(representation.getNotBefore(), actual.getNotBefore());
+        assertEquals(representation.getNotOnOrAfter(), actual.getNotOnOrAfter());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
index 9a80d68..b4f78e0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
@@ -43,7 +43,7 @@ import org.keycloak.testsuite.util.UserBuilder;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
+public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
 
     @Override
     protected RealmBuilder createTestRealm() {
@@ -54,7 +54,7 @@ public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
     }
 
     @Test
-    public void testCreateUserPolicy() {
+    public void testCreate() {
         AuthorizationResource authorization = getClient().authorization();
         UserPolicyRepresentation representation = new UserPolicyRepresentation();
 
@@ -89,7 +89,7 @@ public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setLogic(Logic.POSITIVE);
         representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User A")).collect(Collectors.toSet()));
 
-        UserPoliciesResource policies = authorization.policies().users();
+        UserPoliciesResource policies = authorization.policies().user();
         UserPolicyResource permission = policies.findById(representation.getId());
 
         permission.update(representation);
@@ -109,7 +109,7 @@ public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setName("Test Delete Permission");
         representation.addUser("User A");
 
-        UserPoliciesResource policies = authorization.policies().users();
+        UserPoliciesResource policies = authorization.policies().user();
         Response response = policies.create(representation);
         UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
 
@@ -133,7 +133,7 @@ public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
         representation.setName("Test Generic Config Permission");
         representation.addUser("User A");
 
-        UserPoliciesResource policies = authorization.policies().users();
+        UserPoliciesResource policies = authorization.policies().user();
         Response response = policies.create(representation);
         UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
 
@@ -149,7 +149,7 @@ public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
     }
 
     private void assertCreated(AuthorizationResource authorization, UserPolicyRepresentation representation) {
-        UserPoliciesResource permissions = authorization.policies().users();
+        UserPoliciesResource permissions = authorization.policies().user();
         Response response = permissions.create(representation);
         UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
         UserPolicyResource permission = permissions.findById(created.getId());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
new file mode 100644
index 0000000..2e9086a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthenticator;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.AuthorizationRequest;
+import org.keycloak.authorization.client.representation.AuthorizationResponse;
+import org.keycloak.authorization.client.representation.PermissionRequest;
+import org.keycloak.authorization.client.representation.PermissionResponse;
+import org.keycloak.authorization.client.representation.RegistrationResponse;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthzClientCredentialsTest extends AbstractKeycloakTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(configureRealm(RealmBuilder.create().name("authz-client-jwt-test"), ClientBuilder.create()
+                .attribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==")
+                .authenticatorType(JWTClientAuthenticator.PROVIDER_ID))
+                .build());
+        testRealms.add(configureRealm(RealmBuilder.create().name("authz-test"), ClientBuilder.create().secret("secret")).build());
+    }
+
+    @Before
+    public void beforeAbstractKeycloakTest() throws Exception {
+        super.beforeAbstractKeycloakTest();
+        testContext.getTestRealmReps().forEach(realmRepresentation -> {
+            Keycloak adminClient = getAdminClient();
+            ClientsResource clients = adminClient.realm(realmRepresentation.getRealm()).clients();
+            ClientRepresentation client = clients.findByClientId("resource-server-test").get(0);
+
+            client.setAuthorizationServicesEnabled(false);
+
+            clients.get(client.getId()).update(client);
+
+            client.setAuthorizationServicesEnabled(true);
+
+            clients.get(client.getId()).update(client);
+
+            AuthorizationResource authorization = clients.get(client.getId()).authorization();
+            ResourceServerRepresentation settings = authorization.getSettings();
+
+            settings.setAllowRemoteResourceManagement(true);
+
+            authorization.update(settings);
+        });
+    }
+
+    @Test
+    public void testSuccessfulJWTAuthentication() {
+        assertAccessProtectionAPI(getAuthzClient("keycloak-with-jwt-authentication.json").protection());
+    }
+
+    @Test
+    public void testSuccessfulAuthorizationRequest() throws Exception {
+        AuthzClient authzClient = getAuthzClient("keycloak-with-jwt-authentication.json");
+        ProtectionResource protection = authzClient.protection();
+        PermissionRequest request = new PermissionRequest();
+
+        request.setResourceSetName("Default Resource");
+
+        PermissionResponse ticketResponse = protection.permission().forResource(request);
+        String ticket = ticketResponse.getTicket();
+
+        AuthorizationResponse authorizationResponse = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
+        String rpt = authorizationResponse.getRpt();
+
+        assertNotNull(rpt);
+
+        AccessToken accessToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
+
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertFalse(permissions.isEmpty());
+        assertEquals("Default Resource", permissions.get(0).getResourceSetName());
+    }
+
+    @Test
+    public void failUserWithoutUmaAuthorizationScope() throws Exception {
+        AuthzClient authzClient = getAuthzClient("keycloak-with-jwt-authentication.json");
+        ProtectionResource protection = authzClient.protection();
+        PermissionRequest request = new PermissionRequest();
+
+        request.setResourceSetName("Default Resource");
+
+        PermissionResponse ticketResponse = protection.permission().forResource(request);
+        String ticket = ticketResponse.getTicket();
+
+        try {
+            authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
+            fail("Should fail because user does not have uma_authorization");
+        } catch (AuthorizationDeniedException cause) {
+            assertEquals(403, ((HttpResponseException) cause.getCause()).getStatusCode());
+        }
+    }
+
+    @Test
+    public void failJWTAuthentication() {
+        try {
+            getAuthzClient("keycloak-with-invalid-keys-jwt-authentication.json").protection();
+            fail("Should fail due to invalid signature");
+        } catch (HttpResponseException cause) {
+            assertEquals(400, cause.getStatusCode());
+        }
+    }
+
+    @Test
+    public void testSuccessfulClientSecret() {
+        ProtectionResource protection = getAuthzClient("default-keycloak.json").protection();
+        assertAccessProtectionAPI(protection);
+    }
+
+    private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) {
+        return builder
+                .roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false)))
+                .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
+                .user(UserBuilder.create().username("kolo").password("password"))
+                .client(clientBuilder.clientId("resource-server-test")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .directAccessGrants());
+    }
+
+    private void assertAccessProtectionAPI(ProtectionResource protection) {
+        ResourceRepresentation expected = new ResourceRepresentation("Resource A", Collections.emptySet());
+
+        String id = protection.resource().create(expected).getId();
+        RegistrationResponse response = protection.resource().findById(id);
+        ResourceRepresentation actual = response.getResourceDescription();
+
+        assertNotNull(actual);
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(id, actual.getId());
+    }
+
+    private AuthzClient getAuthzClient(String adapterConfig) {
+        KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getConfigurationStream(adapterConfig));
+
+        return AuthzClient.create(new Configuration(deployment.getAuthServerBaseUrl(), deployment.getRealm(), deployment.getResourceName(), deployment.getResourceCredentials(), deployment.getClient()), new ClientAuthenticator() {
+            @Override
+            public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
+                ClientCredentialsProviderUtils.setClientCredentials(deployment, requestHeaders, requestParams);
+            }
+        });
+    }
+
+    private InputStream getConfigurationStream(String adapterConfig) {
+        return getClass().getResourceAsStream("/authorization-test/" + adapterConfig);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
index 25382e8..c0cca18 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
@@ -63,6 +63,9 @@ public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest {
 
     public static void setup(KeycloakSession session) {
         RealmModel realm = session.realms().getRealmByName(TEST);
+
+        session.getContext().setRealm(realm);
+
         ClientModel client = session.realms().addClient(realm, "myclient");
         RoleModel role1 = client.addRole("client-role1");
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
index 1315b18..04a758e 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java
@@ -123,7 +123,7 @@ public class MigrationTest extends AbstractKeycloakTest {
     
     @Test
     @Migration(versionFrom = "1.9.8.Final")
-    public void migration1_9_8Test() {
+    public void migration1_9_8Test() throws Exception {
         testMigratedData();
         testMigrationTo2_0_0();
         testMigrationTo2_1_0();
@@ -200,7 +200,7 @@ public class MigrationTest extends AbstractKeycloakTest {
         testDuplicateEmailSupport(masterRealm, migrationRealm);
     }
 
-    private void testMigrationTo2_5_1() {
+    private void testMigrationTo2_5_1() throws Exception {
         testOfflineTokenLogin();
     }
     
@@ -407,12 +407,12 @@ public class MigrationTest extends AbstractKeycloakTest {
         }
     }
 
-    private void testOfflineTokenLogin() {
+    private void testOfflineTokenLogin() throws Exception {
         if (isImportMigrationMode()) {
             log.info("Skip offline token login test in the 'import' migrationMode");
         } else {
             log.info("test login with old offline token");
-            String oldOfflineToken = suiteContext.getMigrationContext().getOfflineToken();
+            String oldOfflineToken = suiteContext.getMigrationContext().loadOfflineToken();
             Assert.assertNotNull(oldOfflineToken);
 
             oauth.realm(MIGRATION);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
index f320066..e244f9a 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
@@ -30,12 +30,16 @@ import org.keycloak.events.Details;
 import org.keycloak.events.EventType;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.account.AccountTest;
+import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.pages.AccountApplicationsPage;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.OAuthGrantPage;
@@ -49,6 +53,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.core.Response;
+
 import static org.junit.Assert.assertEquals;
 import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
 import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
@@ -328,4 +334,73 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
 
     }
 
+
+    // KEYCLOAK-4326
+    @Test
+    public void oauthGrantClientTemplateMappers() throws Exception {
+        // Add client template with some protocol mapper
+        RealmResource appRealm = adminClient.realm(REALM_NAME);
+
+        ClientTemplateRepresentation template1 = new ClientTemplateRepresentation();
+        template1.setName("foo");
+        template1.setFullScopeAllowed(false);
+        template1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        Response response = appRealm.clientTemplates().create(template1);
+        String templateId = ApiUtil.getCreatedId(response);
+        response.close();
+
+        ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true);
+        response = appRealm.clientTemplates().get(templateId).getProtocolMappers().createMapper(protocolMapper);
+        response.close();
+
+        // Add template to client
+        ClientResource thirdParty = findClientByClientId(appRealm, THIRD_PARTY_APP);
+        ClientRepresentation thirdPartyRep = thirdParty.toRepresentation();
+        thirdPartyRep.setClientTemplate("foo");
+        thirdPartyRep.setUseTemplateMappers(true);
+        thirdParty.update(thirdPartyRep);
+
+        // Login
+        oauth.clientId(THIRD_PARTY_APP);
+        oauth.doLoginGrant("test-user@localhost", "password");
+        grantPage.assertCurrent();
+        Assert.assertTrue(driver.getPageSource().contains("Email"));
+        Assert.assertTrue(driver.getPageSource().contains("Address"));
+        grantPage.accept();
+
+        events.expectLogin()
+                .client(THIRD_PARTY_APP)
+                .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+                .assertEvent();
+
+        // Go to user's application screen
+        accountAppsPage.open();
+        Assert.assertTrue(accountAppsPage.isCurrent());
+        Map<String, AccountApplicationsPage.AppEntry> apps = accountAppsPage.getApplications();
+        Assert.assertTrue(apps.containsKey("third-party"));
+        Assert.assertTrue(apps.get("third-party").getProtocolMappersGranted().contains("Address"));
+
+        // Login as admin and see the consent screen of particular user
+        UserResource user = ApiUtil.findUserByUsernameId(appRealm, "test-user@localhost");
+        List<Map<String, Object>> consents = user.getConsents();
+        Assert.assertEquals(1, consents.size());
+
+        // Assert automatically logged another time
+        oauth.openLoginForm();
+        appPage.assertCurrent();
+        events.expectLogin()
+                .detail(Details.AUTH_METHOD, OIDCLoginProtocol.LOGIN_PROTOCOL)
+                .detail(Details.CONSENT, Details.CONSENT_VALUE_PERSISTED_CONSENT)
+                .removeDetail(Details.USERNAME)
+                .client(THIRD_PARTY_APP).assertEvent();
+
+        // Revoke
+        accountAppsPage.open();
+        accountAppsPage.revokeGrant(THIRD_PARTY_APP);
+        events.expect(EventType.REVOKE_GRANT)
+                .client("account").detail(Details.REVOKED_CLIENT, THIRD_PARTY_APP).assertEvent();
+
+
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
index b9a2547..6f68908 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
@@ -26,6 +26,9 @@ public abstract class AbstractSamlTest extends AbstractAuthTest {
     protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
     protected static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
 
+    protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST2 = "http://localhost:8080/sales-post2/";
+    protected static final String SAML_CLIENT_ID_SALES_POST2 = "http://localhost:8081/sales-post2/";
+
     protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_ENC = "http://localhost:8080/sales-post-enc/";
     protected static final String SAML_CLIENT_ID_SALES_POST_ENC = "http://localhost:8081/sales-post-enc/";
     protected static final String SAML_CLIENT_SALES_POST_ENC_PRIVATE_KEY = "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t";
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java
new file mode 100644
index 0000000..7870eba
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017 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.saml;
+
+import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.saml.SAML2LogoutRequestBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.Matchers;
+import org.keycloak.testsuite.util.SamlClient;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilderException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.keycloak.testsuite.util.Matchers.*;
+import static org.keycloak.testsuite.util.SamlClient.Binding.*;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class LogoutTest extends AbstractSamlTest {
+
+    private ClientRepresentation salesRep;
+    private ClientRepresentation sales2Rep;
+
+    private SamlClient samlClient;
+
+    @Before
+    public void setup() {
+        salesRep = adminClient.realm(REALM_NAME).clients().findByClientId(SAML_CLIENT_ID_SALES_POST).get(0);
+        sales2Rep = adminClient.realm(REALM_NAME).clients().findByClientId(SAML_CLIENT_ID_SALES_POST2).get(0);
+
+        adminClient.realm(REALM_NAME)
+          .clients().get(salesRep.getId())
+          .update(ClientBuilder.edit(salesRep)
+            .frontchannelLogout(true)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "http://url")
+            .build());
+
+        samlClient = new SamlClient(getAuthServerSamlEndpoint(REALM_NAME));
+    }
+
+    @Override
+    protected boolean isImportAfterEachMethod() {
+        return true;
+    }
+
+    private Document prepareLogoutFromSalesAfterLoggingIntoTwoApps() throws ParsingException, IllegalArgumentException, UriBuilderException, ConfigurationException, ProcessingException {
+        AuthnRequestType loginRep = createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, REALM_NAME);
+        Document doc = SAML2Request.convert(loginRep);
+        SAMLDocumentHolder resp = samlClient.login(bburkeUser, doc, null, POST, POST, false, true);
+        assertThat(resp.getSamlObject(), isSamlResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+        ResponseType loginResp1 = (ResponseType) resp.getSamlObject();
+
+        loginRep = createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST2, SAML_ASSERTION_CONSUMER_URL_SALES_POST2, REALM_NAME);
+        doc = SAML2Request.convert(loginRep);
+        resp = samlClient.subsequentLoginViaSSO(doc, null, POST, POST);
+        assertThat(resp.getSamlObject(), isSamlResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+        ResponseType loginResp2 = (ResponseType) resp.getSamlObject();
+
+        AssertionType firstAssertion = loginResp1.getAssertions().get(0).getAssertion();
+        assertThat(firstAssertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class));
+        NameIDType nameId = (NameIDType) firstAssertion.getSubject().getSubType().getBaseID();
+        AuthnStatementType firstAssertionStatement = (AuthnStatementType) firstAssertion.getStatements().iterator().next();
+
+        return new SAML2LogoutRequestBuilder()
+          .destination(getAuthServerSamlEndpoint(REALM_NAME).toString())
+          .issuer(SAML_CLIENT_ID_SALES_POST)
+          .sessionIndex(firstAssertionStatement.getSessionIndex())
+          .userPrincipal(nameId.getValue(), nameId.getFormat().toString())
+          .buildDocument();
+    }
+
+    @Test
+    public void testLogoutInSameBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(false)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "")
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE)
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.logout(logoutDoc, null, POST, POST);
+    }
+
+    @Test
+    public void testLogoutDifferentBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        // This is in fact the same as admin logging out a session from admin console.
+        // This always succeeds as it is essentially the same as backend logout which
+        // does not report errors to client but only to the server log
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(false)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "")
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE)
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.execute((client, context, strategy) -> {
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            CloseableHttpResponse response = client.execute(post, HttpClientContext.create());
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @Test
+    public void testFrontchannelLogoutInSameBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(true)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "")
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE)
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.execute((client, context, strategy) -> {
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            CloseableHttpResponse response = client.execute(post, context);
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @Test
+    public void testFrontchannelLogoutNoLogoutServiceUrlSetInSameBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(true)
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE)
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE)
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.execute((client, context, strategy) -> {
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            CloseableHttpResponse response = client.execute(post, context);
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @Test
+    public void testFrontchannelLogoutDifferentBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(true)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "")
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE)
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.execute((client, context, strategy) -> {
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            CloseableHttpResponse response = client.execute(post, HttpClientContext.create());
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @Test
+    public void testFrontchannelLogoutWithRedirectUrlDifferentBrowser() throws ParsingException, ConfigurationException, ProcessingException {
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(true)
+            .removeAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "http://url")
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        samlClient.execute((client, context, strategy) -> {
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            CloseableHttpResponse response = client.execute(post, HttpClientContext.create());
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @Test
+    public void testLogoutWithPostBindingUnsetRedirectBindingSet() throws ParsingException, ConfigurationException, ProcessingException {
+        // https://issues.jboss.org/browse/KEYCLOAK-4779
+        adminClient.realm(REALM_NAME)
+          .clients().get(sales2Rep.getId())
+          .update(ClientBuilder.edit(sales2Rep)
+            .frontchannelLogout(true)
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "")
+            .attribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "http://url")
+            .build());
+
+        Document logoutDoc = prepareLogoutFromSalesAfterLoggingIntoTwoApps();
+
+        SAMLDocumentHolder resp = samlClient.getSamlResponse(REDIRECT, (client, context, strategy) -> {
+            strategy.setRedirectable(false);
+            HttpUriRequest post = POST.createSamlUnsignedRequest(getAuthServerSamlEndpoint(REALM_NAME), null, logoutDoc);
+            return client.execute(post, context);
+        });
+
+        // Expect logout request for sales-post2
+        assertThat(resp.getSamlObject(), isSamlLogoutRequest("http://url"));
+        Document logoutRespDoc = new SAML2LogoutResponseBuilder()
+          .destination(getAuthServerSamlEndpoint(REALM_NAME).toString())
+          .issuer(SAML_CLIENT_ID_SALES_POST2)
+          .logoutRequestID(((LogoutRequestType) resp.getSamlObject()).getID())
+          .buildDocument();
+
+        // Emulate successful logout response from sales-post2 logout
+        resp = samlClient.getSamlResponse(POST, (client, context, strategy) -> {
+            strategy.setRedirectable(false);
+            HttpUriRequest post = POST.createSamlUnsignedResponse(getAuthServerSamlEndpoint(REALM_NAME), null, logoutRespDoc);
+            return client.execute(post, context);
+        });
+
+        // Expect final successful logout response from auth server signalling final successful logout
+        assertThat(resp.getSamlObject(), isSamlStatusResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
index df49edb..b4f3130 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
@@ -91,6 +91,11 @@ public class ClientBuilder {
         return this;
     }
 
+    public ClientBuilder frontchannelLogout(Boolean frontchannelLogout) {
+        rep.setFrontchannelLogout(frontchannelLogout);
+        return this;
+    }
+
     public ClientBuilder secret(String secret) {
         rep.setSecret(secret);
         return this;
@@ -115,6 +120,15 @@ public class ClientBuilder {
         return this;
     }
 
+    public ClientBuilder removeAttribute(String name) {
+        Map<String, String> attributes = rep.getAttributes();
+        if (attributes != null) {
+            attributes.remove(name);
+            rep.setAttributes(attributes);
+        }
+        return this;
+    }
+
     public ClientBuilder authenticatorType(String providerId) {
         rep.setClientAuthenticatorType(providerId);
         return this;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
index f88cfdf..0ab3a7b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
@@ -16,12 +16,20 @@
  */
 package org.keycloak.testsuite.util;
 
+import org.keycloak.dom.saml.v2.SAML2Object;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.testsuite.util.matchers.SamlResponseTypeMatcher;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.testsuite.util.matchers.*;
 
+import java.net.URI;
 import java.util.Map;
 import javax.ws.rs.core.Response;
 import org.apache.http.HttpResponse;
 import org.hamcrest.Matcher;
+import static org.hamcrest.Matchers.*;
 
 /**
  * Additional hamcrest matchers for use in {@link org.junit.Assert#assertThat}.
@@ -109,4 +117,40 @@ public class Matchers {
     public static <T> Matcher<Response> header(Matcher<Map<String, T>> matcher) {
         return new ResponseHeaderMatcher(matcher);
     }
+
+    /**
+     * Matches when the SAML status code of a {@link ResponseType} instance is equal to the given code.
+     * @param expectedStatusCode
+     * @return
+     */
+    public static <T> Matcher<SAML2Object> isSamlResponse(JBossSAMLURIConstants expectedStatus) {
+        return allOf(
+          instanceOf(ResponseType.class),
+          new SamlResponseTypeMatcher(is(URI.create(expectedStatus.get())))
+        );
+    }
+
+    /**
+     * Matches when the destination of a SAML {@link LogoutRequestType} instance is equal to the given destination.
+     * @param expectedStatusCode
+     * @return
+     */
+    public static <T> Matcher<SAML2Object> isSamlLogoutRequest(String destination) {
+        return allOf(
+          instanceOf(LogoutRequestType.class),
+          new SamlLogoutRequestTypeMatcher(URI.create(destination))
+        );
+    }
+
+    /**
+     * Matches when the SAML status of a {@link StatusResponseType} instance is equal to the given code.
+     * @param expectedStatusCode
+     * @return
+     */
+    public static <T> Matcher<SAML2Object> isSamlStatusResponse(JBossSAMLURIConstants expectedStatus) {
+        return allOf(
+          instanceOf(StatusResponseType.class),
+          new SamlStatusResponseTypeMatcher(is(URI.create(expectedStatus.get())))
+        );
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java
index 78d5b3f..d7b7230 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseStatusCodeMatcher.java
@@ -16,8 +16,10 @@
  */
 package org.keycloak.testsuite.util.matchers;
 
+import java.io.IOException;
 import javax.ws.rs.core.Response;
 import org.apache.http.HttpResponse;
+import org.apache.http.util.EntityUtils;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -40,6 +42,17 @@ public class HttpResponseStatusCodeMatcher extends BaseMatcher<HttpResponse> {
     }
 
     @Override
+    public void describeMismatch(Object item, Description description) {
+        Description d = description.appendText("was ").appendValue(item)
+          .appendText(" with entity ");
+        try {
+            d.appendText(EntityUtils.toString(((HttpResponse) item).getEntity()));
+        } catch (IOException e) {
+            d.appendText("<Cannot decode entity: " + e.getMessage() + ">");
+        }
+    }
+
+    @Override
     public void describeTo(Description description) {
         description.appendText("response status code matches ").appendDescriptionOf(this.matcher);
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java
new file mode 100644
index 0000000..10f7359
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlLogoutRequestTypeMatcher.java
@@ -0,0 +1,44 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.util.matchers;
+
+import org.keycloak.dom.saml.v2.SAML2Object;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import java.net.URI;
+import org.hamcrest.*;
+import static org.hamcrest.Matchers.*;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class SamlLogoutRequestTypeMatcher extends BaseMatcher<SAML2Object> {
+
+    private final Matcher<URI> destinationMatcher;
+
+    public SamlLogoutRequestTypeMatcher(URI destination) {
+        this.destinationMatcher = is(destination);
+    }
+
+    public SamlLogoutRequestTypeMatcher(Matcher<URI> destinationMatcher) {
+        this.destinationMatcher = destinationMatcher;
+    }
+
+    @Override
+    public boolean matches(Object item) {
+        return destinationMatcher.matches(((LogoutRequestType) item).getDestination());
+    }
+
+    @Override
+    public void describeMismatch(Object item, Description description) {
+        description.appendText("was ").appendValue(((LogoutRequestType) item).getDestination());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("SAML logout request destination matches ").appendDescriptionOf(this.destinationMatcher);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java
new file mode 100644
index 0000000..46eedde
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java
@@ -0,0 +1,47 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.util.matchers;
+
+import org.keycloak.dom.saml.v2.SAML2Object;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import java.net.URI;
+import org.hamcrest.*;
+import static org.hamcrest.Matchers.*;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class SamlResponseTypeMatcher extends BaseMatcher<SAML2Object> {
+
+    private final Matcher<URI> statusMatcher;
+
+    public SamlResponseTypeMatcher(JBossSAMLURIConstants expectedStatus) {
+        this.statusMatcher = is(URI.create(expectedStatus.get()));
+    }
+
+    public SamlResponseTypeMatcher(Matcher<URI> statusMatcher) {
+        this.statusMatcher = statusMatcher;
+    }
+
+    @Override
+    public boolean matches(Object item) {
+        return statusMatcher.matches(((ResponseType) item).getStatus().getStatusCode().getValue());
+    }
+
+    @Override
+    public void describeMismatch(Object item, Description description) {
+        description.appendText("was ").appendValue(((ResponseType) item).getStatus().getStatusCode());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("SAML response status code matches ").appendDescriptionOf(this.statusMatcher);
+    }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java
new file mode 100644
index 0000000..ccd5377
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/SamlStatusResponseTypeMatcher.java
@@ -0,0 +1,45 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.util.matchers;
+
+import org.keycloak.dom.saml.v2.SAML2Object;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import java.net.URI;
+import org.hamcrest.*;
+import static org.hamcrest.Matchers.*;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class SamlStatusResponseTypeMatcher extends BaseMatcher<SAML2Object> {
+
+    private final Matcher<URI> statusMatcher;
+
+    public SamlStatusResponseTypeMatcher(URI statusMatcher) {
+        this.statusMatcher = is(statusMatcher);
+    }
+
+    public SamlStatusResponseTypeMatcher(Matcher<URI> statusMatcher) {
+        this.statusMatcher = statusMatcher;
+    }
+
+    @Override
+    public boolean matches(Object item) {
+        return statusMatcher.matches(((StatusResponseType) item).getStatus().getStatusCode().getValue());
+    }
+
+    @Override
+    public void describeMismatch(Object item, Description description) {
+        description.appendText("was ").appendValue(((StatusResponseType) item).getStatus().getStatusCode().getValue());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("SAML status response status matches ").appendDescriptionOf(this.statusMatcher);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
index ccaca6c..5d5675f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
@@ -58,11 +58,11 @@ import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 
+import org.apache.http.protocol.HttpContext;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 import static org.keycloak.testsuite.admin.Users.getPasswordOf;
 import static org.keycloak.testsuite.arquillian.AuthServerTestEnricher.getAuthServerContextRoot;
-import static org.keycloak.testsuite.util.IOUtil.documentToString;
 import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
 
 /**
@@ -221,9 +221,11 @@ public class SamlClient {
     public static SAMLDocumentHolder extractSamlResponseFromForm(String responsePage) {
         org.jsoup.nodes.Document theResponsePage = Jsoup.parse(responsePage);
         Elements samlResponses = theResponsePage.select("input[name=SAMLResponse]");
-        assertThat("Checking uniqueness of SAMLResponse input field in the page", samlResponses, hasSize(1));
+        Elements samlRequests = theResponsePage.select("input[name=SAMLRequest]");
+        int size = samlResponses.size() + samlRequests.size();
+        assertThat("Checking uniqueness of SAMLResponse/SAMLRequest input field in the page", size, is(1));
 
-        Element respElement = samlResponses.first();
+        Element respElement = samlResponses.isEmpty() ? samlRequests.first() : samlResponses.first();
 
         return SAMLRequestParser.parseResponsePostBinding(respElement.val());
     }
@@ -237,15 +239,15 @@ public class SamlClient {
     public static SAMLDocumentHolder extractSamlResponseFromRedirect(String responseUri) {
         List<NameValuePair> params = URLEncodedUtils.parse(URI.create(responseUri), "UTF-8");
 
-        String samlResponse = null;
+        String samlDoc = null;
         for (NameValuePair param : params) {
-            if ("SAMLResponse".equals(param.getName())) {
-                assertThat(samlResponse, nullValue());
-                samlResponse = param.getValue();
+            if ("SAMLResponse".equals(param.getName()) || "SAMLRequest".equals(param.getName())) {
+                assertThat("Only one SAMLRequest/SAMLResponse check", samlDoc, nullValue());
+                samlDoc = param.getValue();
             }
         }
 
-        return SAMLRequestParser.parseResponseRedirectBinding(samlResponse);
+        return SAMLRequestParser.parseResponseRedirectBinding(samlDoc);
     }
 
     /**
@@ -386,23 +388,14 @@ public class SamlClient {
      */
     public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
                                            Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
-        return login(user, samlEndpoint, samlRequest, relayState, requestBinding, expectedResponseBinding, false, true);
+        return new SamlClient(samlEndpoint).login(user, samlRequest, relayState, requestBinding, expectedResponseBinding, false, true);
     }
 
-    /**
-     * Send request for login form and then login using user param. This method is designed for clients which requires consent
-     *
-     * @param user
-     * @param samlEndpoint
-     * @param samlRequest
-     * @param relayState
-     * @param requestBinding
-     * @param expectedResponseBinding
-     * @return
-     */
-    public static SAMLDocumentHolder loginWithRequiredConsent(UserRepresentation user, URI samlEndpoint,
-                                                              Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding, boolean consent) {
-        return login(user, samlEndpoint, samlRequest, relayState, requestBinding, expectedResponseBinding, true, consent);
+    private final HttpClientContext context = HttpClientContext.create();
+    private final URI samlEndpoint;
+
+    public SamlClient(URI samlEndpoint) {
+        this.samlEndpoint = samlEndpoint;
     }
 
     /**
@@ -418,15 +411,11 @@ public class SamlClient {
      * @param consent
      * @return
      */
-    public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
-                                           Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
-        CloseableHttpResponse response = null;
-        SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
-        try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
-            HttpClientContext context = HttpClientContext.create();
-
+    public SAMLDocumentHolder login(UserRepresentation user,
+                                    Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
+        return getSamlResponse(expectedResponseBinding, (client, context, strategy) -> {
             HttpUriRequest post = requestBinding.createSamlUnsignedRequest(samlEndpoint, relayState, samlRequest);
-            response = client.execute(post, context);
+            CloseableHttpResponse response = client.execute(post, context);
 
             assertThat(response, statusCodeIsHC(Response.Status.OK));
             String loginPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
@@ -444,8 +433,90 @@ public class SamlClient {
             }
 
             strategy.setRedirectable(false);
-            response = client.execute(loginRequest, context);
-            
+            return client.execute(loginRequest, context);
+        });
+    }
+
+    /**
+     * Send request for login form once already logged in, hence login using SSO.
+     * Check whether client requires consent and handle consent page.
+     *
+     * @param user
+     * @param samlEndpoint
+     * @param samlRequest
+     * @param relayState
+     * @param requestBinding
+     * @param expectedResponseBinding
+     * @return
+     */
+    public SAMLDocumentHolder subsequentLoginViaSSO(Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
+        return getSamlResponse(expectedResponseBinding, (client, context, strategy) -> {
+            strategy.setRedirectable(false);
+
+            HttpUriRequest post = requestBinding.createSamlUnsignedRequest(samlEndpoint, relayState, samlRequest);
+            CloseableHttpResponse response = client.execute(post, context);
+            assertThat(response, statusCodeIsHC(Response.Status.FOUND));
+            String location = response.getFirstHeader("Location").getValue();
+
+            response = client.execute(new HttpGet(location), context);
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    /**
+     * Send request for login form once already logged in, hence login using SSO.
+     * Check whether client requires consent and handle consent page.
+     *
+     * @param user
+     * @param samlEndpoint
+     * @param samlRequest
+     * @param relayState
+     * @param requestBinding
+     * @param expectedResponseBinding
+     * @return
+     */
+    public SAMLDocumentHolder logout(Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
+        return getSamlResponse(expectedResponseBinding, (client, context, strategy) -> {
+            strategy.setRedirectable(false);
+
+            HttpUriRequest post = requestBinding.createSamlUnsignedRequest(samlEndpoint, relayState, samlRequest);
+            CloseableHttpResponse response = client.execute(post, context);
+            assertThat(response, statusCodeIsHC(Response.Status.OK));
+            return response;
+        });
+    }
+
+    @FunctionalInterface
+    public interface HttpClientProcessor {
+        public CloseableHttpResponse process(CloseableHttpClient client, HttpContext context, RedirectStrategyWithSwitchableFollowRedirect strategy) throws Exception;
+    }
+
+    public void execute(HttpClientProcessor body) {
+        CloseableHttpResponse response = null;
+        RedirectStrategyWithSwitchableFollowRedirect strategy = new RedirectStrategyWithSwitchableFollowRedirect();
+
+        try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
+            response = body.process(client, context, strategy);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        } finally {
+            if (response != null) {
+                EntityUtils.consumeQuietly(response.getEntity());
+                try {
+                    response.close();
+                } catch (IOException ex) {
+                }
+            }
+        }
+    }
+
+    public SAMLDocumentHolder getSamlResponse(Binding expectedResponseBinding, HttpClientProcessor body) {
+        CloseableHttpResponse response = null;
+        RedirectStrategyWithSwitchableFollowRedirect strategy = new RedirectStrategyWithSwitchableFollowRedirect();
+        try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
+            response = body.process(client, context, strategy);
+
             return expectedResponseBinding.extractResponse(response);
         } catch (Exception ex) {
             throw new RuntimeException(ex);
@@ -469,7 +540,7 @@ public class SamlClient {
      * @return
      */
     public static SAMLDocumentHolder idpInitiatedLogin(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding) {
-        return idpInitiatedLogin(user, idpInitiatedURI, expectedResponseBinding, false, true);
+        return new SamlClient(idpInitiatedURI).idpInitiatedLogin(user, expectedResponseBinding, false, true);
     }
 
     /**
@@ -482,29 +553,24 @@ public class SamlClient {
      * @return
      */
     public static SAMLDocumentHolder idpInitiatedLoginWithRequiredConsent(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding, boolean consent) {
-        return idpInitiatedLogin(user, idpInitiatedURI, expectedResponseBinding, true, consent);
+        return new SamlClient(idpInitiatedURI).idpInitiatedLogin(user, expectedResponseBinding, true, consent);
     }
 
     /**
      * Send request for login form and then login using user param. Checks whether client requires consent and handle consent page.
      *
      * @param user
-     * @param idpInitiatedURI
+     * @param samlEndpoint
      * @param expectedResponseBinding
      * @param consent
      * @return
      */
-    public static SAMLDocumentHolder idpInitiatedLogin(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
-        CloseableHttpResponse response = null;
-        SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
-        try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
-
-            HttpGet get = new HttpGet(idpInitiatedURI);
-            response = client.execute(get);
+    public SAMLDocumentHolder idpInitiatedLogin(UserRepresentation user, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
+        return getSamlResponse(expectedResponseBinding, (client, context, strategy) -> {
+            HttpGet get = new HttpGet(samlEndpoint);
+            CloseableHttpResponse response = client.execute(get);
             assertThat(response, statusCodeIsHC(Response.Status.OK));
 
-            HttpClientContext context = HttpClientContext.create();
-
             String loginPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
             response.close();
 
@@ -520,20 +586,8 @@ public class SamlClient {
             }
 
             strategy.setRedirectable(false);
-            response = client.execute(loginRequest, context);
-
-            return expectedResponseBinding.extractResponse(response);
-        } catch (Exception ex) {
-            throw new RuntimeException(ex);
-        } finally {
-            if (response != null) {
-                EntityUtils.consumeQuietly(response.getEntity());
-                try {
-                    response.close();
-                } catch (IOException ex) {
-                }
-            }
-        }
+            return client.execute(loginRequest, context);
+        });
     }
 
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-acs/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-acs/WEB-INF/keycloak-saml.xml
new file mode 100644
index 0000000..4803920
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-acs/WEB-INF/keycloak-saml.xml
@@ -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.
+  -->
+
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
+                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                       xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_8.xsd">
+    <SP entityID="http://localhost:8081/employee-acs/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="memberOf"/>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                                 responseBinding="POST"
+                                 assertionConsumerServiceUrl="http://localhost:8081/employee-acs/a/different/endpoint/for/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
index 87a4fc8..25e1f21 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
@@ -455,6 +455,56 @@
             ]
         },
         {
+            "clientId": "http://localhost:8081/employee-acs/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/employee-acs",
+            "redirectUris": [
+                "http://localhost:8080/employee-acs/*"
+            ],
+            "adminUrl": "http://localhost:8080/employee-acs",
+            "attributes": {
+                "saml.authnstatement": "true"
+            },
+            "protocolMappers": [
+                {
+                    "name": "email",
+                    "protocol": "saml",
+                    "protocolMapper": "saml-user-property-mapper",
+                    "consentRequired": false,
+                    "config": {
+                        "user.attribute": "email",
+                        "friendly.name": "email",
+                        "attribute.name": "urn:oid:1.2.840.113549.1.9.1",
+                        "attribute.nameformat": "URI Reference"
+                    }
+                },
+                {
+                    "name": "phone",
+                    "protocol": "saml",
+                    "protocolMapper": "saml-user-attribute-mapper",
+                    "consentRequired": false,
+                    "config": {
+                        "user.attribute": "phone",
+                        "attribute.name": "phone",
+                        "attribute.nameformat": "Basic"
+                    }
+                },
+                {
+                    "name": "role-list",
+                    "protocol": "saml",
+                    "protocolMapper": "saml-role-list-mapper",
+                    "consentRequired": false,
+                    "config": {
+                        "attribute.name": "Role",
+                        "attribute.nameformat": "Basic",
+                        "single": "false"
+                    }
+                }
+            ]
+        },
+        {
             "clientId": "http://localhost:8081/employee2/",
             "enabled": true,
             "protocol": "saml",
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json
index 47b7d1b..8bdb635 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/import-authorization-unordered-settings.json
@@ -161,6 +161,20 @@
       "config": {
         "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": "Test Client Policy",
+      "type": "client",
+      "config": {
+        "clients": "[\"admin-cli\"]"
+      }
+    },
+    {
+      "name": "Test User Policy",
+      "type": "user",
+      "config": {
+        "users": "[\"alice\"]"
+      }
     }
   ],
   "scopes": [
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json
new file mode 100644
index 0000000..482741f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json
@@ -0,0 +1,15 @@
+{
+    "realm": "authz-client-jwt-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "jwt": {
+            "client-keystore-file": "classpath:client-auth-test/keystore-client2.jks",
+            "client-keystore-type": "JKS",
+            "client-keystore-password": "storepass",
+            "client-key-alias": "clientkey",
+            "client-key-password": "keypass",
+            "token-expiration": 10
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json
new file mode 100644
index 0000000..0fad5ef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json
@@ -0,0 +1,15 @@
+{
+    "realm": "authz-client-jwt-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "jwt": {
+            "client-keystore-file": "classpath:client-auth-test/keystore-client1.jks",
+            "client-keystore-type": "JKS",
+            "client-keystore-password": "storepass",
+            "client-key-alias": "clientkey",
+            "client-key-password": "keypass",
+            "token-expiration": 10
+        }
+    }
+}
\ 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 75a68ea..e37a0dc 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -84,6 +84,9 @@
         <exclude.test>-</exclude.test>
         <exclude.cors.tests>**/cors/*Test.java</exclude.cors.tests>
 
+        <!-- Use phantomjs as default browser for adapters, since javascript doesn't work correctly on htmlUnit (KEYCLOAK-4703) -->
+        <browser>phantomjs</browser>
+
     </properties>
 
     <modules>
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java
index c29ab76..a6c9527 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java
@@ -16,8 +16,19 @@
  */
 package org.keycloak.testsuite.console.page.clients.authorization.permission;
 
+import static org.openqa.selenium.By.tagName;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicyTypeUI;
 import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -27,7 +38,90 @@ public class Permissions extends Form {
     @FindBy(css = "table[class*='table']")
     private PermissionsTable table;
 
+    @FindBy(id = "create-permission")
+    private Select createSelect;
+
+    @Page
+    private ResourcePermission resourcePermission;
+
+    @Page
+    private ScopePermission scopePermission;
+
     public PermissionsTable permissions() {
         return table;
     }
+
+    public <P extends PolicyTypeUI> P create(AbstractPolicyRepresentation expected) {
+        String type = expected.getType();
+
+        createSelect.selectByValue(type);
+
+        if ("resource".equals(type)) {
+            resourcePermission.form().populate((ResourcePermissionRepresentation) expected);
+            resourcePermission.form().save();
+            return (P) resourcePermission;
+        } else if ("scope".equals(type)) {
+            scopePermission.form().populate((ScopePermissionRepresentation) expected);
+            scopePermission.form().save();
+            return (P) scopePermission;
+        }
+
+        return null;
+    }
+
+    public void update(String name, AbstractPolicyRepresentation representation) {
+        for (WebElement row : permissions().rows()) {
+            PolicyRepresentation actual = permissions().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+                String type = representation.getType();
+
+                if ("resource".equals(type)) {
+                    resourcePermission.form().populate((ResourcePermissionRepresentation) representation);
+                } else if ("scope".equals(type)) {
+                    scopePermission.form().populate((ScopePermissionRepresentation) representation);
+                }
+
+                return;
+            }
+        }
+    }
+
+    public <P extends PolicyTypeUI> P name(String name) {
+        for (WebElement row : permissions().rows()) {
+            PolicyRepresentation actual = permissions().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+                String type = actual.getType();
+                if ("resource".equals(type)) {
+                    return (P) resourcePermission;
+                } else if ("scope".equals(type)) {
+                    return (P) scopePermission;
+                }
+            }
+        }
+        return null;
+    }
+
+    public void delete(String name) {
+        for (WebElement row : permissions().rows()) {
+            PolicyRepresentation actual = permissions().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+
+                String type = actual.getType();
+
+                if ("resource".equals(type)) {
+                    resourcePermission.form().delete();
+                } else if ("scope".equals(type)) {
+                    scopePermission.form().delete();
+                }
+
+                return;
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermission.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermission.java
new file mode 100644
index 0000000..d6c4f3a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermission.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.testsuite.console.page.clients.authorization.permission;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicyTypeUI;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermission implements PolicyTypeUI {
+
+    @Page
+    private ResourcePermissionForm form;
+
+    public ResourcePermissionForm form() {
+        return form;
+    }
+
+    public ResourcePermissionRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(ResourcePermissionRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermissionForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermissionForm.java
new file mode 100644
index 0000000..cf39523
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ResourcePermissionForm.java
@@ -0,0 +1,101 @@
+/*
+ * 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.console.page.clients.authorization.permission;
+
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermissionForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "decisionStrategy")
+    private Select decisionStrategy;
+
+    @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='applyToResourceTypeFlag']]")
+    private OnOffSwitch resourceTypeSwitch;
+
+    @FindBy(id = "resourceType")
+    private WebElement resourceType;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    @FindBy(id = "s2id_policies")
+    private MultipleStringSelect2 policySelect;
+
+    @FindBy(id = "s2id_resources")
+    private MultipleStringSelect2 resourceSelect;
+
+    public void populate(ResourcePermissionRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
+
+        resourceTypeSwitch.setOn(expected.getResourceType() != null);
+
+        if (expected.getResourceType() != null) {
+            setInputValue(resourceType, expected.getResourceType());
+        } else {
+            resourceTypeSwitch.setOn(false);
+            resourceSelect.update(expected.getResources());
+        }
+
+        policySelect.update(expected.getPolicies());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public ResourcePermissionRepresentation toRepresentation() {
+        ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setDecisionStrategy(DecisionStrategy.valueOf(decisionStrategy.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setPolicies(policySelect.getSelected());
+        String inputValue = getInputValue(resourceType);
+
+        if (!"".equals(inputValue)) {
+            representation.setResourceType(inputValue);
+        }
+
+        representation.setResources(resourceSelect.getSelected());
+
+        return representation;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java
new file mode 100644
index 0000000..5b392ad
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.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.testsuite.console.page.clients.authorization.permission;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicyTypeUI;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePermission implements PolicyTypeUI {
+
+    @Page
+    private ScopePermissionForm form;
+
+    public ScopePermissionForm form() {
+        return form;
+    }
+
+    public ScopePermissionRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(ScopePermissionRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
new file mode 100644
index 0000000..f16cd5c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
@@ -0,0 +1,107 @@
+/*
+ * 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.console.page.clients.authorization.permission;
+
+import java.util.Set;
+import java.util.function.Function;
+
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.keycloak.testsuite.console.page.fragment.SingleStringSelect2;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePermissionForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "decisionStrategy")
+    private Select decisionStrategy;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    @FindBy(id = "s2id_policies")
+    private MultipleStringSelect2 policySelect;
+
+    @FindBy(id = "s2id_scopes")
+    private MultipleStringSelect2 scopeSelect;
+
+    @FindBy(id = "s2id_resourceScopes")
+    private MultipleStringSelect2 resourceScopeSelect;
+
+    @FindBy(id = "s2id_resources")
+    private ResourceSelect resourceSelect;
+
+    public void populate(ScopePermissionRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
+
+        Set<String> resources = expected.getResources();
+
+        if (resources != null && !resources.isEmpty()) {
+            resourceSelect.update(resources);
+            resourceScopeSelect.update(expected.getScopes());
+        } else {
+            scopeSelect.update(expected.getScopes());
+        }
+
+        policySelect.update(expected.getPolicies());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public ScopePermissionRepresentation toRepresentation() {
+        ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setDecisionStrategy(DecisionStrategy.valueOf(decisionStrategy.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setPolicies(policySelect.getSelected());
+        representation.setResources(resourceSelect.getSelected());
+        representation.setScopes(scopeSelect.getSelected());
+        representation.getScopes().addAll(resourceScopeSelect.getSelected());
+
+        return representation;
+    }
+
+    public class ResourceSelect extends SingleStringSelect2 {
+        @Override
+        protected Function<WebElement, String> representation() {
+            return super.representation().andThen(s -> "".equals(s) || s.contains("Any resource...") ? null : s);
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicy.java
new file mode 100644
index 0000000..3a27d2b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicy.java
@@ -0,0 +1,42 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicy implements PolicyTypeUI {
+
+    @Page
+    private AggregatePolicyForm form;
+
+    public AggregatePolicyForm form() {
+        return form;
+    }
+
+    public AggregatePolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(AggregatePolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicyForm.java
new file mode 100644
index 0000000..5e7170d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicyForm.java
@@ -0,0 +1,99 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import java.util.Set;
+
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(id = "s2id_policies")
+    private MultipleStringSelect2 policySelect;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(AggregatePolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+
+        Set<String> selectedPolicies = policySelect.getSelected();
+        Set<String> policies = expected.getPolicies();
+
+        for (String policy : policies) {
+            if (!selectedPolicies.contains(policy)) {
+                policySelect.select(policy);
+            }
+        }
+
+        for (String selected : selectedPolicies) {
+            boolean isSelected = false;
+
+            for (String policy : policies) {
+                if (selected.equals(policy)) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                policySelect.deselect(selected);
+            }
+        }
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public AggregatePolicyRepresentation toRepresentation() {
+        AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setPolicies(policySelect.getSelected());
+
+        return representation;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicy.java
new file mode 100644
index 0000000..fe85623
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientPolicy implements PolicyTypeUI {
+
+    @Page
+    private ClientPolicyForm form;
+
+    public ClientPolicyForm form() {
+        return form;
+    }
+
+    public ClientPolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(ClientPolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicyForm.java
new file mode 100644
index 0000000..9095a32
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/ClientPolicyForm.java
@@ -0,0 +1,114 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientPolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(id = "s2id_clients")
+    private ClientSelect clientsInput;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(ClientPolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+
+        clientsInput.update(expected.getClients());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public ClientPolicyRepresentation toRepresentation() {
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setClients(clientsInput.getSelected());
+
+        return representation;
+    }
+
+    public class ClientSelect extends MultipleStringSelect2 {
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return getRoot().findElements(By.xpath("(//table[@id='selected-clients'])/tbody/tr")).stream()
+                    .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
+                    .collect(Collectors.toList());
+        }
+
+        @Override
+        protected BiFunction<WebElement, String, Boolean> deselect() {
+            return (webElement, name) -> {
+                List<WebElement> tds = webElement.findElements(tagName("td"));
+
+                if (!tds.get(0).getText().isEmpty()) {
+                    if (tds.get(0).getText().equals(name)) {
+                        tds.get(1).findElement(By.tagName("button")).click();
+                        return true;
+                    }
+                }
+
+                return false;
+            };
+        }
+
+        @Override
+        protected Function<WebElement, String> representation() {
+            return webElement -> webElement.findElements(tagName("td")).get(0).getText();
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicy.java
new file mode 100644
index 0000000..94fbb08
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicy.java
@@ -0,0 +1,42 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicy implements PolicyTypeUI {
+
+    @Page
+    private JSPolicyForm form;
+
+    public JSPolicyForm form() {
+        return form;
+    }
+
+    public JSPolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(JSPolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicyForm.java
new file mode 100644
index 0000000..e83585b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/JSPolicyForm.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.console.page.clients.authorization.policy;
+
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(JSPolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+
+        JavascriptExecutor scriptExecutor = (JavascriptExecutor) driver;
+
+        scriptExecutor.executeScript("angular.element(document.getElementById('code')).scope().policy.code = '" + expected.getCode() + "'");
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public JSPolicyRepresentation toRepresentation() {
+        JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+
+        JavascriptExecutor scriptExecutor = (JavascriptExecutor) driver;
+
+        representation.setCode((String) scriptExecutor.executeScript("return angular.element(document.getElementById('code')).scope().policy.code;"));
+
+        return representation;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java
index 36a198d..af2a540 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java
@@ -16,9 +16,23 @@
  */
 package org.keycloak.testsuite.console.page.clients.authorization.policy;
 
-import org.keycloak.testsuite.console.page.clients.authorization.permission.PermissionsTable;
+import static org.openqa.selenium.By.tagName;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -26,9 +40,157 @@ import org.openqa.selenium.support.FindBy;
 public class Policies extends Form {
 
     @FindBy(css = "table[class*='table']")
-    private PermissionsTable table;
+    private PoliciesTable table;
+
+    @FindBy(id = "create-policy")
+    private Select createSelect;
+
+    @Page
+    private RolePolicy rolePolicy;
+
+    @Page
+    private UserPolicy userPolicy;
+
+    @Page
+    private AggregatePolicy aggregatePolicy;
+
+    @Page
+    private JSPolicy jsPolicy;
+
+    @Page
+    private TimePolicy timePolicy;
 
-    public PermissionsTable policies() {
+    @Page
+    private RulePolicy rulePolicy;
+
+    @Page
+    private ClientPolicy clientPolicy;
+
+    public PoliciesTable policies() {
         return table;
     }
+
+    public <P extends PolicyTypeUI> P create(AbstractPolicyRepresentation expected) {
+        String type = expected.getType();
+
+        createSelect.selectByValue(type);
+
+        if ("role".equals(type)) {
+            rolePolicy.form().populate((RolePolicyRepresentation) expected);
+            rolePolicy.form().save();
+            return (P) rolePolicy;
+        } else if ("user".equals(type)) {
+            userPolicy.form().populate((UserPolicyRepresentation) expected);
+            userPolicy.form().save();
+            return (P) userPolicy;
+        } else if ("aggregate".equals(type)) {
+            aggregatePolicy.form().populate((AggregatePolicyRepresentation) expected);
+            aggregatePolicy.form().save();
+            return (P) aggregatePolicy;
+        } else if ("js".equals(type)) {
+            jsPolicy.form().populate((JSPolicyRepresentation) expected);
+            jsPolicy.form().save();
+            return (P) jsPolicy;
+        } else if ("time".equals(type)) {
+            timePolicy.form().populate((TimePolicyRepresentation) expected);
+            timePolicy.form().save();
+            return (P) timePolicy;
+        } else if ("rules".equals(type)) {
+            rulePolicy.form().populate((RulePolicyRepresentation) expected);
+            rulePolicy.form().save();
+            return (P) rulePolicy;
+        } else if ("client".equals(type)) {
+            clientPolicy.form().populate((ClientPolicyRepresentation) expected);
+            clientPolicy.form().save();
+            return (P) clientPolicy;
+        }
+
+        return null;
+    }
+
+    public void update(String name, AbstractPolicyRepresentation representation) {
+        for (WebElement row : policies().rows()) {
+            PolicyRepresentation actual = policies().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+                String type = representation.getType();
+
+                if ("role".equals(type)) {
+                    rolePolicy.form().populate((RolePolicyRepresentation) representation);
+                } else if ("user".equals(type)) {
+                    userPolicy.form().populate((UserPolicyRepresentation) representation);
+                } else if ("aggregate".equals(type)) {
+                    aggregatePolicy.form().populate((AggregatePolicyRepresentation) representation);
+                } else if ("js".equals(type)) {
+                    jsPolicy.form().populate((JSPolicyRepresentation) representation);
+                } else if ("time".equals(type)) {
+                    timePolicy.form().populate((TimePolicyRepresentation) representation);
+                } else if ("rules".equals(type)) {
+                    rulePolicy.form().populate((RulePolicyRepresentation) representation);
+                } else if ("client".equals(type)) {
+                    clientPolicy.form().populate((ClientPolicyRepresentation) representation);
+                }
+
+                return;
+            }
+        }
+    }
+
+    public <P extends PolicyTypeUI> P name(String name) {
+        for (WebElement row : policies().rows()) {
+            PolicyRepresentation actual = policies().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+                String type = actual.getType();
+                if ("role".equals(type)) {
+                    return (P) rolePolicy;
+                } else if ("user".equals(type)) {
+                    return (P) userPolicy;
+                } else if ("aggregate".equals(type)) {
+                    return (P) aggregatePolicy;
+                } else if ("js".equals(type)) {
+                    return (P) jsPolicy;
+                } else if ("time".equals(type)) {
+                    return (P) timePolicy;
+                } else if ("rules".equals(type)) {
+                    return (P) rulePolicy;
+                } else if ("client".equals(type)) {
+                    return (P) clientPolicy;
+                }
+            }
+        }
+        return null;
+    }
+
+    public void delete(String name) {
+        for (WebElement row : policies().rows()) {
+            PolicyRepresentation actual = policies().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                row.findElements(tagName("a")).get(0).click();
+                WaitUtils.waitForPageToLoad(driver);
+
+                String type = actual.getType();
+
+                if ("role".equals(type)) {
+                    rolePolicy.form().delete();
+                } else if ("user".equals(type)) {
+                    userPolicy.form().delete();
+                } else if ("aggregate".equals(type)) {
+                    aggregatePolicy.form().delete();
+                } else if ("js".equals(type)) {
+                    jsPolicy.form().delete();
+                } else if ("time".equals(type)) {
+                    timePolicy.form().delete();
+                } else if ("rules".equals(type)) {
+                    rulePolicy.form().delete();
+                } else if ("client".equals(type)) {
+                    clientPolicy.form().delete();
+                }
+
+                return;
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicy.java
new file mode 100644
index 0000000..8d4be91
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicy implements PolicyTypeUI {
+
+    @Page
+    private RolePolicyForm form;
+
+    public RolePolicyForm form() {
+        return form;
+    }
+
+    public RolePolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(RolePolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicyForm.java
new file mode 100644
index 0000000..8b6f114
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RolePolicyForm.java
@@ -0,0 +1,224 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.console.page.fragment.AbstractMultipleSelect2;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(id = "s2id_roles")
+    private RoleMultipleSelect2 realmRoleSelect;
+
+    @FindBy(id = "clients")
+    private Select clientsSelect;
+
+    @FindBy(id = "s2id_clientRoles")
+    private ClientRoleSelect clientRoleSelect;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(RolePolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+
+        Set<RolePolicyRepresentation.RoleDefinition> roles = expected.getRoles();
+
+        for (RolePolicyRepresentation.RoleDefinition role : roles) {
+            boolean clientRole = role.getId().indexOf('/') != -1;
+
+            if (clientRole) {
+                String[] parts = role.getId().split("/");
+                clientsSelect.selectByVisibleText(parts[0]);
+                clientRoleSelect.select(role);
+                clientRoleSelect.setRequired(role);
+            } else {
+                realmRoleSelect.select(role);
+                realmRoleSelect.setRequired(role);
+            }
+        }
+
+        unSelect(roles, realmRoleSelect.getSelected());
+        unSelect(roles, clientRoleSelect.getSelected());
+
+        save();
+    }
+
+    private void unSelect(Set<RolePolicyRepresentation.RoleDefinition> roles, Set<RolePolicyRepresentation.RoleDefinition> selection) {
+        for (RolePolicyRepresentation.RoleDefinition selected : selection) {
+            boolean isSelected = false;
+
+            for (RolePolicyRepresentation.RoleDefinition scope : roles) {
+                if (selected.getId().equals(scope.getId())) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                boolean clientRole = selected.getId().indexOf('/') != -1;
+
+                if (clientRole) {
+                    clientRoleSelect.deselect(selected);
+                } else {
+                    realmRoleSelect.deselect(selected);
+                }
+            }
+        }
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public RolePolicyRepresentation toRepresentation() {
+        RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+
+        Set<RolePolicyRepresentation.RoleDefinition> roles = realmRoleSelect.getSelected();
+
+        roles.addAll(clientRoleSelect.getSelected());
+
+        representation.setRoles(roles);
+
+        return representation;
+    }
+
+    public class RoleMultipleSelect2 extends AbstractMultipleSelect2<RolePolicyRepresentation.RoleDefinition> {
+
+        @Override
+        protected Function<RolePolicyRepresentation.RoleDefinition, String> identity() {
+            return role -> {
+                String id = role.getId();
+                return id.indexOf('/') != -1 ? id.split("/")[1] : id;
+            };
+        }
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return getRoot().findElements(By.xpath("(//table[@id='selected-realm-roles'])/tbody/tr")).stream()
+                    .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
+                    .collect(Collectors.toList());
+        }
+
+        @Override
+        protected Function<WebElement, RolePolicyRepresentation.RoleDefinition> representation() {
+            return webElement -> {
+                List<WebElement> tds = webElement.findElements(tagName("td"));
+                RolePolicyRepresentation.RoleDefinition selectedRole = new RolePolicyRepresentation.RoleDefinition();
+                boolean clientRole = tds.size() == 4;
+
+                selectedRole.setId(clientRole ? tds.get(1).getText() + "/" + tds.get(0).getText() : tds.get(0).getText());
+                selectedRole.setRequired(tds.get(clientRole ? 2 : 1).findElement(By.tagName("input")).isSelected());
+
+                return selectedRole;
+            };
+        }
+
+        public void setRequired(RolePolicyRepresentation.RoleDefinition role) {
+            Iterator<WebElement> iterator = getSelectedElements().iterator();
+
+            while (iterator.hasNext()) {
+                WebElement realmRole = iterator.next();
+                List<WebElement> tds = realmRole.findElements(tagName("td"));
+                boolean clientRole = role.getId().indexOf("/") != -1;
+                WebElement roleName = tds.get(0);
+
+                if (!roleName.getText().isEmpty()) {
+                    if (roleName.getText().equals(getRoleId(role, clientRole))) {
+                        WebElement required = tds.get(clientRole ? 2 : 1).findElement(By.tagName("input"));
+
+                        if (required.isSelected() && role.isRequired()) {
+                            return;
+                        } else if (!required.isSelected() && !role.isRequired()) {
+                            return;
+                        }
+
+                        required.click();
+                        return;
+                    }
+                }
+            }
+        }
+
+        @Override
+        protected BiFunction<WebElement, RolePolicyRepresentation.RoleDefinition, Boolean> deselect() {
+            return (webElement, roleDefinition) -> {
+                List<WebElement> tds = webElement.findElements(tagName("td"));
+                boolean clientRole = tds.size() == 4;
+
+                if (!tds.get(0).getText().isEmpty()) {
+                    if (tds.get(0).getText().equals(getRoleId(roleDefinition, clientRole))) {
+                        tds.get(clientRole ? 3 : 2).findElement(By.tagName("button")).click();
+                        return true;
+                    }
+                }
+
+                return false;
+            };
+        }
+
+        private String getRoleId(RolePolicyRepresentation.RoleDefinition roleDefinition, boolean clientRole) {
+            return clientRole ? roleDefinition.getId().split("/")[1] : roleDefinition.getId();
+        }
+    }
+
+    public class ClientRoleSelect extends RoleMultipleSelect2 {
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return getRoot().findElements(By.xpath("(//table[@id='selected-client-roles'])/tbody/tr")).stream()
+                    .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
+                    .collect(Collectors.toList());
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicy.java
new file mode 100644
index 0000000..6d46347
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RulePolicy implements PolicyTypeUI {
+
+    @Page
+    private RulePolicyForm form;
+
+    public RulePolicyForm form() {
+        return form;
+    }
+
+    public RulePolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(RulePolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicyForm.java
new file mode 100644
index 0000000..17b4d46
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/RulePolicyForm.java
@@ -0,0 +1,114 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RulePolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "artifactGroupId")
+    private WebElement artifactGroupId;
+
+    @FindBy(id = "artifactId")
+    private WebElement artifactId;
+
+    @FindBy(id = "artifactVersion")
+    private WebElement artifactVersion;
+
+    @FindBy(id = "moduleName")
+    private Select moduleName;
+
+    @FindBy(id = "sessionName")
+    private Select sessionName;
+
+    @FindBy(id = "scannerPeriod")
+    private WebElement scannerPeriod;
+
+    @FindBy(id = "scannerPeriodUnit")
+    private Select scannerPeriodUnit;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    @FindBy(id = "resolveModule")
+    private WebElement resolveModuleButton;
+
+    public void populate(RulePolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        setInputValue(artifactGroupId, expected.getArtifactGroupId());
+        setInputValue(artifactId, expected.getArtifactId());
+        setInputValue(artifactVersion, expected.getArtifactVersion());
+
+        resolveModuleButton.click();
+        WaitUtils.waitForPageToLoad(driver);
+
+        moduleName.selectByVisibleText(expected.getModuleName());
+        WaitUtils.pause(1000);
+
+        sessionName.selectByVisibleText(expected.getSessionName());
+
+        setInputValue(scannerPeriod, expected.getScannerPeriod());
+        scannerPeriodUnit.selectByVisibleText(expected.getScannerPeriodUnit());
+        logic.selectByValue(expected.getLogic().name());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public RulePolicyRepresentation toRepresentation() {
+        RulePolicyRepresentation representation = new RulePolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setArtifactGroupId(getInputValue(artifactGroupId));
+        representation.setArtifactId(getInputValue(artifactId));
+        representation.setArtifactVersion(getInputValue(artifactVersion));
+        representation.setModuleName(moduleName.getFirstSelectedOption().getText());
+        representation.setSessionName(sessionName.getFirstSelectedOption().getText());
+        representation.setScannerPeriod(getInputValue(scannerPeriod));
+        representation.setScannerPeriodUnit(scannerPeriodUnit.getFirstSelectedOption().getText());
+
+        return representation;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicy.java
new file mode 100644
index 0000000..ce2a20a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicy implements PolicyTypeUI {
+
+    @Page
+    private TimePolicyForm form;
+
+    public TimePolicyForm form() {
+        return form;
+    }
+
+    public TimePolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(TimePolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicyForm.java
new file mode 100644
index 0000000..5c31f33
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/TimePolicyForm.java
@@ -0,0 +1,129 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(id = "notBefore")
+    private WebElement notBefore;
+
+    @FindBy(id = "notOnOrAfter")
+    private WebElement notOnOrAfter;
+
+    @FindBy(id = "dayMonth")
+    private WebElement dayMonth;
+
+    @FindBy(id = "month")
+    private WebElement month;
+
+    @FindBy(id = "year")
+    private WebElement year;
+
+    @FindBy(id = "hour")
+    private WebElement hour;
+
+    @FindBy(id = "minute")
+    private WebElement minute;
+
+    @FindBy(id = "dayMonthEnd")
+    private WebElement dayMonthEnd;
+
+    @FindBy(id = "monthEnd")
+    private WebElement monthEnd;
+
+    @FindBy(id = "yearEnd")
+    private WebElement yearEnd;
+
+    @FindBy(id = "hourEnd")
+    private WebElement hourEnd;
+
+    @FindBy(id = "minuteEnd")
+    private WebElement minuteEnd;
+
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(TimePolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+        setInputValue(notBefore, expected.getNotBefore());
+        setInputValue(notOnOrAfter, expected.getNotOnOrAfter());
+        setInputValue(dayMonth, expected.getDayMonth());
+        setInputValue(dayMonthEnd, expected.getDayMonthEnd());
+        setInputValue(month, expected.getMonth());
+        setInputValue(monthEnd, expected.getMonthEnd());
+        setInputValue(year, expected.getYear());
+        setInputValue(yearEnd, expected.getYearEnd());
+        setInputValue(hour, expected.getHour());
+        setInputValue(hourEnd, expected.getHourEnd());
+        setInputValue(minute, expected.getMinute());
+        setInputValue(minuteEnd, expected.getMinuteEnd());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public TimePolicyRepresentation toRepresentation() {
+        TimePolicyRepresentation representation = new TimePolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setDayMonth(getInputValue(dayMonth));
+        representation.setDayMonthEnd(getInputValue(dayMonthEnd));
+        representation.setMonth(getInputValue(month));
+        representation.setMonthEnd(getInputValue(monthEnd));
+        representation.setYear(getInputValue(year));
+        representation.setYearEnd(getInputValue(yearEnd));
+        representation.setHour(getInputValue(hour));
+        representation.setHourEnd(getInputValue(hourEnd));
+        representation.setMinute(getInputValue(minute));
+        representation.setMinuteEnd(getInputValue(minuteEnd));
+        representation.setNotBefore(getInputValue(notBefore));
+        representation.setNotOnOrAfter(getInputValue(notOnOrAfter));
+
+        return representation;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicy.java
new file mode 100644
index 0000000..cd5b8eb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicy implements PolicyTypeUI {
+
+    @Page
+    private UserPolicyForm form;
+
+    public UserPolicyForm form() {
+        return form;
+    }
+
+    public UserPolicyRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
+
+    public void update(UserPolicyRepresentation expected) {
+        form().populate(expected);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicyForm.java
new file mode 100644
index 0000000..e403d1b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/UserPolicyForm.java
@@ -0,0 +1,114 @@
+/*
+ * 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.console.page.clients.authorization.policy;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyForm extends Form {
+
+    @FindBy(id = "name")
+    private WebElement name;
+
+    @FindBy(id = "description")
+    private WebElement description;
+
+    @FindBy(id = "logic")
+    private Select logic;
+
+    @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+    private WebElement deleteButton;
+
+    @FindBy(id = "s2id_users")
+    private UserSelect usersInput;
+
+    @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+    private WebElement confirmDelete;
+
+    public void populate(UserPolicyRepresentation expected) {
+        setInputValue(name, expected.getName());
+        setInputValue(description, expected.getDescription());
+        logic.selectByValue(expected.getLogic().name());
+
+        usersInput.update(expected.getUsers());
+
+        save();
+    }
+
+    public void delete() {
+        deleteButton.click();
+        confirmDelete.click();
+    }
+
+    public UserPolicyRepresentation toRepresentation() {
+        UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDescription(getInputValue(description));
+        representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
+        representation.setUsers(usersInput.getSelected());
+
+        return representation;
+    }
+
+    public class UserSelect extends MultipleStringSelect2 {
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return getRoot().findElements(By.xpath("(//table[@id='selected-users'])/tbody/tr")).stream()
+                    .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
+                    .collect(Collectors.toList());
+        }
+
+        @Override
+        protected BiFunction<WebElement, String, Boolean> deselect() {
+            return (webElement, name) -> {
+                List<WebElement> tds = webElement.findElements(tagName("td"));
+
+                if (!tds.get(0).getText().isEmpty()) {
+                    if (tds.get(0).getText().equals(name)) {
+                        tds.get(1).findElement(By.tagName("button")).click();
+                        return true;
+                    }
+                }
+
+                return false;
+            };
+        }
+
+        @Override
+        protected Function<WebElement, String> representation() {
+            return webElement -> webElement.findElements(tagName("td")).get(0).getText();
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java
index 5bb5449..276985e 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java
@@ -75,6 +75,8 @@ public abstract class AbstractAuthorizationSettingsTest extends AbstractClientTe
         clientSettingsPage.tabs().authorization();
         assertTrue(authorizationPage.isCurrent());
 
+        newClient.setId(found.getId());
+
         return newClient;
     }
 }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AggregatePolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AggregatePolicyManagementTest.java
new file mode 100644
index 0000000..f1bba0d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AggregatePolicyManagementTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PoliciesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.AggregatePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.UserPolicy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AggregatePolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        RolesResource realmRoles = testRealmResource().roles();
+        realmRoles.create(new RoleRepresentation("Role A", "", false));
+        realmRoles.create(new RoleRepresentation("Role B", "", false));
+
+        RolePolicyRepresentation policyA = new RolePolicyRepresentation();
+
+        policyA.setName("Policy A");
+        policyA.addRole("Role A");
+
+        AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
+        PoliciesResource policies = authorization.policies();
+        RolePoliciesResource roles = policies.role();
+
+        roles.create(policyA);
+
+        RolePolicyRepresentation policyB = new RolePolicyRepresentation();
+
+        policyB.setName("Policy B");
+        policyB.addRole("Role B");
+
+        roles.create(policyB);
+
+        UserPolicyRepresentation policyC = new UserPolicyRepresentation();
+
+        policyC.setName("Policy C");
+        policyC.addUser("test");
+
+        policies.user().create(policyC);
+    }
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        AggregatePolicyRepresentation expected = new AggregatePolicyRepresentation();
+
+        expected.setName("Test Update Aggregate Policy");
+        expected.setDescription("description");
+        expected.addPolicy("Policy A");
+        expected.addPolicy("Policy B");
+        expected.addPolicy("Policy C");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Update Aggregate Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+        expected.getPolicies().clear();
+        expected.addPolicy("Policy A", "Policy C");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        AggregatePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        AggregatePolicyRepresentation expected = new AggregatePolicyRepresentation();
+
+        expected.setName("Test Delete Aggregate Policy");
+        expected.setDescription("description");
+        expected.addPolicy("Policy C");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private AggregatePolicyRepresentation createPolicy(AggregatePolicyRepresentation expected) {
+        AggregatePolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private AggregatePolicyRepresentation assertPolicy(AggregatePolicyRepresentation expected, AggregatePolicy policy) {
+        AggregatePolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+
+        assertNotNull(actual.getPolicies());
+        assertEquals(expected.getPolicies().size(), actual.getPolicies().size());
+        assertEquals(0, actual.getPolicies().stream().filter(actualPolicy -> !expected.getPolicies().stream()
+                .filter(expectedPolicy -> actualPolicy.equals(expectedPolicy))
+                .findFirst().isPresent())
+                .count());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ClientPolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ClientPolicyManagementTest.java
new file mode 100644
index 0000000..2c95b83
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ClientPolicyManagementTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientPolicy;
+import org.keycloak.testsuite.util.ClientBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientPolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        ClientsResource clients = testRealmResource().clients();
+        clients.create(ClientBuilder.create().clientId("client a").build());
+        clients.create(ClientBuilder.create().clientId("client b").build());
+        clients.create(ClientBuilder.create().clientId("client c").build());
+    }
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ClientPolicyRepresentation expected = new ClientPolicyRepresentation();
+
+        expected.setName("Test Client Policy");
+        expected.setDescription("description");
+        expected.addClient("client a");
+        expected.addClient("client b");
+        expected.addClient("client c");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Client Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+
+        expected.setClients(expected.getClients().stream().filter(client -> !client.equals("client b")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ClientPolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDeletePolicy() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ClientPolicyRepresentation expected = new ClientPolicyRepresentation();
+
+        expected.setName("Test Client Policy");
+        expected.setDescription("description");
+        expected.addClient("client c");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private ClientPolicyRepresentation createPolicy(ClientPolicyRepresentation expected) {
+        ClientPolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private ClientPolicyRepresentation assertPolicy(ClientPolicyRepresentation expected, ClientPolicy policy) {
+        ClientPolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+
+        assertNotNull(actual.getClients());
+        assertEquals(expected.getClients().size(), actual.getClients().size());
+        assertEquals(0, actual.getClients().stream().filter(actualClient -> !expected.getClients().stream()
+                .filter(expectedClient -> actualClient.equals(expectedClient))
+                .findFirst().isPresent())
+                .count());
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/JSPolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/JSPolicyManagementTest.java
new file mode 100644
index 0000000..0b9113c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/JSPolicyManagementTest.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.testsuite.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.JSPolicy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JSPolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        JSPolicyRepresentation expected = new JSPolicyRepresentation();
+
+        expected.setName("Test JS Policy");
+        expected.setDescription("description");
+        expected.setCode("$evaluation.grant();");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test JS Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+        expected.setCode("$evaluation.deny();");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        JSPolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        JSPolicyRepresentation expected = new JSPolicyRepresentation();
+
+        expected.setName("Test JS Policy");
+        expected.setDescription("description");
+        expected.setCode("$evaluation.deny();");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private JSPolicyRepresentation createPolicy(JSPolicyRepresentation expected) {
+        JSPolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private JSPolicyRepresentation assertPolicy(JSPolicyRepresentation expected, JSPolicy policy) {
+        JSPolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+        assertEquals(expected.getCode(), actual.getCode());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourcePermissionManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourcePermissionManagementTest.java
new file mode 100644
index 0000000..4ff011a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourcePermissionManagementTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PoliciesResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.ResourcePermission;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermissionManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        RolesResource realmRoles = testRealmResource().roles();
+        realmRoles.create(new RoleRepresentation("Role A", "", false));
+        realmRoles.create(new RoleRepresentation("Role B", "", false));
+
+        RolePolicyRepresentation policyA = new RolePolicyRepresentation();
+
+        policyA.setName("Policy A");
+        policyA.addRole("Role A");
+
+        AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
+        PoliciesResource policies = authorization.policies();
+        RolePoliciesResource roles = policies.role();
+
+        roles.create(policyA);
+
+        RolePolicyRepresentation policyB = new RolePolicyRepresentation();
+
+        policyB.setName("Policy B");
+        policyB.addRole("Role B");
+
+        roles.create(policyB);
+
+        UserPolicyRepresentation policyC = new UserPolicyRepresentation();
+
+        policyC.setName("Policy C");
+        policyC.addUser("test");
+
+        policies.user().create(policyC);
+
+        ResourcesResource resources = authorization.resources();
+
+        resources.create(new ResourceRepresentation("Resource A"));
+        resources.create(new ResourceRepresentation("Resource B"));
+    }
+
+    @Test
+    public void testUpdateResource() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ResourcePermissionRepresentation expected = new ResourcePermissionRepresentation();
+
+        expected.setName("Test Resource A Permission");
+        expected.setDescription("description");
+        expected.addResource("Resource A");
+        expected.addPolicy("Policy A");
+        expected.addPolicy("Policy B");
+        expected.addPolicy("Policy C");
+
+        expected = createPermission(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Resource A Permission");
+        expected.setDescription("Changed description");
+        expected.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        expected.getResources().clear();
+        expected.addResource("Resource B");
+        expected.getPolicies().clear();
+        expected.addPolicy("Policy A", "Policy C");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ResourcePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testUpdateResourceType() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ResourcePermissionRepresentation expected = new ResourcePermissionRepresentation();
+
+        expected.setName("Test Resource B Type Permission");
+        expected.setDescription("description");
+        expected.setResourceType("test-resource-type");
+        expected.addPolicy("Policy A");
+        expected.addPolicy("Policy B");
+        expected.addPolicy("Policy C");
+
+        expected = createPermission(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Resource B Type Permission");
+        expected.setDescription("Changed description");
+        expected.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+
+        expected.setResourceType("changed-resource-type");
+        expected.setPolicies(expected.getPolicies().stream().filter(policy -> !policy.equals("Policy B")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ResourcePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ResourcePermissionRepresentation expected = new ResourcePermissionRepresentation();
+
+        expected.setName("Test Delete Resource Permission");
+        expected.setDescription("description");
+        expected.addResource("Resource B");
+        expected.addPolicy("Policy C");
+
+        expected = createPermission(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().permissions().permissions().findByName(expected.getName()));
+    }
+
+    private ResourcePermissionRepresentation createPermission(ResourcePermissionRepresentation expected) {
+        ResourcePermission policy = authorizationPage.authorizationTabs().permissions().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private ResourcePermissionRepresentation assertPolicy(ResourcePermissionRepresentation expected, ResourcePermission policy) {
+        ResourcePermissionRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getDecisionStrategy(), actual.getDecisionStrategy());
+        assertEquals(expected.getResourceType(), actual.getResourceType());
+        assertEquals(0, actual.getPolicies().stream().filter(actualPolicy -> !expected.getPolicies().stream()
+                .filter(expectedPolicy -> actualPolicy.equals(expectedPolicy))
+                .findFirst().isPresent())
+                .count());
+        assertEquals(0, actual.getResources().stream().filter(actualResource -> !expected.getResources().stream()
+                .filter(expectedResource -> actualResource.equals(expectedResource))
+                .findFirst().isPresent())
+                .count());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RolePolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RolePolicyManagementTest.java
new file mode 100644
index 0000000..44e4f70
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RolePolicyManagementTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RolePolicy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        RolesResource realmRoles = testRealmResource().roles();
+        realmRoles.create(new RoleRepresentation("Realm Role A", "", false));
+        realmRoles.create(new RoleRepresentation("Realm Role B", "", false));
+        realmRoles.create(new RoleRepresentation("Realm Role C", "", false));
+        RolesResource clientRoles = testRealmResource().clients().get(newClient.getId()).roles();
+        clientRoles.create(new RoleRepresentation("Client Role A", "", false));
+        clientRoles.create(new RoleRepresentation("Client Role B", "", false));
+        clientRoles.create(new RoleRepresentation("Client Role C", "", false));
+    }
+
+    @Test
+    public void testUpdateRealmRoles() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RolePolicyRepresentation expected = new RolePolicyRepresentation();
+
+        expected.setName("Test Update Realm Role Policy");
+        expected.setDescription("description");
+        expected.addRole("Realm Role A");
+        expected.addRole("Realm Role B");
+        expected.addRole("Realm Role C");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Realm Role Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+
+        expected.setRoles(expected.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().equals("Realm Role B")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        RolePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().iterator().next().setRequired(true);
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().clear();
+        expected.addRole("Realm Role B", true);
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testUpdateClientRoles() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RolePolicyRepresentation expected = new RolePolicyRepresentation();
+
+        expected.setName("Test Update Client Role Policy");
+        expected.setDescription("description");
+
+        String clientId = newClient.getClientId();
+
+        expected.addClientRole(clientId, "Client Role A");
+        expected.addClientRole(clientId, "Client Role B");
+        expected.addClientRole(clientId, "Client Role C");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Update Client Role Policy");
+        expected.setDescription("Changed description");
+
+        expected.setRoles(expected.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().contains("Client Role B")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        RolePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().iterator().next().setRequired(true);
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().clear();
+        expected.addClientRole(clientId, "Client Role B", true);
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testRealmAndClientRoles() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RolePolicyRepresentation expected = new RolePolicyRepresentation();
+
+        expected.setName("Test Realm And Client Role Policy");
+        expected.setDescription("description");
+
+        String clientId = newClient.getClientId();
+
+        expected.addRole("Realm Role A");
+        expected.addRole("Realm Role C");
+        expected.addClientRole(clientId, "Client Role A");
+        expected.addClientRole(clientId, "Client Role B");
+        expected.addClientRole(clientId, "Client Role C");
+
+        expected = createPolicy(expected);
+        expected.setRoles(expected.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().contains("Client Role B") && !roleDefinition.getId().contains("Realm Role A")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        RolePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().forEach(roleDefinition -> {
+            if (roleDefinition.getId().equals("Realm Role C")) {
+                roleDefinition.setRequired(true);
+            }
+        });
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        expected = assertPolicy(expected, actual);
+
+        expected.getRoles().clear();
+        expected.addClientRole(clientId, "Client Role B", true);
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(expected.getName(), expected);
+        assertAlertSuccess();
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RolePolicyRepresentation expected = new RolePolicyRepresentation();
+
+        expected.setName("Test Delete Role Policy");
+        expected.setDescription("description");
+        expected.addRole("Realm Role A");
+        expected.addRole("Realm Role B");
+        expected.addRole("Realm Role C");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private RolePolicyRepresentation createPolicy(RolePolicyRepresentation expected) {
+        RolePolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private RolePolicyRepresentation assertPolicy(RolePolicyRepresentation expected, RolePolicy policy) {
+        RolePolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+
+        assertNotNull(actual.getRoles());
+        assertEquals(expected.getRoles().size(), actual.getRoles().size());
+        assertEquals(0, actual.getRoles().stream().filter(actualDefinition -> !expected.getRoles().stream()
+                .filter(roleDefinition -> actualDefinition.getId().contains(roleDefinition.getId().indexOf("/") != -1 ? roleDefinition.getId().split("/")[1] : roleDefinition.getId()) && actualDefinition.isRequired() == roleDefinition.isRequired())
+                .findFirst().isPresent())
+                .count());
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RulePolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RulePolicyManagementTest.java
new file mode 100644
index 0000000..09fb47a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/RulePolicyManagementTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.keycloak.common.Version;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RulePolicy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RulePolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RulePolicyRepresentation expected = createDefaultRepresentation("Test Rule Policy");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed " + previousName);
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+        expected.setModuleName("PhotozAuthzContextualPolicy");
+        expected.setSessionName("MainContextualSession");
+        expected.setScannerPeriod("12");
+        expected.setScannerPeriodUnit("Days");
+
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        RulePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RulePolicyRepresentation expected =createDefaultRepresentation("Delete Rule Policy");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private RulePolicyRepresentation createDefaultRepresentation(String name) {
+        RulePolicyRepresentation expected = new RulePolicyRepresentation();
+
+        expected.setName(name);
+        expected.setDescription("description");
+        expected.setArtifactGroupId("org.keycloak");
+        expected.setArtifactId("photoz-authz-policy");
+        expected.setArtifactVersion(Version.VERSION);
+        expected.setModuleName("PhotozAuthzOwnerPolicy");
+        expected.setSessionName("MainOwnerSession");
+        expected.setScannerPeriod("1");
+        expected.setScannerPeriodUnit("Minutes");
+
+        return expected;
+    }
+
+    private RulePolicyRepresentation createPolicy(RulePolicyRepresentation expected) {
+        RulePolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private RulePolicyRepresentation assertPolicy(RulePolicyRepresentation expected, RulePolicy policy) {
+        RulePolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+        assertEquals(expected.getArtifactGroupId(), actual.getArtifactGroupId());
+        assertEquals(expected.getArtifactId(), actual.getArtifactId());
+        assertEquals(expected.getArtifactVersion(), actual.getArtifactVersion());
+        assertEquals(expected.getModuleName(), actual.getModuleName());
+        assertEquals(expected.getSessionName(), actual.getSessionName());
+        assertEquals(expected.getScannerPeriod(), actual.getScannerPeriod());
+        assertEquals(expected.getScannerPeriodUnit(), actual.getScannerPeriodUnit());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
new file mode 100644
index 0000000..3dfd0c8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PoliciesResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.ResourcePermission;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.ScopePermission;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePermissionManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        RolesResource realmRoles = testRealmResource().roles();
+        realmRoles.create(new RoleRepresentation("Role A", "", false));
+        realmRoles.create(new RoleRepresentation("Role B", "", false));
+
+        RolePolicyRepresentation policyA = new RolePolicyRepresentation();
+
+        policyA.setName("Policy A");
+        policyA.addRole("Role A");
+
+        AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
+        PoliciesResource policies = authorization.policies();
+        RolePoliciesResource roles = policies.role();
+
+        roles.create(policyA);
+
+        RolePolicyRepresentation policyB = new RolePolicyRepresentation();
+
+        policyB.setName("Policy B");
+        policyB.addRole("Role B");
+
+        roles.create(policyB);
+
+        UserPolicyRepresentation policyC = new UserPolicyRepresentation();
+
+        policyC.setName("Policy C");
+        policyC.addUser("test");
+
+        policies.user().create(policyC);
+
+        authorization.scopes().create(new ScopeRepresentation("Scope A"));
+        authorization.scopes().create(new ScopeRepresentation("Scope B"));
+        authorization.scopes().create(new ScopeRepresentation("Scope C"));
+
+        ResourcesResource resources = authorization.resources();
+
+        resources.create(new ResourceRepresentation("Resource A", "Scope A"));
+        resources.create(new ResourceRepresentation("Resource B", "Scope B", "Scope C"));
+    }
+
+    @Test
+    public void testUpdateScopeOnly() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
+
+        expected.setName("Test Scope Only Permission");
+        expected.setDescription("description");
+        expected.addScope("Scope C", "Scope A", "Scope B");
+        expected.addPolicy("Policy C", "Policy A", "Policy B");
+
+        expected = createPermission(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName(previousName + "Changed");
+        expected.setDescription("changed");
+        expected.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        expected.getScopes().clear();
+        expected.addScope("Scope B");
+        expected.getPolicies().clear();
+        expected.addPolicy("Policy C");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ScopePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testUpdateResourceScope() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
+
+        expected.setName("Test Resource Scope Permission");
+        expected.setDescription("description");
+        expected.addResource("Resource A");
+        expected.addScope("Scope A");
+        expected.addPolicy("Policy C", "Policy A", "Policy B");
+
+        expected = createPermission(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName(previousName + "Changed");
+        expected.setDescription("changed");
+        expected.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        expected.getResources().clear();
+        expected.addResource("Resource B");
+        expected.getScopes().clear();
+        expected.addScope("Scope B", "Scope C");
+        expected.getPolicies().clear();
+        expected.addPolicy("Policy C");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        ScopePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
+
+        expected.setName("Test Delete Scope Permission");
+        expected.setDescription("description");
+        expected.addScope("Scope C");
+        expected.addPolicy("Policy C");
+
+        expected = createPermission(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().permissions().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().permissions().permissions().findByName(expected.getName()));
+    }
+
+    private ScopePermissionRepresentation createPermission(ScopePermissionRepresentation expected) {
+        ScopePermission policy = authorizationPage.authorizationTabs().permissions().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private ScopePermissionRepresentation assertPolicy(ScopePermissionRepresentation expected, ScopePermission policy) {
+        ScopePermissionRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getDecisionStrategy(), actual.getDecisionStrategy());
+
+        assertEquals(expected.getPolicies().size(), actual.getPolicies().size());
+        assertEquals(0, actual.getPolicies().stream().filter(actualPolicy -> !expected.getPolicies().stream()
+                .filter(expectedPolicy -> actualPolicy.equals(expectedPolicy))
+                .findFirst().isPresent())
+                .count());
+
+        if (expected.getResources() != null) {
+            assertEquals(expected.getResources().size(), actual.getResources().size());
+            assertEquals(0, actual.getResources().stream().filter(actualResource -> !expected.getResources().stream()
+                    .filter(expectedResource -> actualResource.equals(expectedResource))
+                    .findFirst().isPresent())
+                    .count());
+        }
+
+        assertEquals(expected.getScopes().size(), actual.getScopes().size());
+        assertEquals(0, actual.getScopes().stream().filter(actualScope -> !expected.getScopes().stream()
+                .filter(expectedScope -> actualScope.equals(expectedScope))
+                .findFirst().isPresent())
+                .count());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/TimePolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/TimePolicyManagementTest.java
new file mode 100644
index 0000000..6242c77
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/TimePolicyManagementTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.JSPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.TimePolicy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class TimePolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        TimePolicyRepresentation expected = new TimePolicyRepresentation();
+
+        expected.setName("Test Time Policy");
+        expected.setDescription("description");
+        expected.setNotBefore("2017-01-01 00:00:00");
+        expected.setNotBefore("2018-01-01 00:00:00");
+        expected.setDayMonth("1");
+        expected.setDayMonthEnd("2");
+        expected.setMonth("3");
+        expected.setMonthEnd("4");
+        expected.setYear("5");
+        expected.setYearEnd("6");
+        expected.setHour("7");
+        expected.setHourEnd("8");
+        expected.setMinute("9");
+        expected.setMinuteEnd("10");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test Time Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+        expected.setNotBefore("2018-01-01 00:00:00");
+        expected.setNotBefore("2019-01-01 00:00:00");
+        expected.setDayMonth("23");
+        expected.setDayMonthEnd("25");
+        expected.setMonth("11");
+        expected.setMonthEnd("12");
+        expected.setYear("2020");
+        expected.setYearEnd("2021");
+        expected.setHour("17");
+        expected.setHourEnd("18");
+        expected.setMinute("19");
+        expected.setMinuteEnd("20");
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        TimePolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws InterruptedException {
+        authorizationPage.navigateTo();
+        TimePolicyRepresentation expected = new TimePolicyRepresentation();
+
+        expected.setName("Test Time Policy");
+        expected.setDescription("description");
+        expected.setNotBefore("2017-01-01 00:00:00");
+        expected.setNotBefore("2018-01-01 00:00:00");
+        expected.setDayMonth("1");
+        expected.setDayMonthEnd("2");
+        expected.setMonth("3");
+        expected.setMonthEnd("4");
+        expected.setYear("5");
+        expected.setYearEnd("6");
+        expected.setHour("7");
+        expected.setHourEnd("8");
+        expected.setMinute("9");
+        expected.setMinuteEnd("10");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private TimePolicyRepresentation createPolicy(TimePolicyRepresentation expected) {
+        TimePolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private TimePolicyRepresentation assertPolicy(TimePolicyRepresentation expected, TimePolicy policy) {
+        TimePolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+
+        return actual;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/UserPolicyManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/UserPolicyManagementTest.java
new file mode 100644
index 0000000..ed19bc5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/UserPolicyManagementTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.UserPolicy;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyManagementTest extends AbstractAuthorizationSettingsTest {
+
+    @Before
+    public void configureTest() {
+        super.configureTest();
+        UsersResource users = testRealmResource().users();
+        users.create(UserBuilder.create().username("user a").build());
+        users.create(UserBuilder.create().username("user b").build());
+        users.create(UserBuilder.create().username("user c").build());
+    }
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        UserPolicyRepresentation expected = new UserPolicyRepresentation();
+
+        expected.setName("Test User Policy");
+        expected.setDescription("description");
+        expected.addUser("user a");
+        expected.addUser("user b");
+        expected.addUser("user c");
+
+        expected = createPolicy(expected);
+
+        String previousName = expected.getName();
+
+        expected.setName("Changed Test User Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+
+        expected.setUsers(expected.getUsers().stream().filter(user -> !user.equals("user b")).collect(Collectors.toSet()));
+
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().update(previousName, expected);
+        assertAlertSuccess();
+
+        authorizationPage.navigateTo();
+        UserPolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
+
+        assertPolicy(expected, actual);
+    }
+
+    @Test
+    public void testDeletePolicy() throws InterruptedException {
+        authorizationPage.navigateTo();
+        UserPolicyRepresentation expected = new UserPolicyRepresentation();
+
+        expected.setName("Test User Policy");
+        expected.setDescription("description");
+        expected.addUser("user c");
+
+        expected = createPolicy(expected);
+        authorizationPage.navigateTo();
+        authorizationPage.authorizationTabs().policies().delete(expected.getName());
+        assertAlertSuccess();
+        authorizationPage.navigateTo();
+        assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
+    }
+
+    private UserPolicyRepresentation createPolicy(UserPolicyRepresentation expected) {
+        UserPolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
+        assertAlertSuccess();
+        return assertPolicy(expected, policy);
+    }
+
+    private UserPolicyRepresentation assertPolicy(UserPolicyRepresentation expected, UserPolicy policy) {
+        UserPolicyRepresentation actual = policy.toRepresentation();
+
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.getLogic(), actual.getLogic());
+
+        assertNotNull(actual.getUsers());
+        assertEquals(expected.getUsers().size(), actual.getUsers().size());
+        assertEquals(0, actual.getUsers().stream().filter(actualUser -> !expected.getUsers().stream()
+                .filter(expectedUser -> actualUser.equals(expectedUser))
+                .findFirst().isPresent())
+                .count());
+        return actual;
+    }
+}
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 14708d8..fff8bf2 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -124,6 +124,8 @@ content-type-options=X-Content-Type-Options
 content-type-options-tooltip=Default value prevents Internet Explorer and Google Chrome from MIME-sniffing a response away from the declared content-type (click label for more information)
 robots-tag=X-Robots-Tag
 robots-tag-tooltip=Prevent pages from appearing in search engines (click label for more information)
+x-xss-protection=X-XSS-Protection
+x-xss-protection-tooltip=This header configures the Cross-site scripting (XSS) filter in your browser. Using the default behavior, the browser will prevent rendering of the page when a XSS attack is detected (click label for more information)
 max-login-failures=Max Login Failures
 max-login-failures.tooltip=How many failures before wait is triggered.
 wait-increment=Wait Increment
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index c4a134b..5f58e88 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -2168,15 +2168,29 @@ module.directive('kcEnter', function() {
     };
 });
 
-module.directive('kcSave', function ($compile, Notifications) {
+module.directive('kcSave', function ($compile, $timeout, Notifications) {
+    var clickDelay = 500; // 500 ms
+    
     return {
         restrict: 'A',
         link: function ($scope, elem, attr, ctrl) {
             elem.addClass("btn btn-primary");
             elem.attr("type","submit");
-            elem.bind('click', function() {
+            
+            var disabled = false;
+            elem.on('click', function(evt) {
                 if ($scope.hasOwnProperty("changed") && !$scope.changed) return;
                 
+                // KEYCLOAK-4121: Prevent double form submission
+                if (disabled) {
+                    evt.preventDefault();
+                    evt.stopImmediatePropagation();
+                    return;
+                } else {
+                    disabled = true;
+                    $timeout(function () { disabled = false; }, clickDelay, false);
+                }
+                
                 $scope.$apply(function() {
                     var form = elem.closest('form');
                     if (form && form.attr('name')) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index ed5620c..5d8d462 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -757,6 +757,8 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
                     policy = $scope.policy;
                 }
 
+                delete policy.config;
+
                 $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveModules'
                         , policy).success(function(data) {
                             $scope.drools.moduleNames = data;
@@ -765,6 +767,8 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
             }
 
             $scope.resolveSessions = function() {
+                delete $scope.policy.config;
+
                 $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveSessions'
                         , $scope.policy).success(function(data) {
                             $scope.drools.moduleSessions = data;
@@ -773,17 +777,21 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
         },
 
         onInitUpdate : function(policy) {
-            policy.config.scannerPeriod = parseInt(policy.config.scannerPeriod);
+            policy.scannerPeriod = parseInt(policy.scannerPeriod);
             $scope.resolveModules(policy);
         },
 
         onUpdate : function() {
-            $scope.policy.config.resources = JSON.stringify($scope.policy.config.resources);
+            delete $scope.policy.config;
         },
 
         onInitCreate : function(newPolicy) {
-            newPolicy.config.scannerPeriod = 1;
-            newPolicy.config.scannerPeriodUnit = 'Hours';
+            newPolicy.scannerPeriod = 1;
+            newPolicy.scannerPeriodUnit = 'Hours';
+        },
+
+        onCreate : function() {
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
 });
@@ -1340,6 +1348,8 @@ module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route,
             $scope.$watch('selectedUsers', function() {
                 if (!angular.equals($scope.selectedUsers, selectedUsers)) {
                     $scope.changed = true;
+                } else {
+                    $scope.changed = false;
                 }
             }, true);
         },
@@ -1413,16 +1423,19 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
                 $scope.selectedClients.push(client);
             }
 
-            $scope.removeFromList = function(list, index) {
-                list.splice(index, 1);
+            $scope.removeFromList = function(client) {
+                var index = $scope.selectedClients.indexOf(client);
+                if (index != -1) {
+                    $scope.selectedClients.splice(index, 1);
+                }
             }
         },
 
         onInitUpdate : function(policy) {
             var selectedClients = [];
 
-            if (policy.config.clients) {
-                var clients = eval(policy.config.clients);
+            if (policy.clients) {
+                var clients = policy.clients;
 
                 for (var i = 0; i < clients.length; i++) {
                     Client.get({realm: $route.current.params.realm, client: clients[i]}, function(data) {
@@ -1435,6 +1448,8 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
             $scope.$watch('selectedClients', function() {
                 if (!angular.equals($scope.selectedClients, selectedClients)) {
                     $scope.changed = true;
+                } else {
+                    $scope.changed = false;
                 }
             }, true);
         },
@@ -1446,7 +1461,18 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
                 clients.push($scope.selectedClients[i].id);
             }
 
-            $scope.policy.config.clients = JSON.stringify(clients);
+            $scope.policy.clients = clients;
+            delete $scope.policy.config;
+        },
+
+        onInitCreate : function() {
+            var selectedClients = [];
+
+            $scope.$watch('selectedClients', function() {
+                if (!angular.equals($scope.selectedClients, selectedClients)) {
+                    $scope.changed = true;
+                }
+            }, true);
         },
 
         onCreate : function() {
@@ -1456,7 +1482,8 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
                 clients.push($scope.selectedClients[i].id);
             }
 
-            $scope.policy.config.clients = JSON.stringify(clients);
+            $scope.policy.clients = clients;
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
 });
@@ -1572,6 +1599,8 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
             $scope.$watch('selectedRoles', function() {
                 if (!angular.equals($scope.selectedRoles, selectedRoles)) {
                     $scope.changed = true;
+                } else {
+                    $scope.changed = false;
                 }
             }, true);
         },
@@ -1589,6 +1618,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
             }
 
             $scope.policy.roles = roles;
+            delete $scope.policy.config;
         },
 
         onCreate : function() {
@@ -1604,6 +1634,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
             }
 
             $scope.policy.roles = roles;
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
     
@@ -1636,7 +1667,6 @@ module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $
             $scope.initEditor = function(editor){
                 editor.$blockScrolling = Infinity;
                 var session = editor.getSession();
-                
                 session.setMode('ace/mode/javascript');
             };
         },
@@ -1646,15 +1676,14 @@ module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $
         },
 
         onUpdate : function() {
-
+            delete $scope.policy.config;
         },
 
         onInitCreate : function(newPolicy) {
-            newPolicy.config = {};
         },
 
         onCreate : function() {
-
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
 });
@@ -1669,60 +1698,63 @@ module.controller('ResourceServerPolicyTimeDetailCtrl', function($scope, $route,
         },
 
         onInitUpdate : function(policy) {
-            if (policy.config.dayMonth) {
-                policy.config.dayMonth = parseInt(policy.config.dayMonth);
+            if (policy.dayMonth) {
+                policy.dayMonth = parseInt(policy.dayMonth);
             }
-            if (policy.config.dayMonthEnd) {
-                policy.config.dayMonthEnd = parseInt(policy.config.dayMonthEnd);
+            if (policy.dayMonthEnd) {
+                policy.dayMonthEnd = parseInt(policy.dayMonthEnd);
             }
-            if (policy.config.month) {
-                policy.config.month = parseInt(policy.config.month);
+            if (policy.month) {
+                policy.month = parseInt(policy.month);
             }
-            if (policy.config.monthEnd) {
-                policy.config.monthEnd = parseInt(policy.config.monthEnd);
+            if (policy.monthEnd) {
+                policy.monthEnd = parseInt(policy.monthEnd);
             }
-            if (policy.config.year) {
-                policy.config.year = parseInt(policy.config.year);
+            if (policy.year) {
+                policy.year = parseInt(policy.year);
             }
-            if (policy.config.yearEnd) {
-                policy.config.yearEnd = parseInt(policy.config.yearEnd);
+            if (policy.yearEnd) {
+                policy.yearEnd = parseInt(policy.yearEnd);
             }
-            if (policy.config.hour) {
-                policy.config.hour = parseInt(policy.config.hour);
+            if (policy.hour) {
+                policy.hour = parseInt(policy.hour);
             }
-            if (policy.config.hourEnd) {
-                policy.config.hourEnd = parseInt(policy.config.hourEnd);
+            if (policy.hourEnd) {
+                policy.hourEnd = parseInt(policy.hourEnd);
             }
-            if (policy.config.minute) {
-                policy.config.minute = parseInt(policy.config.minute);
+            if (policy.minute) {
+                policy.minute = parseInt(policy.minute);
             }
-            if (policy.config.minuteEnd) {
-                policy.config.minuteEnd = parseInt(policy.config.minuteEnd);
+            if (policy.minuteEnd) {
+                policy.minuteEnd = parseInt(policy.minuteEnd);
             }
         },
 
         onUpdate : function() {
-
+            delete $scope.policy.config;
         },
 
         onInitCreate : function(newPolicy) {
-            newPolicy.config.expirationTime = 1;
-            newPolicy.config.expirationUnit = 'Minutes';
         },
 
         onCreate : function() {
-
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
 
     $scope.isRequired = function () {
         var policy = $scope.policy;
-        if (policy.config.noa || policy.config.nbf
-            || policy.config.dayMonth
-            || policy.config.month
-            || policy.config.year
-            || policy.config.hour
-            || policy.config.minute) {
+
+        if (!policy) {
+            return true;
+        }
+
+        if (policy.notOnOrAfter || policy.notBefore
+            || policy.dayMonth
+            || policy.month
+            || policy.year
+            || policy.hour
+            || policy.minute) {
             return false;
         }
         return true;
@@ -1767,42 +1799,49 @@ module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $r
         },
 
         onInitUpdate : function(policy) {
-            policy.config.applyPolicies = [];
             ResourceServerPolicy.associatedPolicies({
                 realm : $route.current.params.realm,
                 client : client.id,
                 id : policy.id
             }, function(policies) {
+                $scope.selectedPolicies = [];
                 for (i = 0; i < policies.length; i++) {
                     policies[i].text = policies[i].name;
-                    $scope.policy.config.applyPolicies.push(policies[i]);
+                    $scope.selectedPolicies.push(policies[i]);
                 }
+                var copy = angular.copy($scope.selectedPolicies);
+                $scope.$watch('selectedPolicies', function() {
+                    if (!angular.equals($scope.selectedPolicies, copy)) {
+                        $scope.changed = true;
+                    }
+                }, true);
             });
         },
 
         onUpdate : function() {
             var policies = [];
 
-            for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
-                policies.push($scope.policy.config.applyPolicies[i].id);
+            for (i = 0; i < $scope.selectedPolicies.length; i++) {
+                policies.push($scope.selectedPolicies[i].id);
             }
 
-            $scope.policy.config.applyPolicies = JSON.stringify(policies);
+            $scope.policy.policies = policies;
+            delete $scope.policy.config;
         },
 
         onInitCreate : function(newPolicy) {
-            newPolicy.config = {};
             newPolicy.decisionStrategy = 'UNANIMOUS';
         },
 
         onCreate : function() {
             var policies = [];
 
-            for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
-                policies.push($scope.policy.config.applyPolicies[i].id);
+            for (i = 0; i < $scope.selectedPolicies.length; i++) {
+                policies.push($scope.selectedPolicies[i].id);
             }
 
-            $scope.policy.config.applyPolicies = JSON.stringify(policies);
+            $scope.policy.policies = policies;
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
 });
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 108d609..a2cbea4 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
@@ -1111,6 +1111,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         } else if ($scope.clientEdit.bearerOnly) {
             $scope.clientEdit.serviceAccountsEnabled = false;
         }
+        if ($scope.client.authorizationServicesEnabled && !$scope.clientEdit.authorizationServicesEnabled) {
+            Dialog.confirm("Disable Authorization Settings", "Are you sure you want to disable authorization ? Once you save your changes, all authorization settings associated with this client will be removed. This operation can not be reverted.", function () {
+            }, function () {
+                $scope.clientEdit.authorizationServicesEnabled = true;
+            });
+        }
     }
 
     $scope.$watch('clientEdit', function() {
@@ -1240,15 +1246,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
     $scope.cancel = function() {
         $location.url("/realms/" + realm.realm + "/clients");
     };
-
-    $scope.onAuthorizationSettingsChange = function () {
-        if ($scope.client.authorizationServicesEnabled && !$scope.clientEdit.authorizationServicesEnabled) {
-            Dialog.confirm("Disable Authorization Settings", "Are you sure you want to disable authorization ? Once you save your changes, all authorization settings associated with this client will be removed. This operation can not be reverted.", function () {
-            }, function () {
-                $scope.clientEdit.authorizationServicesEnabled = true;
-            });
-        }
-    }
 });
 
 module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
index 7a5ba6d..221a902 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
@@ -36,36 +36,36 @@
                 <kc-tooltip>{{:: 'authz-permission-resource-apply-to-resource-type.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix" data-ng-hide="applyToResourceTypeFlag">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="resources">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="resourcesUiSelect" id="reqActions" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!applyToResourceTypeFlag"/>
+                    <input type="hidden" ui-select2="resourcesUiSelect" id="resources" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!applyToResourceTypeFlag"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix" data-ng-show="applyToResourceTypeFlag">
-                <label class="col-md-2 control-label" for="policy.resourceType">{{:: 'authz-resource-type' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="resourceType">{{:: 'authz-resource-type' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input class="form-control" type="text" id="policy.resourceType" name="policy.resourceType" data-ng-model="policy.resourceType" data-ng-required="applyToResourceTypeFlag">
+                    <input class="form-control" type="text" id="resourceType" name="policy.resourceType" data-ng-model="policy.resourceType" data-ng-required="applyToResourceTypeFlag">
                 </div>
 
                 <kc-tooltip>{{:: 'authz-permission-resource-type.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
+                    <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
                 </div>
 
                 <kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.decisionStrategy">{{:: 'authz-policy-decision-strategy' | translate}}</label>
+                <label class="col-md-2 control-label" for="decisionStrategy">{{:: 'authz-policy-decision-strategy' | translate}}</label>
 
                 <div class="col-sm-2">
-                    <select class="form-control" id="policy.decisionStrategy"
+                    <select class="form-control" id="decisionStrategy"
                             data-ng-model="policy.decisionStrategy"
                             ng-change="selectDecisionStrategy()">
                         <option value="UNANIMOUS">{{:: 'authz-policy-decision-strategy-unanimous' | translate}}</option>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
index 5da3a12..cce7e24 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
@@ -29,18 +29,18 @@
                 <kc-tooltip>{{:: 'authz-permission-description.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resource' | translate}}</label>
+                <label class="col-md-2 control-label" for="resources">{{:: 'authz-resource' | translate}}</label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="reqActions" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
+                    <input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="resources" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
                 </div>
                 <kc-tooltip>{{:: 'authz-permission-scope-resource.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix" data-ng-show="selectedResource">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="resourceScopes">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <select ui-select2 id="reqActions2"
+                    <select ui-select2 id="resourceScopes"
                             data-ng-model="selectedScopes"
                             data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
                             data-ng-required="selectedResource != null">
@@ -50,27 +50,27 @@
                 <kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix" data-ng-show="!selectedResource">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="scopes">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="selectedScopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="selectedResource == null" />
+                    <input type="hidden" ui-select2="scopesUiSelect" id="scopes" data-ng-model="selectedScopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="selectedResource == null" />
                 </div>
                 <kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
+                    <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
                 </div>
 
                 <kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.decisionStrategy">{{:: 'authz-policy-decision-strategy' | translate}}</label>
+                <label class="col-md-2 control-label" for="decisionStrategy">{{:: 'authz-policy-decision-strategy' | translate}}</label>
 
                 <div class="col-sm-2">
-                    <select class="form-control" id="policy.decisionStrategy"
+                    <select class="form-control" id="decisionStrategy"
                             data-ng-model="policy.decisionStrategy"
                             ng-change="selectDecisionStrategy()">
                         <option value="UNANIMOUS">{{:: 'authz-policy-decision-strategy-unanimous' | translate}}</option>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
index 3c761a5..2101643 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/resource-server-permission-list.html
@@ -50,6 +50,7 @@
                         <div class="pull-right">
                             <select class="form-control" ng-model="policyType"
                                     ng-options="p.name for p in policyProviders track by p.type"
+                                    id="create-permission"
                                     data-ng-change="addPolicy(policyType);">
                                 <option value="" disabled selected>{{:: 'authz-create-permission' | translate}}...</option>
                             </select>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
index 2544d3c..4af440b 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-aggregate-detail.html
@@ -31,10 +31,10 @@
                 <kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
+                <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
 
                 <div class="col-md-6">
-                    <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
+                    <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
             </div>
@@ -54,10 +54,10 @@
                 <kc-tooltip>{{:: 'authz-policy-decision-strategy.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic" name="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-client-detail.html
index 634b836..de2da05 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-client-detail.html
@@ -42,7 +42,7 @@
             <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">
+                    <table class="table table-striped table-bordered" id="selected-clients">
                         <thead>
                         <tr data-ng-hide="!selectedClients.length">
                             <th>{{:: 'clientId' | translate}}</th>
@@ -53,7 +53,7 @@
                         <tr ng-repeat="client in selectedClients | orderBy:'clientId'">
                             <td>{{client.clientId}}</td>
                             <td class="kc-action-cell">
-                                <button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(selectedClients, $index);">{{:: 'remove' | translate}}</button>
+                                <button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(client);">{{:: 'remove' | translate}}</button>
                             </td>
                         </tr>
                         <tr data-ng-show="!selectedClients.length">
@@ -64,10 +64,10 @@
                 </div>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
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
index 2a75327..612a8a4 100644
--- 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
@@ -31,31 +31,31 @@
                 <kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.mavenArtifactGroupId">{{:: 'authz-policy-drools-maven-artifact' | translate}} <span class="required" data-ng-show="create">*</span></label>
-                <button data-ng-click="resolveModules()" class="btn btn-primary">{{:: 'authz-policy-drools-maven-artifact-resolve' | translate}}</button>
+                <label class="col-md-2 control-label" for="artifactGroupId">{{:: 'authz-policy-drools-maven-artifact' | translate}} <span class="required" data-ng-show="create">*</span></label>
+                <button data-ng-click="resolveModules()" id="resolveModule" class="btn btn-primary">{{:: 'authz-policy-drools-maven-artifact-resolve' | translate}}</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>
+                    <input class="form-control" type="text" id="artifactGroupId" name="artifactGroupId" data-ng-model="policy.artifactGroupId" placeholder="Group Identifier" required>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-drools-maven-artifact.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.mavenArtifactId"></label>
+                <label class="col-md-2 control-label" for="artifactId"></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>
+                    <input class="form-control" type="text" id="artifactId" name="artifactId" data-ng-model="policy.artifactId" autofocus placeholder="Artifact Identifier" required>
                 </div>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.mavenArtifactVersion"></label>
+                <label class="col-md-2 control-label" for="artifactVersion"></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>
+                    <input class="form-control" type="text" id="artifactVersion" name="artifactVersion" data-ng-model="policy.artifactVersion" autofocus placeholder="Version" required>
                 </div>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.moduleName">{{:: 'authz-policy-drools-module' | translate}} <span class="required" data-ng-show="create">*</span></label>
+                <label class="col-md-2 control-label" for="moduleName">{{:: 'authz-policy-drools-module' | translate}} <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"
+                        <select class="form-control" id="moduleName"
+                                ng-model="policy.moduleName"
                                 ng-options="moduleName as moduleName for moduleName in drools.moduleNames"
                                 ng-change="resolveSessions()"
                                 ng-disabled="!drools.moduleNames.length"
@@ -66,11 +66,11 @@
                 <kc-tooltip>{{:: 'authz-policy-drools-module.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.sessionName">{{:: 'authz-policy-drools-session' | translate}} <span class="required" data-ng-show="create">*</span></label>
+                <label class="col-md-2 control-label" for="sessionName">{{:: 'authz-policy-drools-session' | translate}} <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"
+                        <select class="form-control" id="sessionName"
+                                ng-model="policy.sessionName"
                                 ng-options="sessionName as sessionName for sessionName in drools.moduleSessions"
                                 ng-disabled="!drools.moduleSessions.length"
                                 required>
@@ -80,14 +80,14 @@
                 <kc-tooltip>{{:: 'authz-policy-drools-session.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.scannerPeriod">{{:: 'authz-policy-drools-update-period' | translate}}</label>
+                <label class="col-md-2 control-label" for="scannerPeriod">{{:: 'authz-policy-drools-update-period' | translate}}</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">
+                    <input class="form-control" type="number" required min="1" max="31536000" data-ng-model="policy.scannerPeriod" id="scannerPeriod"
+                           name="scannerPeriod"
+                           ng-disabled="!policy.sessionName"/>
+                    <select class="form-control" id="scannerPeriodUnit" name="scannerPeriodUnit"
+                            data-ng-model="policy.scannerPeriodUnit"
+                            ng-disabled="!policy.sessionName">
                         <option value="Seconds">{{:: 'seconds' | translate}}</option>
                         <option value="Minutes">{{:: 'minutes' | translate}}</option>
                         <option value="Hours">{{:: 'hours' | translate}}</option>
@@ -97,10 +97,10 @@
                 <kc-tooltip>{{:: 'authz-policy-drools-update-period.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
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
index fb2fbc4..679c15a 100644
--- 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
@@ -33,17 +33,17 @@
                 <kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="description">{{:: 'authz-policy-js-code' | translate}} </label>
+                <label class="col-md-2 control-label" for="code">{{:: 'authz-policy-js-code' | translate}} </label>
                 <div class="col-sm-6">
-                    <div ui-ace="{ onLoad : initEditor }" data-ng-model="policy.config.code"></div>
+                    <div ui-ace="{ onLoad : initEditor }" id="code" data-ng-model="policy.code"></div>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-js-code.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
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
index f9c2aca..47e563a 100644
--- 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
@@ -63,7 +63,7 @@
             <div class="form-group clearfix" style="margin-top: -15px;">
                 <label class="col-md-2 control-label"></label>
                 <div class="col-sm-4" data-ng-show="hasRealmRole()">
-                    <table class="table table-striped table-bordered">
+                    <table class="table table-striped table-bordered" id="selected-realm-roles">
                         <thead>
                         <tr>
                             <th class="col-sm-5">{{:: 'name' | translate}}</th>
@@ -114,7 +114,7 @@
             <div class="form-group clearfix" style="margin-top: -15px;">
                 <label class="col-md-2 control-label"></label>
                 <div class="col-sm-4" data-ng-show="hasClientRole()">
-                    <table class="table table-striped table-bordered">
+                    <table class="table table-striped table-bordered" id="selected-client-roles">
                         <thead>
                             <tr>
                                 <th class="col-sm-5">{{:: 'name' | translate}}</th>
@@ -140,10 +140,10 @@
                 </div>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
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
index 5177734..81793d5 100644
--- 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
@@ -34,66 +34,66 @@
                 <kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.nbf">{{:: 'not-before' | translate}}</label>
+                <label class="col-md-2 control-label" for="notBefore">{{:: 'not-before' | translate}}</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" data-ng-required="isRequired()">
+                    <input class="form-control" style="width: 150px" type="text" id="notBefore" name="notBefore" data-ng-model="policy.notBefore" placeholder="yyyy-MM-dd hh:mm:ss" data-ng-required="isRequired()">
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-not-before.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-not-on-after' | translate}}</label>
+                <label class="col-md-2 control-label" for="notOnOrAfter">{{:: 'authz-policy-time-not-on-after' | translate}}</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" data-ng-required="isRequired()">
+                    <input class="form-control" style="width: 150px" type="text" id="notOnOrAfter" name="notOnOrAfter" data-ng-model="policy.notOnOrAfter" placeholder="yyyy-MM-dd hh:mm:ss" data-ng-required="isRequired()">
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-not-on-after.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-day-month' | translate}}</label>
+                <label class="col-md-2 control-label" for="dayMonth">{{:: 'authz-policy-time-day-month' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" type="number" min="1" max="31" data-ng-model="policy.config.dayMonth" id="dayMonth" name="dayMonth" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.dayMonth}}" max="31" data-ng-model="policy.config.dayMonthEnd" id="dayMonthEnd" name="dayMonthEnd"/>
+                    <input class="form-control" type="number" min="1" max="31" data-ng-model="policy.dayMonth" id="dayMonth" name="dayMonth" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.dayMonth}}" max="31" data-ng-model="policy.dayMonthEnd" id="dayMonthEnd" name="dayMonthEnd"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-day-month.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-month' | translate}}</label>
+                <label class="col-md-2 control-label" for="month">{{:: 'authz-policy-time-month' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" type="number" min="1" max="12" data-ng-model="policy.config.month" id="month" name="month" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.month}}" max="12" data-ng-model="policy.config.monthEnd" id="monthEnd" name="monthEnd"/>
+                    <input class="form-control" type="number" min="1" max="12" data-ng-model="policy.month" id="month" name="month" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.month}}" max="12" data-ng-model="policy.monthEnd" id="monthEnd" name="monthEnd"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-month.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-year' | translate}}</label>
+                <label class="col-md-2 control-label" for="year">{{:: 'authz-policy-time-year' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" type="number" min="2016" max="2050" data-ng-model="policy.config.year" id="year" name="year" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.year}}" max="2050" data-ng-model="policy.config.yearEnd" id="yearEnd" name="yearEnd"/>
+                    <input class="form-control" type="number" data-ng-model="policy.year" id="year" name="year" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.year}}" max="2050" data-ng-model="policy.yearEnd" id="yearEnd" name="yearEnd"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-year.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-hour' | translate}}</label>
+                <label class="col-md-2 control-label" for="hour">{{:: 'authz-policy-time-hour' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" type="number" min="0" max="23" data-ng-model="policy.config.hour" id="hour" name="hour" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.hour}}" max="23" data-ng-model="policy.config.hourEnd" id="hourEnd" name="hourEnd"/>
+                    <input class="form-control" type="number" min="0" max="23" data-ng-model="policy.hour" id="hour" name="hour" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.hour}}" max="23" data-ng-model="policy.hourEnd" id="hourEnd" name="hourEnd"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-hour.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-minute' | translate}}</label>
+                <label class="col-md-2 control-label" for="minute">{{:: 'authz-policy-time-minute' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" type="number" min="0" max="59" data-ng-model="policy.config.minute" id="minute" name="minute" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.minute}}" max="59" data-ng-model="policy.config.minuteEnd" id="minuteEnd" name="minuteEnd"/>
+                    <input class="form-control" type="number" min="0" max="59" data-ng-model="policy.minute" id="minute" name="minute" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.minute}}" max="59" data-ng-model="policy.minuteEnd" id="minuteEnd" name="minuteEnd"/>
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-minute.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
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
index 2a3b019..c619910 100644
--- 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
@@ -42,7 +42,7 @@
             <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">
+                    <table class="table table-striped table-bordered" id="selected-users">
                         <thead>
                             <tr data-ng-hide="!selectedUsers.length">
                                 <th>{{:: 'username' | translate}}</th>
@@ -64,10 +64,10 @@
                 </div>
             </div>
             <div class="form-group clearfix">
-                <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
+                <label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
 
                 <div class="col-sm-1">
-                    <select class="form-control" id="policy.logic"
+                    <select class="form-control" id="logic"
                             data-ng-model="policy.logic">
                         <option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
                         <option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
index 999eb9f..f4e0e89 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-list.html
@@ -50,7 +50,7 @@
                         <div class="pull-right">
                             <a id="hideDetails" data-ng-show="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-hide-details' | translate}}</a>
                             <a id="showDetails" data-ng-hide="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-show-details' | translate}}</a>
-                            <select class="form-control" ng-model="policyType"
+                            <select id="create-policy" 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>{{:: 'authz-create-policy' | translate}}...</option>
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 6b890e5..3eb084f 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
@@ -114,7 +114,7 @@
                 <label class="col-md-2 control-label" for="authorizationServicesEnabled">{{:: 'authz-authorization-services-enabled' | translate}}</label>
                 <kc-tooltip>{{:: 'authz-authorization-services-enabled.tooltip' | translate}}</kc-tooltip>
                 <div class="col-md-6">
-                    <input ng-model="clientEdit.authorizationServicesEnabled" ng-click="onAuthorizationSettingsChange()" name="authorizationServicesEnabled" id="authorizationServicesEnabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+                    <input ng-model="clientEdit.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'">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html b/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
index 75e5ba0..f2787cd 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
@@ -36,6 +36,13 @@
                 </div>
                 <kc-tooltip>{{:: 'robots-tag-tooltip' | translate}}</kc-tooltip>
             </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="xXSSProtection"><a href="https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xxxsp" target="_blank">{{:: 'x-xss-protection' | translate}}</a></label>
+                <div class="col-sm-6">
+                    <input class="form-control" id="xXSSProtection" type="text" ng-model="realm.browserSecurityHeaders.xXSSProtection">
+                </div>
+                <kc-tooltip>{{:: 'x-xss-protection-tooltip' | translate}}</kc-tooltip>
+            </div>
         </fieldset>
         <div class="form-group" data-ng-show="access.manageRealm">
             <div class="col-md-10 col-md-offset-2">
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
index 5a5936f..7def4d1 100644
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
@@ -42,6 +42,7 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces
     private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
     private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
     private static final ModuleIdentifier KEYCLOAK_SERVER_SPI = ModuleIdentifier.create("org.keycloak.keycloak-server-spi");
+    private static final ModuleIdentifier KEYCLOAK_SERVER_SPI_PRIVATE = ModuleIdentifier.create("org.keycloak.keycloak-server-spi-private");
     private static final ModuleIdentifier KEYCLOAK_JPA = ModuleIdentifier.create("org.keycloak.keycloak-model-jpa");
     private static final ModuleIdentifier JAXRS = ModuleIdentifier.create("javax.ws.rs.api");
     private static final ModuleIdentifier RESTEASY = ModuleIdentifier.create("org.jboss.resteasy.resteasy-jaxrs");
@@ -66,6 +67,7 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI_PRIVATE, false, false, false, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JAXRS, false, false, false, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, RESTEASY, false, false, false, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE, false, false, false, false));
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java
index 97dd52a..fcd1b46 100644
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java
@@ -48,7 +48,7 @@ public class KeycloakProviderDeploymentProcessor implements DeploymentUnitProces
 
         if (!KeycloakProviderDependencyProcessor.isKeycloakProviderDeployment(deploymentUnit)) return;
 
-        logger.infof("Deploying Keycloak provider: {0}", deploymentUnit.getName());
+        logger.infov("Deploying Keycloak provider: {0}", deploymentUnit.getName());
         final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
         ProviderManager pm = new ProviderManager(module.getClassLoader());
         ProviderManagerRegistry.SINGLETON.deploy(pm);
@@ -66,7 +66,7 @@ public class KeycloakProviderDeploymentProcessor implements DeploymentUnitProces
     public void undeploy(DeploymentUnit context) {
         ProviderManager pm = context.getAttachment(ATTACHMENT_KEY);
         if (pm != null) {
-            logger.infof("Undeploying Keycloak provider: {0}", context.getName());
+            logger.infov("Undeploying Keycloak provider: {0}", context.getName());
             ProviderManagerRegistry.SINGLETON.undeploy(pm);
             context.removeAttachment(ATTACHMENT_KEY);
         }