keycloak-memoizeit

Merge pull request #4054 from pedroigor/KEYCLOAK-3135 [KEYCLOAK-3135]

4/24/2017 5:44:07 PM

Changes

Details

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..655d8d1 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
@@ -29,12 +29,13 @@ 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,22 +55,22 @@ 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<>());
     }
 
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/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/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..6d19a84 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<>();
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/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/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/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..e981652 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
@@ -74,4 +74,10 @@ public interface PoliciesResource {
 
     @Path("user")
     UserPoliciesResource users();
+
+    @Path("js")
+    JSPoliciesResource js();
+
+    @Path("time")
+    TimePoliciesResource time();
 }
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/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..f69ed2d
--- /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 AbstractPermissionManagementTest {
+
+    @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/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..0c34705
--- /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 AbstractPermissionManagementTest {
+
+    @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/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..9900247 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,18 @@
  */
 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.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 +37,79 @@ public class Permissions extends Form {
     @FindBy(css = "table[class*='table']")
     private PermissionsTable table;
 
+    @FindBy(id = "create-permission")
+    private Select createSelect;
+
+    @Page
+    private ResourcePermission resourcePermission;
+
     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)) {
+            return null;
+        }
+
+        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);
+                }
+
+                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;
+                }
+            }
+        }
+        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();
+                }
+
+                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..87facb3
--- /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,264 @@
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+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;
+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 PolicyInput policyInput;
+
+    @FindBy(id = "s2id_resources")
+    private ResourceInput resourceInput;
+
+    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);
+            Set<String> selectedResources = resourceInput.getSelected();
+            Set<String> resources = expected.getResources();
+
+            for (String resource : resources) {
+                if (!selectedResources.contains(resource)) {
+                    resourceInput.select(resource);
+                }
+            }
+
+            for (String selected : selectedResources) {
+                boolean isSelected = false;
+
+                for (String resource : resources) {
+                    if (selected.equals(resource)) {
+                        isSelected = true;
+                        break;
+                    }
+                }
+
+                if (!isSelected) {
+                    resourceInput.unSelect(selected, driver);
+                }
+            }
+        }
+
+        Set<String> selectedPolicies = policyInput.getSelected();
+        Set<String> policies = expected.getPolicies();
+
+        for (String policy : policies) {
+            if (!selectedPolicies.contains(policy)) {
+                policyInput.select(policy, driver);
+            }
+        }
+
+        for (String selected : selectedPolicies) {
+            boolean isSelected = false;
+
+            for (String policy : policies) {
+                if (selected.equals(policy)) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                policyInput.unSelect(selected, driver);
+            }
+        }
+
+        WaitUtils.pause(1000);
+
+        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(policyInput.getSelected());
+        representation.setResources(resourceInput.getSelected());
+
+        return representation;
+    }
+
+    public class PolicyInput {
+
+        @Root
+        private WebElement root;
+
+        @FindBy(xpath = "//input[contains(@class,'select2-input')]")
+        private WebElement search;
+
+        @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+        private List<WebElement> result;
+
+        @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+        private List<WebElement> selection;
+
+        public void select(String name, WebDriver driver) {
+            root.click();
+            WaitUtils.pause(1000);
+
+            Actions actions = new Actions(driver);
+
+            actions.sendKeys(name).perform();
+            WaitUtils.pause(1000);
+
+            if (result.isEmpty()) {
+                actions.sendKeys(Keys.ESCAPE).perform();
+                return;
+            }
+            for (WebElement result : result) {
+                if (result.getText().equalsIgnoreCase(name)) {
+                    result.click();
+                    return;
+                }
+            }
+        }
+
+        public Set<String> getSelected() {
+            HashSet<String> values = new HashSet<>();
+
+            for (WebElement selected : selection) {
+                values.add(selected.findElements(By.tagName("div")).get(0).getText());
+            }
+
+            return values;
+        }
+
+        public void unSelect(String name, WebDriver driver) {
+            for (WebElement selected : selection) {
+                WebElement selection = selected.findElements(By.tagName("div")).get(0);
+                if (name.equals(selection.getText())) {
+                    WebElement element = selection.findElement(By.xpath("//a[contains(@class,'select2-search-choice-close')]"));
+                    JavascriptExecutor executor = (JavascriptExecutor) driver;
+                    executor.executeScript("arguments[0].click();", element);
+                    WaitUtils.pause(1000);
+                    return;
+                }
+            }
+        }
+    }
+
+    public class ResourceInput {
+
+        @Root
+        private WebElement root;
+
+        @FindBy(xpath = "//input[contains(@class,'select2-input')]")
+        private WebElement search;
+
+        @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+        private List<WebElement> result;
+
+        @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+        private List<WebElement> selection;
+
+        public void select(String name) {
+            root.click();
+            WaitUtils.pause(1000);
+            setInputValue(search, name);
+            WaitUtils.pause(1000);
+            if (result.isEmpty()) {
+                search.sendKeys(Keys.ESCAPE);
+                return;
+            }
+            for (WebElement result : result) {
+                if (result.getText().equalsIgnoreCase(name)) {
+                    result.click();
+                    return;
+                }
+            }
+        }
+
+        public Set<String> getSelected() {
+            HashSet<String> values = new HashSet<>();
+
+            for (WebElement selected : selection) {
+                values.add(selected.findElements(By.tagName("div")).get(0).getText());
+            }
+
+            return values;
+        }
+
+        public void unSelect(String name, WebDriver driver) {
+            for (WebElement selected : selection) {
+                if (name.equals(selected.findElements(By.tagName("div")).get(0).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(1000);
+                    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/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..c1d5f21
--- /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,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.console.page.clients.authorization.policy;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.testsuite.page.Form;
+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;
+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 PolicyInput policyInput;
+
+    @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 = policyInput.getSelected();
+        Set<String> policies = expected.getPolicies();
+
+        for (String policy : policies) {
+            if (!selectedPolicies.contains(policy)) {
+                policyInput.select(policy, driver);
+            }
+        }
+
+        for (String selected : selectedPolicies) {
+            boolean isSelected = false;
+
+            for (String policy : policies) {
+                if (selected.equals(policy)) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                policyInput.unSelect(selected, driver);
+            }
+        }
+
+        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(policyInput.getSelected());
+
+        return representation;
+    }
+
+    public class PolicyInput {
+
+        @Root
+        private WebElement root;
+
+        @FindBy(xpath = "//input[contains(@class,'select2-input')]")
+        private WebElement search;
+
+        @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+        private List<WebElement> result;
+
+        @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+        private List<WebElement> selection;
+
+        public void select(String name, WebDriver driver) {
+            root.click();
+            WaitUtils.pause(1000);
+
+            Actions actions = new Actions(driver);
+
+            actions.sendKeys(name).perform();
+            WaitUtils.pause(1000);
+
+            if (result.isEmpty()) {
+                actions.sendKeys(Keys.ESCAPE).perform();
+                return;
+            }
+            for (WebElement result : result) {
+                if (result.getText().equalsIgnoreCase(name)) {
+                    result.click();
+                    return;
+                }
+            }
+        }
+
+        public Set<String> getSelected() {
+            HashSet<String> values = new HashSet<>();
+
+            for (WebElement selected : selection) {
+                values.add(selected.findElements(By.tagName("div")).get(0).getText());
+            }
+
+            return values;
+        }
+
+        public void unSelect(String name, WebDriver driver) {
+            for (WebElement selected : selection) {
+                if (name.equals(selected.findElements(By.tagName("div")).get(0).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(1000);
+                    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/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..2a41efe 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,21 @@
  */
 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.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+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 +38,131 @@ 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;
 
-    public PermissionsTable policies() {
+    @Page
+    private TimePolicy timePolicy;
+
+    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;
+        }
+
+        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);
+                }
+
+                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;
+                }
+            }
+        }
+        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();
+                }
+
+                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..29ae535
--- /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,284 @@
+/*
+ * 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.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+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;
+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 RolesInput realmRolesInput;
+
+    @FindBy(id = "clients")
+    private Select clientsSelect;
+
+    @FindBy(id = "s2id_clientRoles")
+    private ClientRolesInput clientRolesInput;
+
+    @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]);
+                clientRolesInput.select(parts[1], driver);
+                clientRolesInput.setRequired(parts[1], role);
+            } else {
+                realmRolesInput.select(role.getId(), driver);
+                realmRolesInput.setRequired(role.getId(), role);
+            }
+        }
+
+        unSelect(roles, realmRolesInput.getSelected());
+        unSelect(roles, clientRolesInput.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) {
+                    clientRolesInput.unSelect(selected.getId().split("/")[1], driver);
+                } else {
+                    realmRolesInput.unSelect(selected.getId(), driver);
+                }
+            }
+        }
+    }
+
+    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 = realmRolesInput.getSelected();
+
+        roles.addAll(clientRolesInput.getSelected());
+
+        representation.setRoles(roles);
+
+        return representation;
+    }
+
+    public class RolesInput extends AbstractRolesInput {
+        @Override
+        protected RolePolicyRepresentation.RoleDefinition getSelectedRoles(List<WebElement> tds) {
+            RolePolicyRepresentation.RoleDefinition selectedRole = new RolePolicyRepresentation.RoleDefinition();
+            selectedRole.setId(tds.get(0).getText());
+            selectedRole.setRequired(tds.get(1).findElement(By.tagName("input")).isSelected());
+            return selectedRole;
+        }
+
+        @Override
+        protected WebElement getRemoveButton(List<WebElement> tds) {
+            return tds.get(2);
+        }
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return root.findElements(By.xpath("(//table[@id='selected-realm-roles'])/tbody/tr"));
+        }
+
+        @Override
+        protected WebElement getRequiredColumn(List<WebElement> tds) {
+            return tds.get(1);
+        }
+    }
+
+    public class ClientRolesInput extends AbstractRolesInput {
+        @Override
+        protected WebElement getRemoveButton(List<WebElement> tds) {
+            return tds.get(3);
+        }
+
+        @Override
+        protected RolePolicyRepresentation.RoleDefinition getSelectedRoles(List<WebElement> tds) {
+            RolePolicyRepresentation.RoleDefinition selectedRole = new RolePolicyRepresentation.RoleDefinition();
+            selectedRole.setId(tds.get(1).getText() + "/" + tds.get(0).getText());
+            selectedRole.setRequired(tds.get(2).findElement(By.tagName("input")).isSelected());
+            return selectedRole;
+        }
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return root.findElements(By.xpath("(//table[@id='selected-client-roles'])/tbody/tr"));
+        }
+
+        @Override
+        protected WebElement getRequiredColumn(List<WebElement> tds) {
+            return tds.get(2);
+        }
+    }
+
+    public abstract class AbstractRolesInput {
+
+        @Root
+        protected WebElement root;
+
+        @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+        private List<WebElement> result;
+
+        @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+        private List<WebElement> selection;
+
+        public void select(String roleId, WebDriver driver) {
+            root.click();
+            WaitUtils.pause(1000);
+
+            Actions actions = new Actions(driver);
+
+            actions.sendKeys(roleId).perform();
+            WaitUtils.pause(1000);
+
+            if (result.isEmpty()) {
+                actions.sendKeys(Keys.ESCAPE).perform();
+                return;
+            }
+
+            for (WebElement result : result) {
+                if (result.getText().equalsIgnoreCase(roleId)) {
+                    result.click();
+                    return;
+                }
+            }
+        }
+
+        public Set<RolePolicyRepresentation.RoleDefinition> getSelected() {
+            List<WebElement> realmRoles = getSelectedElements();
+            Set<RolePolicyRepresentation.RoleDefinition> values = new HashSet<>();
+
+            for (WebElement realmRole : realmRoles) {
+                List<WebElement> tds = realmRole.findElements(tagName("td"));
+                if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                    values.add(getSelectedRoles(tds));
+                }
+            }
+
+            return values;
+        }
+
+        protected abstract RolePolicyRepresentation.RoleDefinition getSelectedRoles(List<WebElement> tds);
+
+        protected abstract List<WebElement> getSelectedElements();
+
+        public void unSelect(String name, WebDriver driver) {
+            Iterator<WebElement> iterator = getSelectedElements().iterator();
+
+            while (iterator.hasNext()) {
+                WebElement realmRole = iterator.next();
+                List<WebElement> tds = realmRole.findElements(tagName("td"));
+
+                if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                    if (tds.get(0).getText().equals(name)) {
+                        getRemoveButton(tds).findElement(By.tagName("button")).click();
+                        return;
+                    }
+                }
+            }
+        }
+
+        protected abstract WebElement getRemoveButton(List<WebElement> tds);
+
+        public void setRequired(String name, RolePolicyRepresentation.RoleDefinition role) {
+            Iterator<WebElement> iterator = getSelectedElements().iterator();
+
+            while (iterator.hasNext()) {
+                WebElement realmRole = iterator.next();
+                List<WebElement> tds = realmRole.findElements(tagName("td"));
+
+                if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                    if (tds.get(0).getText().equals(name)) {
+                        WebElement required = getRequiredColumn(tds).findElement(By.tagName("input"));
+
+                        if (required.isSelected() && role.isRequired()) {
+                            return;
+                        } else if (!required.isSelected() && !role.isRequired()) {
+                            return;
+                        }
+
+                        required.click();
+                        return;
+                    }
+                }
+            }
+        }
+
+        protected abstract WebElement getRequiredColumn(List<WebElement> tds);
+    }
+}
\ 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..46caad0
--- /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,190 @@
+/*
+ * 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.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+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;
+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 UsersInput 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());
+
+        Set<String> users = expected.getUsers();
+
+        for (String user : users) {
+            usersInput.select(user, driver);
+        }
+
+        unSelect(users, usersInput.getSelected());
+
+        save();
+    }
+
+    private void unSelect(Set<String> users, Set<String> selection) {
+        for (String selected : selection) {
+            boolean isSelected = false;
+
+            for (String user : users) {
+                if (selected.equals(user)) {
+                    isSelected = true;
+                    break;
+                }
+            }
+
+            if (!isSelected) {
+                usersInput.unSelect(selected, driver);
+            }
+        }
+    }
+
+    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 UsersInput extends AbstractUserInput {
+        @Override
+        protected WebElement getRemoveButton(List<WebElement> tds) {
+            return tds.get(1);
+        }
+
+        @Override
+        protected List<WebElement> getSelectedElements() {
+            return root.findElements(By.xpath("(//table[@id='selected-users'])/tbody/tr"));
+        }
+    }
+
+    public abstract class AbstractUserInput {
+
+        @Root
+        protected WebElement root;
+
+        @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+        private List<WebElement> result;
+
+        @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+        private List<WebElement> selection;
+
+        public void select(String roleId, WebDriver driver) {
+            root.click();
+            WaitUtils.pause(1000);
+
+            Actions actions = new Actions(driver);
+
+            actions.sendKeys(roleId).perform();
+            WaitUtils.pause(1000);
+
+            if (result.isEmpty()) {
+                actions.sendKeys(Keys.ESCAPE).perform();
+                return;
+            }
+
+            for (WebElement result : result) {
+                if (result.getText().equalsIgnoreCase(roleId)) {
+                    result.click();
+                    return;
+                }
+            }
+        }
+
+        public Set<String> getSelected() {
+            List<WebElement> users = getSelectedElements();
+            Set<String> values = new HashSet<>();
+
+            for (WebElement user : users) {
+                List<WebElement> tds = user.findElements(tagName("td"));
+                if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                    values.add(tds.get(0).getText());
+                }
+            }
+
+            return values;
+        }
+
+        protected abstract List<WebElement> getSelectedElements();
+
+        public void unSelect(String name, WebDriver driver) {
+            Iterator<WebElement> iterator = getSelectedElements().iterator();
+
+            while (iterator.hasNext()) {
+                WebElement realmRole = iterator.next();
+                List<WebElement> tds = realmRole.findElements(tagName("td"));
+
+                if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                    if (tds.get(0).getText().equals(name)) {
+                        getRemoveButton(tds).findElement(By.tagName("button")).click();
+                        return;
+                    }
+                }
+            }
+        }
+
+        protected abstract WebElement getRemoveButton(List<WebElement> tds);
+    }
+}
\ 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..2051ac3
--- /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.roles();
+
+        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.users().create(policyC);
+    }
+
+    @Test
+    public void testUpdate() throws InterruptedException {
+        authorizationPage.navigateTo();
+        AggregatePolicyRepresentation expected = new AggregatePolicyRepresentation();
+
+        expected.setName("Test 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 Aggregate Policy");
+        expected.setDescription("Changed description");
+        expected.setLogic(Logic.NEGATIVE);
+
+        expected.setPolicies(expected.getPolicies().stream().filter(policy -> !policy.equals("Policy B")).collect(Collectors.toSet()));
+
+        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 testDeletePolicy() throws InterruptedException {
+        authorizationPage.navigateTo();
+        AggregatePolicyRepresentation expected = new AggregatePolicyRepresentation();
+
+        expected.setName("Test 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/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..fffce15
--- /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 testDeletePolicy() 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..a4ab7ef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourcePermissionManagementTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.roles();
+
+        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.users().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.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 testUpdateResourceType() throws InterruptedException {
+        authorizationPage.navigateTo();
+        ResourcePermissionRepresentation expected = new ResourcePermissionRepresentation();
+
+        expected.setName("Test Resource B 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 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 testDeletePolicy() 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.getLogic(), actual.getLogic());
+
+        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..713b93a
--- /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 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 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 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 testDeletePolicy() throws InterruptedException {
+        authorizationPage.navigateTo();
+        RolePolicyRepresentation expected = new RolePolicyRepresentation();
+
+        expected.setName("Test Realm 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()) && 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/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..9e9d2e8
--- /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 testDeletePolicy() 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/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index ed5620c..9918041 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
@@ -1340,6 +1340,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,8 +1415,11 @@ 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);
+                }
             }
         },
 
@@ -1435,6 +1440,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);
         },
@@ -1449,6 +1456,16 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
             $scope.policy.config.clients = JSON.stringify(clients);
         },
 
+        onInitCreate : function() {
+            var selectedClients = [];
+
+            $scope.$watch('selectedClients', function() {
+                if (!angular.equals($scope.selectedClients, selectedClients)) {
+                    $scope.changed = true;
+                }
+            }, true);
+        },
+
         onCreate : function() {
             var clients = [];
 
@@ -1572,6 +1589,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 +1608,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
             }
 
             $scope.policy.roles = roles;
+            delete $scope.policy.config;
         },
 
         onCreate : function() {
@@ -1604,6 +1624,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
             }
 
             $scope.policy.roles = roles;
+            delete $scope.policy.config;
         }
     }, realm, client, $scope);
     
@@ -1636,7 +1657,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 +1666,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 +1688,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 +1789,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/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..dda34a2 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
@@ -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">
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'">