keycloak-aplcache

Merge pull request #4873 from pedroigor/master [KEYCLOAK-5806]

12/20/2017 8:53:50 AM

Changes

Details

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 fcb5b8c..2872658 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
@@ -56,16 +56,16 @@ public class Permissions extends Form {
         return table;
     }
 
-    public <P extends PolicyTypeUI> P create(AbstractPolicyRepresentation expected) {
+    public <P extends PolicyTypeUI> P create(AbstractPolicyRepresentation expected, boolean save) {
         String type = expected.getType();
 
         createSelect.selectByValue(type);
 
         if ("resource".equals(type)) {
-            resourcePermission.form().populate((ResourcePermissionRepresentation) expected);
+            resourcePermission.form().populate((ResourcePermissionRepresentation) expected, save);
             return (P) resourcePermission;
         } else if ("scope".equals(type)) {
-            scopePermission.form().populate((ScopePermissionRepresentation) expected);
+            scopePermission.form().populate((ScopePermissionRepresentation) expected, save);
             return (P) scopePermission;
         }
 
@@ -73,6 +73,10 @@ public class Permissions extends Form {
     }
 
     public void update(String name, AbstractPolicyRepresentation representation) {
+        update(name, representation, true);
+    }
+
+    public void update(String name, AbstractPolicyRepresentation representation, boolean save) {
         for (WebElement row : permissions().rows()) {
             PolicyRepresentation actual = permissions().toRepresentation(row);
             if (actual.getName().equalsIgnoreCase(name)) {
@@ -81,9 +85,9 @@ public class Permissions extends Form {
                 String type = representation.getType();
 
                 if ("resource".equals(type)) {
-                    resourcePermission.form().populate((ResourcePermissionRepresentation) representation);
+                    resourcePermission.form().populate((ResourcePermissionRepresentation) representation, save);
                 } else if ("scope".equals(type)) {
-                    scopePermission.form().populate((ScopePermissionRepresentation) representation);
+                    scopePermission.form().populate((ScopePermissionRepresentation) representation, save);
                 }
 
                 return;
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
index d6c4f3a..ba5302d 100644
--- 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
@@ -17,7 +17,7 @@
 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.AbstractPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicyTypeUI;
 
@@ -38,6 +38,10 @@ public class ResourcePermission implements PolicyTypeUI {
     }
 
     public void update(ResourcePermissionRepresentation expected) {
-        form().populate(expected);
+        form().populate(expected, true);
+    }
+
+    public void createPolicy(AbstractPolicyRepresentation expected) {
+        form().createPolicy(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
index d53f820..b473044 100644
--- 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
@@ -16,8 +16,27 @@
  */
 package org.keycloak.testsuite.console.page.clients.authorization.permission;
 
+import static org.keycloak.testsuite.util.UIUtils.performOperationWithPageReload;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.GroupPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.JSPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicySelect;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RolePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RulePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.TimePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.UserPolicy;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
 import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
 import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
@@ -53,12 +72,36 @@ public class ResourcePermissionForm extends Form {
     protected ModalDialog modalDialog;
 
     @FindBy(id = "s2id_policies")
-    private MultipleStringSelect2 policySelect;
+    private PolicySelect policySelect;
 
     @FindBy(id = "s2id_resources")
     private MultipleStringSelect2 resourceSelect;
 
-    public void populate(ResourcePermissionRepresentation expected) {
+    @FindBy(id = "create-policy")
+    private Select createPolicySelect;
+
+    @Page
+    private RolePolicy rolePolicy;
+
+    @Page
+    private UserPolicy userPolicy;
+
+    @Page
+    private ClientPolicy clientPolicy;
+
+    @Page
+    private JSPolicy jsPolicy;
+
+    @Page
+    private TimePolicy timePolicy;
+
+    @Page
+    private RulePolicy rulePolicy;
+
+    @Page
+    private GroupPolicy groupPolicy;
+
+    public void populate(ResourcePermissionRepresentation expected, boolean save) {
         setInputValue(name, expected.getName());
         setInputValue(description, expected.getDescription());
         decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
@@ -76,7 +119,9 @@ public class ResourcePermissionForm extends Form {
             policySelect.update(expected.getPolicies());
         }
 
-        save();
+        if (save) {
+            save();
+        }
     }
 
     public void delete() {
@@ -101,4 +146,24 @@ public class ResourcePermissionForm extends Form {
 
         return representation;
     }
+
+    public void createPolicy(AbstractPolicyRepresentation expected) {
+        performOperationWithPageReload(() -> createPolicySelect.selectByValue(expected.getType()));
+
+        if ("role".equals(expected.getType())) {
+            rolePolicy.form().populate((RolePolicyRepresentation) expected, true);
+        } else if ("user".equalsIgnoreCase(expected.getType())) {
+            userPolicy.form().populate((UserPolicyRepresentation) expected, true);
+        } else if ("client".equalsIgnoreCase(expected.getType())) {
+            clientPolicy.form().populate((ClientPolicyRepresentation) expected, true);
+        } else if ("js".equalsIgnoreCase(expected.getType())) {
+            jsPolicy.form().populate((JSPolicyRepresentation) expected, true);
+        } else if ("time".equalsIgnoreCase(expected.getType())) {
+            timePolicy.form().populate((TimePolicyRepresentation) expected, true);
+        } else if ("rules".equalsIgnoreCase(expected.getType())) {
+            rulePolicy.form().populate((RulePolicyRepresentation) expected, true);
+        } else if ("group".equalsIgnoreCase(expected.getType())) {
+            groupPolicy.form().populate((GroupPolicyRepresentation) expected, true);
+        }
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java
index 5b392ad..cfeba8e 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermission.java
@@ -17,7 +17,7 @@
 package org.keycloak.testsuite.console.page.clients.authorization.permission;
 
 import org.jboss.arquillian.graphene.page.Page;
-import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
 import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicyTypeUI;
 
@@ -38,6 +38,14 @@ public class ScopePermission implements PolicyTypeUI {
     }
 
     public void update(ScopePermissionRepresentation expected) {
-        form().populate(expected);
+        update(expected, true);
+    }
+
+    public void update(ScopePermissionRepresentation expected, boolean save) {
+        form().populate(expected, save);
+    }
+
+    public void createPolicy(AbstractPolicyRepresentation expected) {
+        form().createPolicy(expected);
     }
 }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
index 696ff22..4172843 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/ScopePermissionForm.java
@@ -16,11 +16,30 @@
  */
 package org.keycloak.testsuite.console.page.clients.authorization.permission;
 
+import static org.keycloak.testsuite.util.UIUtils.performOperationWithPageReload;
+
 import java.util.Set;
 import java.util.function.Function;
 
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.GroupPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.JSPolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.PolicySelect;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RolePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.RulePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.TimePolicy;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.UserPolicy;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
 import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
 import org.keycloak.testsuite.console.page.fragment.SingleStringSelect2;
@@ -50,7 +69,7 @@ public class ScopePermissionForm extends Form {
     protected ModalDialog modalDialog;
 
     @FindBy(id = "s2id_policies")
-    private MultipleStringSelect2 policySelect;
+    private PolicySelect policySelect;
 
     @FindBy(id = "s2id_scopes")
     private MultipleStringSelect2 scopeSelect;
@@ -61,7 +80,31 @@ public class ScopePermissionForm extends Form {
     @FindBy(id = "s2id_resources")
     private ResourceSelect resourceSelect;
 
-    public void populate(ScopePermissionRepresentation expected) {
+    @FindBy(id = "create-policy")
+    private Select createPolicySelect;
+
+    @Page
+    private RolePolicy rolePolicy;
+
+    @Page
+    private UserPolicy userPolicy;
+
+    @Page
+    private ClientPolicy clientPolicy;
+
+    @Page
+    private JSPolicy jsPolicy;
+
+    @Page
+    private TimePolicy timePolicy;
+
+    @Page
+    private RulePolicy rulePolicy;
+
+    @Page
+    private GroupPolicy groupPolicy;
+
+    public void populate(ScopePermissionRepresentation expected, boolean save) {
         setInputValue(name, expected.getName());
         setInputValue(description, expected.getDescription());
         decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
@@ -79,7 +122,9 @@ public class ScopePermissionForm extends Form {
             policySelect.update(expected.getPolicies());
         }
 
-        save();
+        if (save) {
+            save();
+        }
     }
 
     public void delete() {
@@ -107,4 +152,24 @@ public class ScopePermissionForm extends Form {
             return super.representation().andThen(s -> "".equals(s) || s.contains("Any resource...") ? null : s);
         }
     }
+
+    public void createPolicy(AbstractPolicyRepresentation expected) {
+        performOperationWithPageReload(() -> createPolicySelect.selectByValue(expected.getType()));
+
+        if ("role".equals(expected.getType())) {
+            rolePolicy.form().populate((RolePolicyRepresentation) expected, true);
+        } else if ("user".equalsIgnoreCase(expected.getType())) {
+            userPolicy.form().populate((UserPolicyRepresentation) expected, true);
+        } else if ("client".equalsIgnoreCase(expected.getType())) {
+            clientPolicy.form().populate((ClientPolicyRepresentation) expected, true);
+        } else if ("js".equalsIgnoreCase(expected.getType())) {
+            jsPolicy.form().populate((JSPolicyRepresentation) expected, true);
+        } else if ("time".equalsIgnoreCase(expected.getType())) {
+            timePolicy.form().populate((TimePolicyRepresentation) expected, true);
+        } else if ("rules".equalsIgnoreCase(expected.getType())) {
+            rulePolicy.form().populate((RulePolicyRepresentation) expected, true);
+        } else if ("group".equalsIgnoreCase(expected.getType())) {
+            groupPolicy.form().populate((GroupPolicyRepresentation) expected, true);
+        }
+    }
 }
\ 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/AggregatePolicyForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/AggregatePolicyForm.java
index 8ed8c39..732f820 100644
--- 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
@@ -17,13 +17,8 @@
 package org.keycloak.testsuite.console.page.clients.authorization.policy;
 
 import static org.keycloak.testsuite.util.UIUtils.performOperationWithPageReload;
-import static org.openqa.selenium.By.tagName;
 
-import java.util.List;
 import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 import org.jboss.arquillian.graphene.page.Page;
 import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
@@ -37,9 +32,7 @@ import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
-import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
 import org.keycloak.testsuite.page.Form;
-import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 import org.openqa.selenium.support.ui.Select;
@@ -162,35 +155,4 @@ public class AggregatePolicyForm extends Form {
             groupPolicy.form().populate((GroupPolicyRepresentation) expected, true);
         }
     }
-
-    public class PolicySelect extends MultipleStringSelect2 {
-
-        @Override
-        protected List<WebElement> getSelectedElements() {
-            return getRoot().findElements(By.xpath("(//table[@id='selected-policies'])/tbody/tr")).stream()
-                    .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
-                    .collect(Collectors.toList());
-        }
-
-        @Override
-        protected BiFunction<WebElement, String, Boolean> deselect() {
-            return (webElement, name) -> {
-                List<WebElement> tds = webElement.findElements(tagName("td"));
-
-                if (!tds.get(0).getText().isEmpty()) {
-                    if (tds.get(0).getText().equals(name)) {
-                        tds.get(3).click();
-                        return true;
-                    }
-                }
-
-                return false;
-            };
-        }
-
-        @Override
-        protected Function<WebElement, String> representation() {
-            return webElement -> webElement.findElements(tagName("td")).get(0).getText();
-        }
-    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PolicySelect.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PolicySelect.java
new file mode 100644
index 0000000..ffae31d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PolicySelect.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.policy;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+public class PolicySelect extends MultipleStringSelect2 {
+
+    @Override
+    protected List<WebElement> getSelectedElements() {
+        return getRoot().findElements(By.xpath("(//table[@id='selected-policies'])/tbody/tr")).stream()
+                .filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    protected BiFunction<WebElement, String, Boolean> deselect() {
+        return (webElement, name) -> {
+            List<WebElement> tds = webElement.findElements(tagName("td"));
+
+            if (!tds.get(0).getText().isEmpty()) {
+                if (tds.get(0).getText().equals(name)) {
+                    tds.get(3).click();
+                    return true;
+                }
+            }
+
+            return false;
+        };
+    }
+
+    @Override
+    protected Function<WebElement, String> representation() {
+        return webElement -> webElement.findElements(tagName("td")).get(0).getText();
+    }
+}
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
index 8ac8b3b..7c3597c 100644
--- 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
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.UUID;
 import java.util.stream.Collectors;
 
 import org.junit.Before;
@@ -30,12 +31,14 @@ 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.AggregatePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 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;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.AggregatePolicy;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -207,8 +210,36 @@ public class ResourcePermissionManagementTest extends AbstractAuthorizationSetti
         assertNull(authorizationPage.authorizationTabs().permissions().permissions().findByName(expected.getName()));
     }
 
+    @Test
+    public void testCreateWithChild() {
+        ResourcePermissionRepresentation expected = new ResourcePermissionRepresentation();
+
+        expected.setName(UUID.randomUUID().toString());
+        expected.setDescription("description");
+        expected.addResource("Resource B");
+        expected.addPolicy("Policy C");
+
+        ResourcePermission policy = authorizationPage.authorizationTabs().permissions().create(expected, false);
+
+        RolePolicyRepresentation childPolicy = new RolePolicyRepresentation();
+
+        childPolicy.setName(UUID.randomUUID().toString());
+        childPolicy.addRole("Role A");
+
+        policy.createPolicy(childPolicy);
+        policy.form().save();
+
+        assertAlertSuccess();
+
+        expected.addPolicy(childPolicy.getName());
+
+        authorizationPage.navigateTo();
+        ResourcePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+    }
+
     private ResourcePermissionRepresentation createPermission(ResourcePermissionRepresentation expected) {
-        ResourcePermission policy = authorizationPage.authorizationTabs().permissions().create(expected);
+        ResourcePermission policy = authorizationPage.authorizationTabs().permissions().create(expected, true);
         assertAlertSuccess();
         return assertPolicy(expected, policy);
     }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
index 7599537..2e2804f 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopePermissionManagementTest.java
@@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.UUID;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
@@ -29,11 +31,13 @@ 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.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.ResourcePermission;
 import org.keycloak.testsuite.console.page.clients.authorization.permission.ScopePermission;
 
 /**
@@ -210,8 +214,70 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
         assertNull(authorizationPage.authorizationTabs().permissions().permissions().findByName(expected.getName()));
     }
 
+    @Test
+    public void testCreateUpdateWithChild() {
+        ScopePermissionRepresentation expected = new ScopePermissionRepresentation();
+
+        expected.setName(UUID.randomUUID().toString());
+        expected.setDescription("description");
+        expected.addScope("Scope C");
+        expected.addPolicy("Policy C");
+
+        ScopePermission policy = authorizationPage.authorizationTabs().permissions().create(expected, false);
+
+        RolePolicyRepresentation childPolicy = new RolePolicyRepresentation();
+
+        childPolicy.setName(UUID.randomUUID().toString());
+        childPolicy.addRole("Role A");
+
+        policy.createPolicy(childPolicy);
+        policy.form().save();
+        assertAlertSuccess();
+
+        expected.addPolicy(childPolicy.getName());
+
+        authorizationPage.navigateTo();
+        ScopePermission actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+
+        RolePolicyRepresentation childPolicy2 = new RolePolicyRepresentation();
+
+        childPolicy2.setName(UUID.randomUUID().toString());
+        childPolicy2.addRole("Role A");
+
+        policy.createPolicy(childPolicy2);
+        policy.form().save();
+        assertAlertSuccess();
+        expected.addPolicy(childPolicy2.getName());
+
+        authorizationPage.navigateTo();
+        actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+
+        expected.addResource("Resource B");
+        expected.getScopes().clear();
+        expected.addScope("Scope B", "Scope C");
+        expected.getScopes().remove("Policy C");
+
+        RolePolicyRepresentation childPolicy3 = new RolePolicyRepresentation();
+
+        childPolicy3.setName(UUID.randomUUID().toString());
+        childPolicy3.addRole("Role A");
+
+        policy.update(expected, false);
+
+        policy.createPolicy(childPolicy3);
+        policy.form().save();
+        assertAlertSuccess();
+        expected.addPolicy(childPolicy3.getName());
+
+        authorizationPage.navigateTo();
+        actual = authorizationPage.authorizationTabs().permissions().name(expected.getName());
+        assertPolicy(expected, actual);
+    }
+
     private ScopePermissionRepresentation createPermission(ScopePermissionRepresentation expected) {
-        ScopePermission policy = authorizationPage.authorizationTabs().permissions().create(expected);
+        ScopePermission policy = authorizationPage.authorizationTabs().permissions().create(expected, true);
         assertAlertSuccess();
         return assertPolicy(expected, policy);
     }
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 e6f7d5c..ab50fb7 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
@@ -1309,8 +1309,10 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
 
             var policies = [];
 
-            for (i = 0; i < $scope.selectedPolicies.length; i++) {
-                policies.push($scope.selectedPolicies[i].id);
+            if ($scope.selectedPolicies) {
+                for (i = 0; i < $scope.selectedPolicies.length; i++) {
+                    policies.push($scope.selectedPolicies[i].id);
+                }
             }
 
             $scope.policy.policies = policies;
@@ -1355,8 +1357,10 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
 
             var policies = [];
 
-            for (i = 0; i < $scope.selectedPolicies.length; i++) {
-                policies.push($scope.selectedPolicies[i].id);
+            if ($scope.selectedPolicies) {
+                for (i = 0; i < $scope.selectedPolicies.length; i++) {
+                    policies.push($scope.selectedPolicies[i].id);
+                }
             }
 
             $scope.policy.policies = policies;
@@ -2286,6 +2290,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
                     var policy = angular.copy(data);
 
                     $scope.changed = $scope.historyBackOnSaveOrCancel || PolicyController.isBackNewAssociatedPolicy();
+                    $scope.policy = angular.copy(policy);
 
                     if (PolicyController.isBackNewAssociatedPolicy()) {
                         if (delegate.onRestoreState) {
@@ -2296,8 +2301,6 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
                         delegate.onInitUpdate(policy);
                     }
 
-                    $scope.policy = angular.copy(policy);
-
                     $scope.$watch('policy', function() {
                         if (!angular.equals($scope.policy, policy)) {
                             $scope.changed = true;
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 cbd06fd..4716d7f 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
@@ -55,36 +55,41 @@
             <div class="form-group clearfix">
                 <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
                 <div class="col-sm-6">
-                     <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." />
-                    <p/>
-                    <table class="table table-striped table-bordered" id="selected-policies">
+                    <table class="table table-striped table-bordered" style="margin-top: 0px" id="selected-policies">
                         <thead>
                         <tr>
-                            <th class="kc-table-actions" colspan="3">
-                                <div class="form-inline">
-                                    <div class="form-group">
-                                    </div>
-                                    <div class="pull-right">
-                                        <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>
-                                        </select>
+                            <th class="kc-table-actions" colspan="2">
+                                <div class="form-inline col-md-12" style="width: 107%">
+                                    <div class="form-group" style="width: 100%">
+                                        <div class="input-group" style="width: 100%">
+                                            <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..."/>
+                                        </div>
                                     </div>
                                 </div>
                             </th>
+                            <th class="kc-table-actions">
+                                <div class="pull-right" style="width: 100%">
+                                    <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);"
+                                            data-ng-hide="historyBackOnSaveOrCancel">
+                                        <option value="" disabled selected>{{:: 'authz-create-policy' | translate}}...</option>
+                                    </select>
+                                </div>
+                            </th>
                         </tr>
                         <tr data-ng-hide="!selectedPolicies || selectedPolicies.length == 0">
                             <th>{{:: 'name' | translate}}</th>
                             <th>{{:: 'description' | translate}}</th>
-                            <th>{{:: 'actions' | translate}}</th>
+                            <th width="20%">{{:: 'actions' | translate}}</th>
                         </tr>
                         </thead>
                         <tbody>
                         <tr ng-repeat="policy in selectedPolicies">
-                            <td><a href="" data-ng-click="detailPolicy(policy)">{{policy.name}}</a></td>
+                            <td data-ng-hide="historyBackOnSaveOrCancel"><a href="" data-ng-click="detailPolicy(policy)">{{policy.name}}</a></td>
+                            <td data-ng-show="historyBackOnSaveOrCancel">{{policy.name}}</td>
                             <td>{{policy.description}}</td>
-                            <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);">
+                            <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);" style="vertical-align: middle">
                                 {{:: 'remove' | translate}}
                             </td>
                         </tr>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
index 4f469a2..a7c5f3d 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
@@ -59,36 +59,41 @@
             <div class="form-group clearfix">
                 <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
                 <div class="col-sm-6">
-                    <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." />
-                    <p/>
-                    <table class="table table-striped table-bordered" id="selected-policies">
+                    <table class="table table-striped table-bordered" style="margin-top: 0px" id="selected-policies">
                         <thead>
                         <tr>
-                            <th class="kc-table-actions" colspan="3">
-                                <div class="form-inline">
-                                    <div class="form-group">
-                                    </div>
-                                    <div class="pull-right">
-                                        <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>
-                                        </select>
+                            <th class="kc-table-actions" colspan="2">
+                                <div class="form-inline col-md-12" style="width: 107%">
+                                    <div class="form-group" style="width: 100%">
+                                        <div class="input-group" style="width: 100%">
+                                            <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..."/>
+                                        </div>
                                     </div>
                                 </div>
                             </th>
+                            <th class="kc-table-actions">
+                                <div class="pull-right" style="width: 100%">
+                                    <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);"
+                                            data-ng-hide="historyBackOnSaveOrCancel">
+                                        <option value="" disabled selected>{{:: 'authz-create-policy' | translate}}...</option>
+                                    </select>
+                                </div>
+                            </th>
                         </tr>
                         <tr data-ng-hide="!selectedPolicies || selectedPolicies.length == 0">
                             <th>{{:: 'name' | translate}}</th>
                             <th>{{:: 'description' | translate}}</th>
-                            <th>{{:: 'actions' | translate}}</th>
+                            <th width="20%">{{:: 'actions' | translate}}</th>
                         </tr>
                         </thead>
                         <tbody>
                         <tr ng-repeat="policy in selectedPolicies">
-                            <td><a href="" data-ng-click="detailPolicy(policy)">{{policy.name}}</a></td>
+                            <td data-ng-hide="historyBackOnSaveOrCancel"><a href="" data-ng-click="detailPolicy(policy)">{{policy.name}}</a></td>
+                            <td data-ng-show="historyBackOnSaveOrCancel">{{policy.name}}</td>
                             <td>{{policy.description}}</td>
-                            <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);">
+                            <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);" style="vertical-align: middle">
                                 {{:: 'remove' | translate}}
                             </td>
                         </tr>
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 acaa7a2..25be65b 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
@@ -35,30 +35,33 @@
             <div class="form-group clearfix">
                 <label class="col-md-2 control-label" for="policies">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
                 <div class="col-sm-6">
-                    <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." data-ng-required="!selectedPolicies || selectedPolicies.length == 0"/>
-                    <p/>
-                    <table class="table table-striped table-bordered" id="selected-policies">
+                    <table class="table table-striped table-bordered" style="margin-top: 0px" id="selected-policies">
                         <thead>
                             <tr>
-                                <th class="kc-table-actions" colspan="3">
-                                    <div class="form-inline">
-                                        <div class="form-group">
-                                        </div>
-                                        <div class="pull-right">
-                                            <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);"
-                                                    data-ng-hide="historyBackOnSaveOrCancel">
-                                                <option value="" disabled selected>{{:: 'authz-create-policy' | translate}}...</option>
-                                            </select>
+                                <th class="kc-table-actions" colspan="2">
+                                    <div class="form-inline col-md-12" style="width: 107%">
+                                        <div class="form-group" style="width: 100%">
+                                            <div class="input-group" style="width: 100%">
+                                                <input type="hidden" ui-select2="policiesUiSelect" id="policies" data-ng-change="selectPolicy(selectedPolicy);" data-ng-model="selectedPolicy" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." data-ng-required="!selectedPolicies || selectedPolicies.length == 0"/>
+                                            </div>
                                         </div>
                                     </div>
                                 </th>
+                                <th class="kc-table-actions">
+                                    <div class="pull-right" style="width: 100%">
+                                        <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);"
+                                                data-ng-hide="historyBackOnSaveOrCancel">
+                                            <option value="" disabled selected>{{:: 'authz-create-policy' | translate}}...</option>
+                                        </select>
+                                    </div>
+                                </th>
                             </tr>
                             <tr data-ng-hide="!selectedPolicies || selectedPolicies.length == 0">
                                 <th>{{:: 'name' | translate}}</th>
                                 <th>{{:: 'description' | translate}}</th>
-                                <th>{{:: 'actions' | translate}}</th>
+                                <th width="20%">{{:: 'actions' | translate}}</th>
                             </tr>
                         </thead>
                         <tbody>
@@ -66,7 +69,7 @@
                                 <td data-ng-hide="historyBackOnSaveOrCancel"><a href="" data-ng-click="detailPolicy(policy)">{{policy.name}}</a></td>
                                 <td data-ng-show="historyBackOnSaveOrCancel">{{policy.name}}</td>
                                 <td>{{policy.description}}</td>
-                                <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);">
+                                <td class="kc-action-cell" ng-click="removePolicy(selectedPolicies, policy);" style="vertical-align: middle">
                                     {{:: 'remove' | translate}}
                                 </td>
                             </tr>