[SYNCOPE-1360] Reworking DynRealms-based delegated administration from Admin Console
authorFrancesco Chicchiriccò <ilgrosso@apache.org>
Mon, 3 Sep 2018 15:28:05 +0000 (17:28 +0200)
committerFrancesco Chicchiriccò <ilgrosso@apache.org>
Mon, 3 Sep 2018 15:28:14 +0000 (17:28 +0200)
client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/Action.java
client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
src/main/asciidoc/reference-guide/concepts/roles.adoc

index bfde815..2f952ee 100644 (file)
@@ -20,8 +20,10 @@ package org.apache.syncope.client.console;
 
 import java.text.DateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -32,6 +34,7 @@ import java.util.concurrent.Future;
 import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.MediaType;
 import org.apache.commons.collections4.list.SetUniqueList;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.commons.lang3.tuple.Pair;
@@ -224,19 +227,33 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession {
         return sortable;
     }
 
-    public boolean owns(final String entitlements) {
-        return owns(entitlements, SyncopeConstants.ROOT_REALM);
-    }
-
-    public boolean owns(final String entitlements, final String realm) {
+    public boolean owns(final String entitlements, final String... realms) {
         if (StringUtils.isEmpty(entitlements)) {
             return true;
         }
 
+        if (auth == null) {
+            return false;
+        }
+
+        Set<String> requested = ArrayUtils.isEmpty(realms)
+                ? Collections.singleton(SyncopeConstants.ROOT_REALM)
+                : new HashSet<>(Arrays.asList(realms));
+
         for (String entitlement : entitlements.split(",")) {
-            if (auth != null && auth.containsKey(entitlement) && (realm == null
-                    || auth.get(entitlement).stream().anyMatch(ownedRealm -> realm.startsWith(ownedRealm)))) {
-                return true;
+            if (auth.containsKey(entitlement)) {
+                boolean owns = false;
+
+                Set<String> owned = auth.get(entitlement);
+                for (String realm : requested) {
+                    if (realm.startsWith(SyncopeConstants.ROOT_REALM)) {
+                        owns |= owned.stream().anyMatch(ownedRealm -> realm.startsWith(ownedRealm));
+                    } else {
+                        owns |= owned.contains(realm);
+                    }
+                }
+
+                return owns;
             }
         }
 
index 8fdc994..ec8a995 100644 (file)
@@ -101,7 +101,11 @@ public abstract class AnyDirectoryPanel<A extends AnyTO, E extends AbstractAnyRe
         } else {
             MetaDataRoleAuthorizationStrategy.unauthorizeAll(addAjaxLink, RENDER);
         }
-        setReadOnly(!SyncopeConsoleSession.get().owns(String.format("%s_UPDATE", builder.type), builder.realm));
+        if (builder.dynRealm == null) {
+            setReadOnly(!SyncopeConsoleSession.get().owns(String.format("%s_UPDATE", builder.type), builder.realm));
+        } else {
+            setReadOnly(!SyncopeConsoleSession.get().owns(String.format("%s_UPDATE", builder.type), builder.dynRealm));
+        }
 
         this.realm = builder.realm;
         this.type = builder.type;
@@ -243,6 +247,8 @@ public abstract class AnyDirectoryPanel<A extends AnyTO, E extends AbstractAnyRe
          */
         protected String realm = SyncopeConstants.ROOT_REALM;
 
+        protected String dynRealm = null;
+
         /**
          * Any type related to current panel.
          */
@@ -266,6 +272,11 @@ public abstract class AnyDirectoryPanel<A extends AnyTO, E extends AbstractAnyRe
             return this;
         }
 
+        public Builder<A, E> setDynRealm(final String dynRealm) {
+            this.dynRealm = dynRealm;
+            return this;
+        }
+
         @Override
         public List<AnyTypeClassTO> getAnyTypeClassTOs() {
             return this.anyTypeClassTOs;
index 7a642dc..34eeed5 100644 (file)
@@ -111,7 +111,7 @@ public class AnyObjectDirectoryPanel extends AnyDirectoryPanel<AnyObjectTO, AnyO
             }
         }, ActionType.EDIT,
                 String.format("%s,%s", AnyEntitlement.READ.getFor(type), AnyEntitlement.UPDATE.getFor(type))).
