Improve role management
authorMathieu <mbaudier@argeo.org>
Tue, 8 Nov 2022 04:35:05 +0000 (05:35 +0100)
committerMathieu <mbaudier@argeo.org>
Tue, 8 Nov 2022 04:35:05 +0000 (05:35 +0100)
org.argeo.api.cms/src/org/argeo/api/cms/directory/HierarchyUnit.java
org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java
org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java
org.argeo.cms/src/org/argeo/cms/directory/ldap/AbstractLdapDirectory.java
org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapHierarchyUnit.java
org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java

index 04593d94e428ef766fa87831fbaa0f3c745aaee7..f04dc4648637f9ca3c3197f584f7e70b41411998 100644 (file)
@@ -26,7 +26,11 @@ public interface HierarchyUnit {
         * @return <code>true</code> if functional, <code>false</code> is technical
         *         (e.g. People, Groups, etc.)
         */
-       boolean isFunctional();
+       default boolean isFunctional() {
+               return isType(Type.FUNCTIONAL);
+       }
+
+       boolean isType(Type type);
 
        /**
         * The base of this organisational unit within the hierarchy. This would
@@ -39,4 +43,11 @@ public interface HierarchyUnit {
 
        /** Its metadata (typically LDAP attributes). */
        Dictionary<String, Object> getProperties();
+
+       enum Type {
+               PEOPLE, //
+               GROUPS, //
+               ROLES, //
+               FUNCTIONAL;
+       }
 }
index ee522a580d8417c4d1f0aac52cd240024291ed11..f2b4f0a58250ae584a0f9ee217df26bcc2ed1f81 100644 (file)
@@ -75,7 +75,7 @@ public final class CurrentUser {
                return role.implied(currentSubject(), context);
        }
 
-       /** Implies this {@link SystemRole} in this context. */
+       /** Implies this role name, also independently of the context. */
        public final static boolean implies(String role, String context) {
                return SystemRole.implied(NamespaceUtils.parsePrefixedName(role), currentSubject(), context);
        }
index bf91f39413c84bba1a8181d7f0cb5032d18a33ca..a281c2f7d3084a5e86e83524e78f628389edbdc4 100644 (file)
@@ -1,25 +1,41 @@
 package org.argeo.cms.auth;
 
+import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.ArgeoNamespace;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.cms.directory.ldap.LdapNameUtils;
+
+/** Simplifies analysis of system roles. */
 public class RoleNameUtils {
+       public static String getLastRdnValue(String dn) {
+               return LdapNameUtils.getLastRdnValue(dn);
+//             // we don't use LdapName for portability with Android
+//             // TODO make it more robust
+//             String[] parts = dn.split(",");
+//             String[] rdn = parts[0].split("=");
+//             return rdn[1];
+       }
+
+       public static QName getLastRdnAsName(String dn) {
+               String cn = getLastRdnValue(dn);
+               QName roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn);
+               return roleName;
+       }
 
-       /*
-        * UTILITIES
-        */
-       public final static String getLastRdnValue(String dn) {
-               // we don't use LdapName for portability with Android
-               // TODO make it more robust
-               String[] parts = dn.split(",");
-               String[] rdn = parts[0].split("=");
-               return rdn[1];
+       public static boolean isSystemRole(QName roleName) {
+               return roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI);
        }
 
-       public final static String getParent(String dn) {
+       public static String getParent(String dn) {
                int index = dn.indexOf(',');
                return dn.substring(index + 1);
        }
 
        /** Up two levels. */
-       public final static String getContext(String dn) {
+       public static String getContext(String dn) {
                return getParent(getParent(dn));
        }
 }
index 5d62d9803f08a3eb033cd929949c505bab92f679..646752d412f988d7ba3ddb60e05edcff7e0f5b67 100644 (file)
@@ -8,13 +8,27 @@ import javax.xml.namespace.QName;
 import org.argeo.api.cms.CmsConstants;
 import org.argeo.cms.internal.auth.ImpliedByPrincipal;
 
+/** A programmatic role. */
 public interface SystemRole {
        QName getName();
 
+       /** Whether this role is implied for this authenticated user. */
        default boolean implied(Subject subject, String context) {
                return implied(getName(), subject, context);
        }
 
+       /** Whether this role is implied for this distinguished name. */
+       default boolean implied(String dn, String context) {
+               String roleContext = RoleNameUtils.getContext(dn);
+               QName roleName = RoleNameUtils.getLastRdnAsName(dn);
+               return roleContext.equalsIgnoreCase(context) && getName().equals(roleName);
+       }
+
+       /**
+        * Whether this role is implied for this authenticated subject. If context is
+        * <code>null</code>, it is not considered; this should be used to build user
+        * interfaces, but not to authorise.
+        */
        static boolean implied(QName name, Subject subject, String context) {
                Set<ImpliedByPrincipal> roles = subject.getPrincipals(ImpliedByPrincipal.class);
                for (ImpliedByPrincipal role : roles) {
@@ -30,6 +44,5 @@ public interface SystemRole {
                        }
                }
                return false;
-
        }
 }
index 06c33b01129107d2b7d724f59a9646a1a1b4fa18..9bc13ef00a9cb90a5077b0e904229fdf0b4c8b00 100644 (file)
@@ -320,8 +320,8 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
        }
 
        @Override