-                setRealm(realm);
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<AnyObjectTO>() {
 
@@ -156,7 +156,7 @@ public class AnyObjectDirectoryPanel extends AnyDirectoryPanel<AnyObjectTO, AnyO
                 }
             }, ActionType.MANAGE_RESOURCES,
                     String.format("%s,%s", AnyEntitlement.READ.getFor(type), AnyEntitlement.UPDATE.getFor(type))).
-                    setRealm(realm);
+                    setRealms(realm, model.getObject().getDynRealms());
 
             panel.add(
                     new ActionLink<AnyObjectTO>() {
index 127471b..12ec331 100644 (file)
@@ -250,7 +250,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 panel = new UserDirectoryPanel.Builder(
                         anyTypeClassRestClient.list(anyTypeTO.getClasses()),
                         anyTypeTO.getKey(),
-                        pageRef).setRealm(realm).setFiltered(true).
+                        pageRef).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
                         setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(FormLayoutInfoUtils.instantiate(
                         userTO,
                         anyTypeTO.getClasses(),
@@ -269,7 +269,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 panel = new GroupDirectoryPanel.Builder(
                         anyTypeClassRestClient.list(anyTypeTO.getClasses()),
                         anyTypeTO.getKey(),
-                        pageRef).setRealm(realm).setFiltered(true).
+                        pageRef).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
                         setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(FormLayoutInfoUtils.instantiate(
                         groupTO,
                         anyTypeTO.getClasses(),
@@ -291,7 +291,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 panel = new AnyObjectDirectoryPanel.Builder(
                         anyTypeClassRestClient.list(anyTypeTO.getClasses()),
                         anyTypeTO.getKey(),
-                        pageRef).setRealm(realm).setFiltered(true).
+                        pageRef).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
                         setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(FormLayoutInfoUtils.instantiate(
                         anyObjectTO,
                         anyTypeTO.getClasses(),
index ac5663b..e7026c2 100644 (file)
@@ -216,7 +216,7 @@ public class GroupDirectoryPanel extends AnyDirectoryPanel<GroupTO, GroupRestCli
             }
         }, ActionType.EDIT,
                 String.format("%s,%s", StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE)).
-                setRealm(realm);
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<GroupTO>() {
 
@@ -247,7 +247,8 @@ public class GroupDirectoryPanel extends AnyDirectoryPanel<GroupTO, GroupRestCli
                 typeExtensionsModal.header(new StringResourceModel("typeExtensions", model));
                 typeExtensionsModal.show(true);
             }
-        }, ActionType.TYPE_EXTENSIONS, StandardEntitlement.GROUP_UPDATE).setRealm(realm);
+        }, ActionType.TYPE_EXTENSIONS, StandardEntitlement.GROUP_UPDATE).
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<GroupTO>() {
 
@@ -265,7 +266,7 @@ public class GroupDirectoryPanel extends AnyDirectoryPanel<GroupTO, GroupRestCli
             }
         }, ActionType.MEMBERS,
                 String.format("%s,%s", StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE)).
-                setRealm(realm);
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<GroupTO>() {
 
@@ -333,7 +334,7 @@ public class GroupDirectoryPanel extends AnyDirectoryPanel<GroupTO, GroupRestCli
             }
         }, ActionType.MANAGE_RESOURCES,
                 String.format("%s,%s", StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE)).
-                setRealm(realm);
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<GroupTO>() {
 
index b0edf68..597a4f5 100644 (file)
@@ -141,7 +141,7 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
             }
         }, ActionType.EDIT,
                 String.format("%s,%s", StandardEntitlement.USER_READ, StandardEntitlement.USER_UPDATE)).
-                setRealm(realm);
+                setRealms(realm, model.getObject().getDynRealms());
 
         panel.add(new ActionLink<UserTO>() {
 
@@ -183,7 +183,8 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
                 }
                 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
-        }, ActionType.MUSTCHANGEPASSWORD, StandardEntitlement.USER_UPDATE).setRealm(realm);
+        }, ActionType.MUSTCHANGEPASSWORD, StandardEntitlement.USER_UPDATE).
+                setRealms(realm, model.getObject().getDynRealms());
 
         if (wizardInModal) {
             panel.add(new ActionLink<UserTO>() {
@@ -206,7 +207,8 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
 
                     displayAttributeModal.show(true);
                 }
-            }, ActionType.PASSWORD_MANAGEMENT, StandardEntitlement.USER_UPDATE).setRealm(realm);
+            }, ActionType.PASSWORD_MANAGEMENT, StandardEntitlement.USER_UPDATE).
+                    setRealms(realm, model.getObject().getDynRealms());
 
             if (SyncopeConsoleSession.get().getPlatformInfo().isPwdResetAllowed()
                     && !SyncopeConsoleSession.get().getPlatformInfo().isPwdResetRequiringSecurityQuestions()) {
@@ -230,7 +232,8 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
                         }
                         ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
                     }