-       public boolean isFunctional() {
-               return true;
+       public boolean isType(Type type) {
+               return Type.FUNCTIONAL.equals(type);
        }
 
        @Override
index 7abf0988534a9aceb58d76598e39b71028aa5384..0e005133a10aec011b39316ba21567c85475ec3f 100644 (file)
@@ -9,14 +9,24 @@ import org.argeo.api.cms.directory.HierarchyUnit;
 
 /** LDIF/LDAP based implementation of {@link HierarchyUnit}. */
 public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit {
-       private final boolean functional;
+//     private final boolean functional;
+
+       private final Type type;
 
        public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn) {
                super(directory, dn);
 
                Rdn rdn = LdapNameUtils.getLastRdn(dn);
-               functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn)
-                               || directory.getSystemRoleBaseRdn().equals(rdn));
+               if (directory.getUserBaseRdn().equals(rdn))
+                       type = Type.PEOPLE;
+               else if (directory.getGroupBaseRdn().equals(rdn))
+                       type = Type.GROUPS;
+               else if (directory.getSystemRoleBaseRdn().equals(rdn))
+                       type = Type.ROLES;
+               else
+                       type = Type.FUNCTIONAL;
+//             functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn)
+//                             || directory.getSystemRoleBaseRdn().equals(rdn));
        }
 
        @Override
@@ -30,8 +40,8 @@ public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit
        }
 
        @Override
-       public boolean isFunctional() {
-               return functional;
+       public boolean isType(Type type) {
+               return this.type.equals(type);
        }
 
        @Override
index 7472908cd565656d09692d23370d2c8a51f2629d..15c47293ec06cf8423aefcb60cd5c146b7e4c113 100644 (file)
@@ -1,15 +1,11 @@
 package org.argeo.cms.internal.auth;
 
-import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext;
-
 import java.security.Principal;
 import java.util.HashSet;
 import java.util.Set;
 
 import javax.xml.namespace.QName;
 
-import org.argeo.api.acr.ArgeoNamespace;
-import org.argeo.api.acr.NamespaceUtils;
 import org.argeo.cms.auth.RoleNameUtils;
 import org.osgi.service.useradmin.Authorization;
 
@@ -23,71 +19,25 @@ import org.osgi.service.useradmin.Authorization;
  */
 public final class ImpliedByPrincipal implements Principal {
        private final String name;
-       private Set<Principal> causes = new HashSet<Principal>();
-
-       private QName roleName;
-//     private int type = Role.ROLE;
+       private final QName roleName;
+       private final boolean systemRole;
+       private final String context;
 
-       private boolean systemRole = false;
-       private String context;
+       private Set<Principal> causes = new HashSet<Principal>();
 
        public ImpliedByPrincipal(String name, Principal userPrincipal) {
                this.name = name;
-               String cn = RoleNameUtils.getLastRdnValue(name);
-               roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn);
-               if (roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI)) {
-                       systemRole = true;
-               }
+               roleName = RoleNameUtils.getLastRdnAsName(name);
+               systemRole = RoleNameUtils.isSystemRole(roleName);
                context = RoleNameUtils.getContext(name);
-//             try {
-//                     this.name = new LdapName(name);
-//             } catch (InvalidNameException e) {
-//                     throw new IllegalArgumentException("Badly formatted role name", e);
-//             }
                if (userPrincipal != null)
                        causes.add(userPrincipal);
        }
 
-//     public ImpliedByPrincipal(LdapName name, Principal userPrincipal) {
-//             this.name = name;
-//             if (userPrincipal != null)
-//                     causes.add(userPrincipal);
-//     }
-
        public String getName() {
                return name;
        }
 
-       /*
-        * USER ADMIN
-        */
-//     public boolean addMember(Principal user) {
-//             throw new UnsupportedOperationException();
-//     }
-//
-//     public boolean removeMember(Principal user) {
-//             throw new UnsupportedOperationException();
-//     }
-//
-//     public boolean isMember(Principal member) {
-//             return causes.contains(member);
-//     }
-//
-//     public Enumeration<? extends Principal> members() {
-//             return Collections.enumeration(causes);
-//     }
-//
-//
-//     /** Type of {@link Role}, if known. */
-//     public int getType() {
-//             return type;
-//     }
-//
-//     /** Not supported for the time being. */
-//     public Dictionary<String, Object> getProperties() {
-//             throw new UnsupportedOperationException();
-//     }
-
        /*
         * OBJECT
         */
@@ -111,8 +61,6 @@ public final class ImpliedByPrincipal implements Principal {
 
        @Override
        public boolean equals(Object obj) {
-               // if (this == obj)
-               // return true;
                if (obj instanceof ImpliedByPrincipal) {
                        ImpliedByPrincipal that = (ImpliedByPrincipal) obj;
                        // TODO check members too?
@@ -123,7 +71,6 @@ public final class ImpliedByPrincipal implements Principal {
 
        @Override
        public String toString() {
-               // return name.toString() + " implied by " + causes;
                return name.toString();
        }
 }