-                }, ActionType.REQUEST_PASSWORD_RESET, StandardEntitlement.USER_UPDATE).setRealm(realm);
+                }, ActionType.REQUEST_PASSWORD_RESET, StandardEntitlement.USER_UPDATE).
+                        setRealms(realm, model.getObject().getDynRealms());
             }
 
             panel.add(new ActionLink<UserTO>() {
@@ -255,7 +258,8 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
 
                     altDefaultModal.show(true);
                 }
-            }, ActionType.ENABLE, StandardEntitlement.USER_UPDATE).setRealm(realm);
+            }, ActionType.ENABLE, StandardEntitlement.USER_UPDATE).
+                    setRealms(realm, model.getObject().getDynRealms());
 
             panel.add(new ActionLink<UserTO>() {
 
@@ -281,7 +285,7 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
                 }
             }, ActionType.MANAGE_RESOURCES,
                     String.format("%s,%s", StandardEntitlement.USER_READ, StandardEntitlement.USER_UPDATE)).
-                    setRealm(realm);
+                    setRealms(realm, model.getObject().getDynRealms());
 
             panel.add(new ActionLink<UserTO>() {
 
index 98d2556..f01a984 100644 (file)
@@ -19,6 +19,8 @@
 package org.apache.syncope.client.console.wicket.markup.html.form;
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.wicket.model.Model;
 
@@ -31,7 +33,7 @@ public final class Action<T extends Serializable> implements Serializable {
 
     private static final long serialVersionUID = -7989237020377623993L;
 
-    private String realm = null;
+    private final List<String> realms = new ArrayList<>();
 
     private final ActionLink<T> link;
 
@@ -66,12 +68,24 @@ public final class Action<T extends Serializable> implements Serializable {
         this.indicator = true;
     }
 
-    public String getRealm() {
-        return realm;
+    public String[] getRealms() {
+        return realms.toArray(new String[realms.size()]);
     }
 
     public void setRealm(final String realm) {
-        this.realm = realm;
+        this.realms.clear();
+
+        if (realm != null) {
+            this.realms.add(realm);
+        }
+    }
+
+    public void setRealms(final String realm, final List<String> dynRealms) {
+        setRealm(realm);
+
+        if (dynRealms != null) {
+            this.realms.addAll(dynRealms);
+        }
     }
 
     public ActionLink<T> getLink() {
@@ -104,7 +118,7 @@ public final class Action<T extends Serializable> implements Serializable {
         this.visibleLabel = false;
         return this;
     }
-    
+
     public Action<T> showLabel() {
         this.visibleLabel = true;
         return this;
index 91f83b9..6b98619 100644 (file)
@@ -130,7 +130,7 @@ public final class ActionPanel<T extends Serializable> extends Panel {
             };
         }
 
-        if (SyncopeConsoleSession.get().owns(action.getEntitlements(), action.getRealm())) {
+        if (SyncopeConsoleSession.get().owns(action.getEntitlements(), action.getRealms())) {
             MetaDataRoleAuthorizationStrategy.authorizeAll(actionLink, RENDER);
         } else {
             MetaDataRoleAuthorizationStrategy.unauthorizeAll(actionLink, RENDER);
index df2f270..d328c43 100644 (file)
@@ -586,10 +586,10 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                 userDAO.findAllResources(user),
                 details);
 
-        if (details) {
-            // dynamic realms
-            userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey()));
+        // dynamic realms
+        userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey()));
 
+        if (details) {
             // roles
             userTO.getRoles().addAll(user.getRoles().stream().map(Entity::getKey).collect(Collectors.toList()));
 
index 28e00e7..af28661 100644 (file)
@@ -104,4 +104,5 @@ For example, the following entitlements are normally required to be granted for
 . `USER_READ`
 . `ANYTYPE_READ`
 . `REALM_LIST`
+. `GROUP_SEARCH`
 ====