Introduce system roles
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 21 Jun 2022 09:27:36 +0000 (11:27 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 21 Jun 2022 09:27:36 +0000 (11:27 +0200)
31 files changed:
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java
org.argeo.api.acr/src/org/argeo/api/acr/CrName.java
org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java [new file with mode: 0644]
org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java
org.argeo.api.cms/src/org/argeo/api/cms/CmsSession.java
org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java
org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java
org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java
org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContent.java
org.argeo.cms/src/org/argeo/cms/acr/directory/HierarchyUnitContent.java
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/CmsRole.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java
org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java
org.argeo.cms/src/org/argeo/cms/security/NodeSecurityUtils.java [deleted file]
org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/HierarchyUnit.java
org.argeo.util/src/org/argeo/osgi/useradmin/IpaUtils.java
org.argeo.util/src/org/argeo/osgi/useradmin/LdifHierarchyUnit.java
org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java
org.argeo.util/src/org/argeo/osgi/useradmin/UserAdminConf.java
org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java

index 8a360509527e41ac4eb3a562498d3fe4784e757b..95b1eb2cb0e1a28ebf6bf38492093de10c5e1ea6 100644 (file)
@@ -8,9 +8,9 @@ import java.util.Collection;
 import java.util.List;
 
 import javax.annotation.PostConstruct;
-import javax.naming.ldap.LdapName;
 
 import org.argeo.api.cms.CmsSession;
+import org.argeo.cms.auth.RoleNameUtils;
 import org.argeo.eclipse.ui.ColumnViewerComparator;
 import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
 import org.argeo.util.LangUtils;
@@ -88,8 +88,8 @@ public class CmsSessionsView {
                        private static final long serialVersionUID = -5234573509093747505L;
 
                        public String getText(Object element) {
-                               LdapName userDn = ((CmsSession) element).getUserDn();
-                               return userDn.getRdn(userDn.size() - 1).getValue().toString();
+                               String userDn = ((CmsSession) element).getUserDn();
+                               return RoleNameUtils.getLastRdnValue(userDn);
                        }
 
                        public String getToolTipText(Object element) {
index dbbd970a78d84ba98fd06ffc4644fd2a56d5dbdb..934b880cd106da27dec592f9ec86fd822512489f 100644 (file)
@@ -91,7 +91,7 @@ public class UserAdminWrapper {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getGlobalId();
+                       String baseDn = userDirectory.getContext();
 
                        if (onlyWritable && readOnly)
                                continue;
index 1fee92e397dc70e6fdfb8477b1d41a25d9a6a8c0..a3f6d901045a411ebe80761619bef54a1d41f4d2 100644 (file)
@@ -41,6 +41,10 @@ public class ContentName extends QName {
 
 //     private final UUID uuid;
 
+       public ContentName(String namespaceURI, String localPart) {
+               super(namespaceURI, localPart, checkPrefix(RuntimeNamespaceContext.getNamespaceContext(), namespaceURI));
+       }
+
        public ContentName(String namespaceURI, String localPart, NamespaceContext nsContext) {
                super(namespaceURI, localPart, checkPrefix(nsContext, namespaceURI));
        }
index c772daca32a7964ac576e87d7bafad420532b393..f79c47446b4e431a5680f5142317606d1a9696fc 100644 (file)
@@ -34,16 +34,15 @@ public enum CrName implements ContentNameSupplier {
        ;
 
        public final static String CR_NAMESPACE_URI = "http://argeo.org/ns/cr";
-//     public final static String CR_BASIC_NAMESPACE_URI = CR_NAMESPACE_URI + "/basic";
-//     public final static String CR_OWNER_NAMESPACE_URI = CR_NAMESPACE_URI + "/owner";
-//     public final static String CR_POSIX_NAMESPACE_URI = CR_NAMESPACE_URI + "/posix";
 
        public final static String CR_DEFAULT_PREFIX = "cr";
 
        public final static String LDAP_NAMESPACE_URI = "http://argeo.org/ns/ldap";
        public final static String LDAP_DEFAULT_PREFIX = "ldap";
 
-       
+       public final static String ROLE_NAMESPACE_URI = "http://argeo.org/ns/role";
+       public final static String ROLE_DEFAULT_PREFIX = "role";
+
        private final ContentName value;
 
        CrName() {
diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java b/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java
new file mode 100644 (file)
index 0000000..d228c23
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.api.acr;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import javax.xml.namespace.NamespaceContext;
+
+/**
+ * Programmatically defined {@link NamespaceContext}, code contributing
+ * namespaces MUST register here with a single default prefix.
+ */
+public class RuntimeNamespaceContext implements NamespaceContext {
+       private NavigableMap<String, String> prefixes = new TreeMap<>();
+       private NavigableMap<String, String> namespaces = new TreeMap<>();
+
+       @Override
+       public String getPrefix(String namespaceURI) {
+               return NamespaceUtils.getPrefix((ns) -> {
+                       String prefix = namespaces.get(ns);
+                       if (prefix == null)
+                               throw new IllegalStateException("Namespace " + ns + " is not registered.");
+                       return prefix;
+               }, namespaceURI);
+       }
+
+       @Override
+       public String getNamespaceURI(String prefix) {
+               return NamespaceUtils.getNamespaceURI((p) -> {
+                       String ns = prefixes.get(p);
+                       if (ns == null)
+                               throw new IllegalStateException("Prefix " + p + " is not registered.");
+                       return ns;
+               }, prefix);
+       }
+
+       @Override
+       public Iterator<String> getPrefixes(String namespaceURI) {
+               return Collections.singleton(getPrefix(namespaceURI)).iterator();
+       }
+
+       /*
+        * STATIC
+        */
+
+       private final static RuntimeNamespaceContext INSTANCE = new RuntimeNamespaceContext();
+
+       static {
+               register(CrName.CR_NAMESPACE_URI, CrName.CR_DEFAULT_PREFIX);
+               register(CrName.LDAP_NAMESPACE_URI, CrName.LDAP_DEFAULT_PREFIX);
+               register(CrName.ROLE_NAMESPACE_URI, CrName.ROLE_DEFAULT_PREFIX);
+       }
+
+       public static NamespaceContext getNamespaceContext() {
+               return INSTANCE;
+       }
+
+       public static Map<String, String> getPrefixes() {
+               return Collections.unmodifiableNavigableMap(INSTANCE.prefixes);
+       }
+
+       public synchronized static void register(String namespaceURI, String prefix) {
+               NavigableMap<String, String> prefixes = INSTANCE.prefixes;
+               NavigableMap<String, String> namespaces = INSTANCE.namespaces;
+               if (prefixes.containsKey(prefix)) {
+                       String ns = prefixes.get(prefix);
+                       if (ns.equals(namespaceURI))
+                               return; // ignore silently
+                       throw new IllegalStateException("Prefix " + prefix + " is already registered with namespace URI " + ns);
+               }
+               if (namespaces.containsKey(namespaceURI)) {
+                       String p = namespaces.get(namespaceURI);
+                       if (p.equals(prefix))
+                               return; // ignore silently
+                       throw new IllegalStateException("Namespace " + namespaceURI + " is already registered with prefix " + p);
+               }
+               prefixes.put(prefix, namespaceURI);
+               namespaces.put(namespaceURI, prefix);
+       }
+}
index ba915ac4000a9e6268920b13e4535897af9ea047..9c4f6e633a5918ac0b50d053c0a541d66a76ab36 100644 (file)
@@ -6,6 +6,7 @@ import java.util.concurrent.CompletionStage;
 
 import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentSession;
+import org.argeo.api.acr.RuntimeNamespaceContext;
 
 /** A {@link ContentSession} implementation. */
 public interface ProvidedSession extends ContentSession {
@@ -29,57 +30,16 @@ public interface ProvidedSession extends ContentSession {
 
        @Override
        default String getPrefix(String namespaceURI) {
-               Iterator<String> prefixes = getPrefixes(namespaceURI);
-               return prefixes.hasNext() ? prefixes.next() : null;
+               return RuntimeNamespaceContext.getNamespaceContext().getPrefix(namespaceURI);
        }
 
-//     /** @return the bound namespace or null if not found */
-//     String findNamespace(String prefix);
-//
-//     // TODO find the default prefix?
-//     Set<String> findPrefixes(String namespaceURI);
-//
-//     /** To be overridden for optimisation, as it will be called a lot */
-//     default String findPrefix(String namespaceURI) {
-//             Set<String> prefixes = findPrefixes(namespaceURI);
-//             if (prefixes.isEmpty())
-//                     return null;
-//             return prefixes.iterator().next();
-//     }
-
-//     @Override
-//     default String getNamespaceURI(String prefix) {
-//             String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix);
-//             if (namespaceURI != null)
-//                     return namespaceURI;
-//             if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))
-//                     return XMLConstants.NULL_NS_URI;
-//             namespaceURI = findNamespace(prefix);
-//             if (namespaceURI != null)
-//                     return namespaceURI;
-//             return XMLConstants.NULL_NS_URI;
-//     }
-//
-//     @Override
-//     default String getPrefix(String namespaceURI) {
-//             String prefix = NamespaceUtils.getStandardPrefix(namespaceURI);
-//             if (prefix != null)
-//                     return prefix;
-//             if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
-//                     return XMLConstants.DEFAULT_NS_PREFIX;
-//             return findPrefix(namespaceURI);
-//     }
-//
-//     @Override
-//     default Iterator<String> getPrefixes(String namespaceURI) {
-//             Iterator<String> standard = NamespaceUtils.getStandardPrefixes(namespaceURI);
-//             if (standard != null)
-//                     return standard;
-//             if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
-//                     return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX).iterator();
-//             Set<String> prefixes = findPrefixes(namespaceURI);
-//             assert prefixes != null;
-//             return prefixes.iterator();
-//     }
+       @Override
+       default String getNamespaceURI(String prefix) {
+               return RuntimeNamespaceContext.getNamespaceContext().getNamespaceURI(prefix);
+       }
 
+       @Override
+       default Iterator<String> getPrefixes(String namespaceURI) {
+               return RuntimeNamespaceContext.getNamespaceContext().getPrefixes(namespaceURI);
+       }
 }
index ea9d10ba24d1573ed74df47bf9049e07969f7ed1..1d61c6c674c34506643ad5e5cabdedeacb3ea2de 100644 (file)
@@ -5,7 +5,6 @@ import java.util.Locale;
 import java.util.UUID;
 import java.util.function.Consumer;
 
-import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 
 /** An authenticated user session. */
@@ -18,7 +17,7 @@ public interface CmsSession {
 
        String getUserRole();
 
-       LdapName getUserDn();
+       String getUserDn();
 
        String getLocalId();
 
index 1481b3a40ff4125c32f94eaa3adda8c7f08364d9..2e65e7e691dee045cdb4eb940624691a61a8f8a6 100644 (file)
@@ -19,6 +19,7 @@ import javax.xml.transform.stream.StreamResult;
 
 import org.argeo.api.acr.Content;
 import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.RuntimeNamespaceContext;
 import org.argeo.api.acr.spi.ContentProvider;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedRepository;
@@ -111,10 +112,11 @@ public abstract class AbstractContentRepository implements ProvidedRepository {
                        document = dBuilder.newDocument();
                        Element root = document.createElementNS(CrName.CR_NAMESPACE_URI, CrName.ROOT.get().toPrefixedString());
 
-                       for (String prefix : typesManager.getPrefixes().keySet()) {
+                       for (String prefix : RuntimeNamespaceContext.getPrefixes().keySet()) {
 //                             root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix,
 //                                             contentTypesManager.getPrefixes().get(prefix));
-                               DomUtils.addNamespace(root, prefix, typesManager.getPrefixes().get(prefix));
+                               DomUtils.addNamespace(root, prefix,
+                                               RuntimeNamespaceContext.getNamespaceContext().getNamespaceURI(prefix));
                        }
 
                        document.appendChild(root);
index 0255856344e34109d05c301465be74d1415c32d7..4e1c3bcfd0846c53567c82459988564c278cb4a9 100644 (file)
@@ -17,6 +17,7 @@ import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentSession;
 import org.argeo.api.acr.CrName;
 import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.RuntimeNamespaceContext;
 import org.argeo.api.acr.spi.ContentProvider;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedRepository;
@@ -113,17 +114,19 @@ class CmsContentSession implements ProvidedSession {
         * NAMESPACE CONTEXT
         */
 
-       @Override
-       public String getNamespaceURI(String prefix) {
-               return NamespaceUtils.getNamespaceURI((p) -> contentRepository.getTypesManager().getPrefixes().get(p), prefix);
-       }
-
-       @Override
-       public Iterator<String> getPrefixes(String namespaceURI) {
-               return NamespaceUtils.getPrefixes((ns) -> contentRepository.getTypesManager().getPrefixes().entrySet().stream()
-                               .filter(e -> e.getValue().equals(ns)).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()),
-                               namespaceURI);
-       }
+//     @Override
+//     public String getNamespaceURI(String prefix) {
+//             return RuntimeNamespaceContext.getNamespaceContext().getNamespaceURI(prefix);
+////           return NamespaceUtils.getNamespaceURI((p) -> contentRepository.getTypesManager().getPrefixes().get(p), prefix);
+//     }
+//
+//     @Override
+//     public Iterator<String> getPrefixes(String namespaceURI) {
+//             return RuntimeNamespaceContext.getNamespaceContext().getPrefixes(namespaceURI);
+////           return NamespaceUtils.getPrefixes((ns) -> contentRepository.getTypesManager().getPrefixes().entrySet().stream()
+////                           .filter(e -> e.getValue().equals(ns)).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()),
+////                           namespaceURI);
+//     }
 
        @Override
        public CompletionStage<ContentSession> edit(Consumer<ContentSession> work) {
index 731c6d5624488d07ac04277471fe815c52489954..29541b9d854339e7423ca9d750957a2f7fdbb67e 100644 (file)
@@ -5,7 +5,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
@@ -43,6 +42,7 @@ import org.apache.xerces.xs.XSTerm;
 import org.apache.xerces.xs.XSTypeDefinition;
 import org.argeo.api.acr.CrAttributeType;
 import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.RuntimeNamespaceContext;
 import org.argeo.api.cms.CmsLog;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.SAXException;
@@ -51,7 +51,7 @@ import org.xml.sax.SAXParseException;
 /** Register content types. */
 class TypesManager {
        private final static CmsLog log = CmsLog.getLog(TypesManager.class);
-       private Map<String, String> prefixes = new TreeMap<>();
+//     private Map<String, String> prefixes = new TreeMap<>();
 
        // immutable factories
        private SchemaFactory schemaFactory;
@@ -86,20 +86,22 @@ class TypesManager {
                for (CmsContentTypes cs : CmsContentTypes.values()) {
                        StreamSource source = new StreamSource(cs.getResource().toExternalForm());
                        sources.add(source);
-                       if (prefixes.containsKey(cs.getDefaultPrefix()))
-                               throw new IllegalStateException("Prefix " + cs.getDefaultPrefix() + " is already mapped with "
-                                               + prefixes.get(cs.getDefaultPrefix()));
-                       prefixes.put(cs.getDefaultPrefix(), cs.getNamespace());
+//                     if (prefixes.containsKey(cs.getDefaultPrefix()))
+//                             throw new IllegalStateException("Prefix " + cs.getDefaultPrefix() + " is already mapped with "
+//                                             + prefixes.get(cs.getDefaultPrefix()));
+//                     prefixes.put(cs.getDefaultPrefix(), cs.getNamespace());
+                       RuntimeNamespaceContext.register(cs.getNamespace(), cs.getDefaultPrefix());
                }
 
                reload();
        }
 
        public synchronized void registerTypes(String defaultPrefix, String namespace, String xsdSystemId) {
-               if (prefixes.containsKey(defaultPrefix))
-                       throw new IllegalStateException(
-                                       "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
-               prefixes.put(defaultPrefix, namespace);
+//             if (prefixes.containsKey(defaultPrefix))
+//                     throw new IllegalStateException(
+//                                     "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix));
+//             prefixes.put(defaultPrefix, namespace);
+               RuntimeNamespaceContext.register(namespace, defaultPrefix);
 
                sources.add(new StreamSource(xsdSystemId));
                reload();
@@ -459,9 +461,9 @@ class TypesManager {
                }
        }
 
-       public Map<String, String> getPrefixes() {
-               return prefixes;
-       }
+//     public Map<String, String> getPrefixes() {
+//             return prefixes;
+//     }
 
        public List<Source> getSources() {
                return sources;
index dc9aefa800b7385f4e4848ba8ebaa628d821a944..eef7055df4fbd43048ac6696495c61bf077c661a 100644 (file)
@@ -32,7 +32,7 @@ class DirectoryContent extends AbstractContent {
        @Override
        public Iterator<Content> iterator() {
                List<Content> res = new ArrayList<>();
-               for (Iterator<HierarchyUnit> it = directory.getRootHierarchyUnits(false).iterator(); it.hasNext();) {
+               for (Iterator<HierarchyUnit> it = directory.getDirectHierarchyUnits(false).iterator(); it.hasNext();) {
                        res.add(new HierarchyUnitContent(getSession(), provider, it.next()));
                }
                return res.iterator();
index 5ec57c51f859297b8553c31b1852d79edc7d2838..78bc72f5d041912a68bfa87c741ed3c7c8acc382 100644 (file)
@@ -56,7 +56,7 @@ class HierarchyUnitContent extends AbstractContent {
        @Override
        public Iterator<Content> iterator() {
                List<Content> lst = new ArrayList<>();
-               for (HierarchyUnit hu : hierarchyUnit.getDirectHierachyUnits())
+               for (HierarchyUnit hu : hierarchyUnit.getDirectHierachyUnits(false))
                        lst.add(new HierarchyUnitContent(getSession(), provider, hu));
 
                for (Role role : hierarchyUnit.getHierarchyUnitRoles(null, false))
index 1efb93afdd36b9ad1432f4976d4351c48dfa2277..54824e140763b07787ab8b8fef83153ad98243f2 100644 (file)
@@ -1,12 +1,20 @@
 package org.argeo.cms.auth;
 
+import static org.argeo.api.cms.CmsConstants.ROLE_ADMIN;
+import static org.argeo.api.cms.CmsConstants.ROLE_ANONYMOUS;
+import static org.argeo.api.cms.CmsConstants.ROLE_USER;
+import static org.argeo.api.cms.CmsConstants.ROLE_USER_ADMIN;
+
 import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 import java.util.UUID;
 
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
+//import javax.naming.InvalidNameException;
+//import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 import javax.security.auth.x500.X500Principal;
 
@@ -18,7 +26,6 @@ import org.argeo.cms.internal.auth.CmsSessionImpl;
 import org.argeo.cms.internal.auth.ImpliedByPrincipal;
 import org.argeo.cms.internal.http.WebCmsSessionImpl;
 import org.argeo.cms.internal.runtime.CmsContextImpl;
-import org.argeo.cms.security.NodeSecurityUtils;
 import org.argeo.osgi.useradmin.AuthenticatingUser;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.useradmin.Authorization;
@@ -41,6 +48,9 @@ class CmsAuthUtils {
 
        final static String SINGLE_USER_LOCAL_ID = "single-user";
 
+       private final static List<String> RESERVED_ROLES = Collections
+                       .unmodifiableList(Arrays.asList(new String[] { ROLE_ADMIN, ROLE_ANONYMOUS, ROLE_USER, ROLE_USER_ADMIN }));
+
        static void addAuthorization(Subject subject, Authorization authorization) {
                assert subject != null;
                checkSubjectEmpty(subject);
@@ -52,46 +62,46 @@ class CmsAuthUtils {
                boolean singleUser = authorization instanceof SingleUserAuthorization;
 
                Set<Principal> principals = subject.getPrincipals();
-               try {
-                       String authName = authorization.getName();
-
-                       // determine user's principal
-                       final LdapName name;
-                       final Principal userPrincipal;
-                       if (authName == null) {
-                               name = NodeSecurityUtils.ROLE_ANONYMOUS_NAME;
-                               userPrincipal = new AnonymousPrincipal();
-                               principals.add(userPrincipal);
-                       } else {
-                               name = new LdapName(authName);
-                               NodeSecurityUtils.checkUserName(name);
-                               userPrincipal = new X500Principal(name.toString());
-                               principals.add(userPrincipal);
-
-                               if (singleUser) {
-                                       principals.add(new ImpliedByPrincipal(NodeSecurityUtils.ROLE_ADMIN_NAME, userPrincipal));
-                                       principals.add(new DataAdminPrincipal());
-                               }
+//             try {
+               String authName = authorization.getName();
+
+               // determine user's principal
+//                     final LdapName name;
+               final Principal userPrincipal;
+               if (authName == null) {
+//                             name = NodeSecurityUtils.ROLE_ANONYMOUS_NAME;
+                       userPrincipal = new AnonymousPrincipal();
+                       principals.add(userPrincipal);
+               } else {
+//                             name = new LdapName(authName);
+                       checkUserName(authName);
+                       userPrincipal = new X500Principal(authName.toString());
+                       principals.add(userPrincipal);
+
+                       if (singleUser) {
+                               principals.add(new ImpliedByPrincipal(CmsConstants.ROLE_ADMIN, userPrincipal));
+                               principals.add(new DataAdminPrincipal());
                        }
+               }
 
-                       // Add roles provided by authorization
-                       for (String role : authorization.getRoles()) {
-                               LdapName roleName = new LdapName(role);
-                               if (roleName.equals(name)) {
-                                       // skip
-                               } else if (roleName.equals(NodeSecurityUtils.ROLE_ANONYMOUS_NAME)) {
-                                       // skip
-                               } else {
-                                       NodeSecurityUtils.checkImpliedPrincipalName(roleName);
-                                       principals.add(new ImpliedByPrincipal(roleName.toString(), userPrincipal));
-                                       if (roleName.equals(NodeSecurityUtils.ROLE_ADMIN_NAME))
-                                               principals.add(new DataAdminPrincipal());
-                               }
+               // Add roles provided by authorization
+               for (String role : authorization.getRoles()) {
+//                             LdapName roleName = new LdapName(role);
+                       if (role.equals(authName)) {
+                               // skip
+                       } else if (role.equals(CmsConstants.ROLE_ANONYMOUS)) {
+                               // skip
+                       } else {
+//                                     NodeSecurityUtils.checkImpliedPrincipalName(role);
+                               principals.add(new ImpliedByPrincipal(role, userPrincipal));
+                               if (role.equals(CmsConstants.ROLE_ADMIN))
+                                       principals.add(new DataAdminPrincipal());
                        }
-
-               } catch (InvalidNameException e) {
-                       throw new IllegalArgumentException("Cannot commit", e);
                }
+
+//             } catch (InvalidNameException e) {
+//                     throw new IllegalArgumentException("Cannot commit", e);
+//             }
        }
 
        private static void checkSubjectEmpty(Subject subject) {
@@ -225,6 +235,11 @@ class CmsAuthUtils {
                return principals.iterator().next();
        }
 
+       private static void checkUserName(String name) throws IllegalArgumentException {
+               if (RESERVED_ROLES.contains(name))
+                       throw new IllegalArgumentException(name + " is a reserved name");
+       }
+
        private CmsAuthUtils() {
 
        }
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsRole.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsRole.java
new file mode 100644 (file)
index 0000000..af64508
--- /dev/null
@@ -0,0 +1,28 @@
+package org.argeo.cms.auth;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.CrName;
+
+public enum CmsRole implements SystemRole {
+       userAdmin, //
+       groupAdmin;
+
+       private final static String QUALIFIER = "cms.";
+
+       private final ContentName name;
+
+       CmsRole() {
+               name = new ContentName(CrName.ROLE_NAMESPACE_URI, QUALIFIER + name());
+       }
+
+       public QName getName() {
+               return name;
+       }
+
+       @Override
+       public String toString() {
+               return name.toPrefixedString();
+       }
+}
index faf5555d0a61ef31ee06c7a79412e516ad7d67dd..16ac638c107a943422e84a059b0a79414f68e54f 100644 (file)
@@ -71,6 +71,11 @@ public final class CurrentUser {
                return roles.contains(role);
        }
 
+       /** Implies this {@link SystemRole} in this context. */
+       public final static boolean implies(SystemRole role, String context) {
+               return role.implied(currentSubject(), context);
+       }
+
        /** Executes as the current user */
        public final static <T> T doAs(PrivilegedAction<T> action) {
                return Subject.doAs(currentSubject(), action);
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java
new file mode 100644 (file)
index 0000000..bf91f39
--- /dev/null
@@ -0,0 +1,25 @@
+package org.argeo.cms.auth;
+
+public class RoleNameUtils {
+
+       /*
+        * 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 final static String getParent(String dn) {
+               int index = dn.indexOf(',');
+               return dn.substring(index + 1);
+       }
+
+       /** Up two levels. */
+       public final static String getContext(String dn) {
+               return getParent(getParent(dn));
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java b/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java
new file mode 100644 (file)
index 0000000..9c686a6
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.cms.auth;
+
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.xml.namespace.QName;
+
+import org.argeo.cms.internal.auth.ImpliedByPrincipal;
+
+public interface SystemRole {
+       QName getName();
+
+       default boolean implied(Subject subject, String context) {
+               Set<ImpliedByPrincipal> roles = subject.getPrincipals(ImpliedByPrincipal.class);
+               for (ImpliedByPrincipal role : roles) {
+                       if (role.isSystemRole()) {
+                               if (role.getRoleName().equals(getName())) {
+                                       if (role.getContext().equalsIgnoreCase(context))
+                                               return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+}
index 164d319f197ae92074b2c5965233deb6f7c1be5c..2e074e7781e57c5438272a6fc11221164512994f 100644 (file)
@@ -17,18 +17,16 @@ import java.util.UUID;
 import java.util.function.Consumer;
 
 import javax.crypto.SecretKey;
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.x500.X500Principal;
 
 import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsConstants;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsSession;
 import org.argeo.cms.internal.runtime.CmsContextImpl;
-import org.argeo.cms.security.NodeSecurityUtils;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.useradmin.Authorization;
 
@@ -43,7 +41,8 @@ public class CmsSessionImpl implements CmsSession, Serializable {
        private final UUID uuid;
        private final String localSessionId;
        private Authorization authorization;
-       private final LdapName userDn;
+//     private final LdapName userDn;
+       private final String userDn;
        private final boolean anonymous;
 
        private final ZonedDateTime creationTime;
@@ -73,15 +72,11 @@ public class CmsSessionImpl implements CmsSession, Serializable {
                // this.initialSubject = initialSubject;
                this.localSessionId = localSessionId;
                this.authorization = authorization;
-               if (authorization.getName() != null)
-                       try {
-                               this.userDn = new LdapName(authorization.getName());
-                               this.anonymous = false;
-                       } catch (InvalidNameException e) {
-                               throw new IllegalArgumentException("Invalid user name " + authorization.getName(), e);
-                       }
-               else {
-                       this.userDn = NodeSecurityUtils.ROLE_ANONYMOUS_NAME;
+               if (authorization.getName() != null) {
+                       this.userDn = authorization.getName();
+                       this.anonymous = false;
+               } else {
+                       this.userDn = CmsConstants.ROLE_ANONYMOUS;
                        this.anonymous = true;
                }
                this.uuid = uuid;
@@ -156,7 +151,7 @@ public class CmsSessionImpl implements CmsSession, Serializable {
        }
 
        @Override
-       public LdapName getUserDn() {
+       public String getUserDn() {
                return userDn;
        }
 
index 5d5477e9b953d567cea8d831af3dcf86dbc48494..e69cda64459ac05058058b2a96d9ab317da149ae 100644 (file)
@@ -232,7 +232,7 @@ public class CmsUserManagerImpl implements CmsUserManager {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getGlobalId();
+                       String baseDn = userDirectory.getContext();
 
                        if (onlyWritable && readOnly)
                                continue;
@@ -247,7 +247,7 @@ public class CmsUserManagerImpl implements CmsUserManager {
        }
 
        public Set<UserDirectory> getUserDirectories() {
-               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getGlobalId().compareTo(o2.getGlobalId()));
+               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getContext().compareTo(o2.getContext()));
                res.addAll(userDirectories.keySet());
                return res;
        }
@@ -445,8 +445,8 @@ public class CmsUserManagerImpl implements CmsUserManager {
                String name = user.getName();
                NavigableMap<String, UserDirectory> possible = new TreeMap<>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
-                       if (name.endsWith(userDirectory.getGlobalId())) {
-                               possible.put(userDirectory.getGlobalId(), userDirectory);
+                       if (name.endsWith(userDirectory.getContext())) {
+                               possible.put(userDirectory.getContext(), userDirectory);
                        }
                }
                if (possible.size() == 0)
index c753601296657c9e69d03499a7b931ad9bdf8ee4..73f474637750a2f61aa825cddbdc0d12861df99e 100644 (file)
@@ -1,17 +1,17 @@
 package org.argeo.cms.internal.auth;
 
+import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext;
+
 import java.security.Principal;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Set;
 
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
+import javax.xml.namespace.QName;
 
+import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.cms.auth.RoleNameUtils;
 import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
 
 /**
  * A {@link Principal} which has been implied by an {@link Authorization}. If it
@@ -21,67 +21,88 @@ import org.osgi.service.useradmin.Role;
  * identity is removed, the related {@link ImpliedByPrincipal}s can thus be
  * removed.
  */
-public final class ImpliedByPrincipal implements Principal, Role {
-       private final LdapName name;
+public final class ImpliedByPrincipal implements Principal {
+       private final String name;
        private Set<Principal> causes = new HashSet<Principal>();
 
-       private int type = Role.ROLE;
+       private QName roleName;
+//     private int type = Role.ROLE;
+
+       private boolean systemRole = false;
+       private String context;
 
        public ImpliedByPrincipal(String name, Principal userPrincipal) {
-               try {
-                       this.name = new LdapName(name);
-               } catch (InvalidNameException e) {
-                       throw new IllegalArgumentException("Badly formatted role name", e);
+               this.name = name;
+               String cn = RoleNameUtils.getLastRdnValue(name);
+               roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn);
+               if (roleName.getNamespaceURI().equals(CrName.ROLE_NAMESPACE_URI)) {
+                       systemRole = true;
                }
+               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 ImpliedByPrincipal(LdapName name, Principal userPrincipal) {
+//             this.name = name;
+//             if (userPrincipal != null)
+//                     causes.add(userPrincipal);
+//     }
 
        public String getName() {
-               return name.toString();
-       }
-
-       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);
+               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();
+//     }
 
-       @Override
-       /** Type of {@link Role}, if known. */
-       public int getType() {
-               return type;
+       /*
+        * OBJECT
+        */
+
+       public QName getRoleName() {
+               return roleName;
        }
 
-       @Override
-       /** Not supported for the time being. */
-       public Dictionary<String, Object> getProperties() {
-               throw new UnsupportedOperationException();
+       public String getContext() {
+               return context;
        }
 
-       /*
-        * OBJECT
-        */
+       public boolean isSystemRole() {
+               return systemRole;
+       }
 
        @Override
        public int hashCode() {
index 49e55f16c5f0b6ac2e44aa9bcc122a1ace2f451c..890e283914d9d2027c5e4964a07239c101b46ff9 100644 (file)
@@ -107,14 +107,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                } else {
                        throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
                }
-               String basePath = userDirectory.getGlobalId();
+               String basePath = userDirectory.getContext();
 
                addUserDirectory(userDirectory);
                if (isSystemRolesBaseDn(basePath)) {
                        addStandardSystemRoles();
                }
                if (log.isDebugEnabled()) {
-                       log.debug("User directory " + userDirectory.getGlobalId() + (u != null ? " [" + u.getScheme() + "]" : "")
+                       log.debug("User directory " + userDirectory.getContext() + (u != null ? " [" + u.getScheme() + "]" : "")
                                        + " enabled." + (realm != null ? " " + realm + " realm." : ""));
                }
                return userDirectory;
diff --git a/org.argeo.cms/src/org/argeo/cms/security/NodeSecurityUtils.java b/org.argeo.cms/src/org/argeo/cms/security/NodeSecurityUtils.java
deleted file mode 100644 (file)
index fb53940..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.cms.security;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
-import org.argeo.api.cms.CmsConstants;
-
-public class NodeSecurityUtils {
-       public final static LdapName ROLE_ADMIN_NAME, ROLE_DATA_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME,
-                       ROLE_USER_ADMIN_NAME;
-       public final static List<LdapName> RESERVED_ROLES;
-       static {
-               try {
-                       ROLE_ADMIN_NAME = new LdapName(CmsConstants.ROLE_ADMIN);
-                       ROLE_DATA_ADMIN_NAME = new LdapName(CmsConstants.ROLE_DATA_ADMIN);
-                       ROLE_USER_NAME = new LdapName(CmsConstants.ROLE_USER);
-                       ROLE_USER_ADMIN_NAME = new LdapName(CmsConstants.ROLE_USER_ADMIN);
-                       ROLE_ANONYMOUS_NAME = new LdapName(CmsConstants.ROLE_ANONYMOUS);
-                       RESERVED_ROLES = Collections.unmodifiableList(Arrays.asList(
-                                       new LdapName[] { ROLE_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME, ROLE_USER_ADMIN_NAME }));
-               } catch (InvalidNameException e) {
-                       throw new Error("Cannot initialize login module class", e);
-               }
-       }
-
-       public static void checkUserName(LdapName name) throws IllegalArgumentException {
-               if (RESERVED_ROLES.contains(name))
-                       throw new IllegalArgumentException(name + " is a reserved name");
-       }
-
-       public static void checkImpliedPrincipalName(LdapName roleName) throws IllegalArgumentException {
-//             if (ROLE_USER_NAME.equals(roleName) || ROLE_ANONYMOUS_NAME.equals(roleName))
-//                     throw new IllegalArgumentException(roleName + " cannot be listed as role");
-       }
-
-}
index f2e1d26066603ff30560f2bef6198f6e88cac865..e13f56289ce8b44b2f9629d2e297aadccbf03c03 100644 (file)
@@ -50,8 +50,8 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        private final Hashtable<String, Object> properties;
        private final LdapName baseDn;
        // private final LdapName userBaseDn, groupBaseDn;
-       private final Rdn userBaseRdn, groupBaseRdn;
-       private final String userObjectClass, userBase, groupObjectClass, groupBase;
+       private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn;
+       private final String userObjectClass, groupObjectClass;
 
        private final boolean readOnly;
        private final boolean disabled;
@@ -97,15 +97,17 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                forcedPassword = UserAdminConf.forcedPassword.getValue(properties);
 
                userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
-               userBase = UserAdminConf.userBase.getValue(properties);
+               String userBase = UserAdminConf.userBase.getValue(properties);
                groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
-               groupBase = UserAdminConf.groupBase.getValue(properties);
+               String groupBase = UserAdminConf.groupBase.getValue(properties);
+               String systemRoleBase = UserAdminConf.systemRoleBase.getValue(properties);
                try {
                        baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties));
                        userBaseRdn = new Rdn(userBase);
 //                     userBaseDn = new LdapName(userBase + "," + baseDn);
                        groupBaseRdn = new Rdn(groupBase);
 //                     groupBaseDn = new LdapName(groupBase + "," + baseDn);
+                       systemRoleBaseRdn = new Rdn(systemRoleBase);
                } catch (InvalidNameException e) {
                        throw new IllegalArgumentException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties),
                                        e);
@@ -151,7 +153,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
         */
 
        @Override
-       public String getGlobalId() {
+       public String getContext() {
                return getBaseDn().toString();
        }
 
@@ -190,20 +192,21 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                try {
                        LdapName name = (LdapName) getBaseDn().clone();
                        String[] segments = path.split("/");
-                       String parentSegment = null;
+                       Rdn parentRdn = null;
                        for (String segment : segments) {
-                               String attr = "ou";
-                               if (parentSegment != null) {
-                                       if (getUserBase().equals(parentSegment))
-                                               attr = "uid";
-                                       else if (getGroupBase().equals(parentSegment))
-                                               attr = "cn";
+                               // TODO make attr names configurable ?
+                               String attr = LdapAttrs.ou.name();
+                               if (parentRdn != null) {
+                                       if (getUserBaseRdn().equals(parentRdn))
+                                               attr = LdapAttrs.uid.name();
+                                       else if (getGroupBaseRdn().equals(parentRdn))
+                                               attr = LdapAttrs.cn.name();
+                                       else if (getSystemRoleBaseRdn().equals(parentRdn))
+                                               attr = LdapAttrs.cn.name();
                                }
                                Rdn rdn = new Rdn(attr, segment);
                                name.add(rdn);
-
-                               // TODO make it more robust using RDNs
-                               parentSegment = rdn.toString();
+                               parentRdn = rdn;
                        }
                        return name;
                } catch (InvalidNameException e) {
@@ -534,10 +537,6 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                throw new UnsupportedOperationException();
        }
 
-       void isFunctionalHierarchyUnit(HierarchyUnit hu) {
-
-       }
-
 //     @Override
 //     public List<? extends Role> getHierarchyUnitRoles(String filter, boolean deep) {
 //             try {
@@ -548,7 +547,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
 //     }
 
        @Override
-       public Iterable<HierarchyUnit> getRootHierarchyUnits(boolean functionalOnly) {
+       public Iterable<HierarchyUnit> getDirectHierarchyUnits(boolean functionalOnly) {
                throw new UnsupportedOperationException();
        }
 
@@ -561,6 +560,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        }
 
        protected DirectoryGroup newGroup(LdapName name, Attributes attrs) {
+               if (LdapNameUtils.getParentRdn(name).equals(getSystemRoleBaseRdn()))
+                       return new LdifGroup.LdifSystemPermissions(this, name, attrs);
+               
                if (hasObjectClass(attrs, LdapObjs.organization))
                        return new LdifGroup.LdifOrganization(this, name, attrs);
                else
@@ -638,7 +640,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
 
        protected int roleType(LdapName dn) {
                Rdn technicalRdn = LdapNameUtils.getParentRdn(dn);
-               if (groupBaseRdn.equals(technicalRdn))
+               if (getGroupBaseRdn().equals(technicalRdn) || getSystemRoleBaseRdn().equals(technicalRdn))
                        return Role.GROUP;
                else if (userBaseRdn.equals(technicalRdn))
                        return Role.USER;
@@ -652,11 +654,6 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                return userObjectClass;
        }
 
-       @Deprecated
-       String getUserBase() {
-               return userBase;
-       }
-
        Rdn getUserBaseRdn() {
                return userBaseRdn;
        }
@@ -669,15 +666,14 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                return groupObjectClass;
        }
 
-       @Deprecated
-       String getGroupBase() {
-               return groupBase;
-       }
-
        Rdn getGroupBaseRdn() {
                return groupBaseRdn;
        }
 
+       Rdn getSystemRoleBaseRdn() {
+               return systemRoleBaseRdn;
+       }
+
        LdapName getBaseDn() {
                return (LdapName) baseDn.clone();
        }
index 54a6d9e315c8b2a9a99ae017e4099c25f8b8ae2f..ca1fa338bab58e785dbda260e8431f908c1bd811 100644 (file)
@@ -160,7 +160,7 @@ public class AggregatingUserAdmin implements UserAdmin {
                if (!(ud instanceof AbstractUserDirectory))
                        throw new IllegalArgumentException("Only " + AbstractUserDirectory.class.getName() + " is supported");
                AbstractUserDirectory userDirectory = (AbstractUserDirectory) ud;
-               String basePath = userDirectory.getGlobalId();
+               String basePath = userDirectory.getContext();
                if (isSystemRolesBaseDn(basePath)) {
                        this.systemRoles = userDirectory;
                        systemRoles.setExternalRoles(this);
@@ -272,7 +272,7 @@ public class AggregatingUserAdmin implements UserAdmin {
        }
 
        public Set<UserDirectory> getUserDirectories() {
-               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getGlobalId().compareTo(o2.getGlobalId()));
+               TreeSet<UserDirectory> res = new TreeSet<>((o1, o2) -> o1.getContext().compareTo(o2.getContext()));
                res.addAll(businessRoles.values());
                return res;
        }
index ba76617d768bfef3b469732ba8b93a7d6de2ec41..2c21342e3858247e19618d2b6ac09fad2ed847c1 100644 (file)
@@ -10,13 +10,11 @@ public interface HierarchyUnit {
 
        HierarchyUnit getParent();
 
-       Iterable<HierarchyUnit> getDirectHierachyUnits();
-
-       Iterable<HierarchyUnit> getFunctionalHierachyUnits();
+       Iterable<HierarchyUnit> getDirectHierachyUnits(boolean functionalOnly);
 
        boolean isFunctional();
 
-       String getBasePath();
+       String getContext();
 
        List<? extends Role> getHierarchyUnitRoles(String filter, boolean deep);
 
index a9bc9417f5d2e436ec8734b2969723c2e8191ad0..076f26949c49b0ca6f20dfc9cef8dc06093c9818 100644 (file)
@@ -20,6 +20,7 @@ import org.argeo.util.naming.LdapAttrs;
 public class IpaUtils {
        public final static String IPA_USER_BASE = "cn=users,cn=accounts";
        public final static String IPA_GROUP_BASE = "cn=groups,cn=accounts";
+       public final static String IPA_ROLE_BASE = "cn=roles,cn=accounts";
        public final static String IPA_SERVICE_BASE = "cn=services,cn=accounts";
 
        private final static String KRB_PRINCIPAL_NAME = LdapAttrs.krbPrincipalName.name().toLowerCase();
@@ -37,6 +38,7 @@ public class IpaUtils {
                properties.put(UserAdminConf.realm.name(), realm);
                properties.put(UserAdminConf.userBase.name(), IPA_USER_BASE);
                properties.put(UserAdminConf.groupBase.name(), IPA_GROUP_BASE);
+               properties.put(UserAdminConf.systemRoleBase.name(), IPA_ROLE_BASE);
                properties.put(UserAdminConf.readOnly.name(), Boolean.TRUE.toString());
        }
 
index b4813e685b5b457ab65bdf6ffc488187f999479a..d4259c87d5714a4d829fecd03c390b0af843ca9f 100644 (file)
@@ -1,6 +1,7 @@
 package org.argeo.osgi.useradmin;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -31,7 +32,8 @@ class LdifHierarchyUnit implements HierarchyUnit {
                this.attributes = attributes;
 
                Rdn rdn = LdapNameUtils.getLastRdn(dn);
-               functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn));
+               functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn)
+                               || directory.getSystemRoleBaseRdn().equals(rdn));
        }
 
        @Override
@@ -40,18 +42,16 @@ class LdifHierarchyUnit implements HierarchyUnit {
        }
 
        @Override
-       public Iterable<HierarchyUnit> getDirectHierachyUnits() {
-               return children;
-       }
-
-       @Override
-       public Iterable<HierarchyUnit> getFunctionalHierachyUnits() {
+       public Iterable<HierarchyUnit> getDirectHierachyUnits(boolean functionalOnly) {
                List<HierarchyUnit> res = new ArrayList<>();
-               for (HierarchyUnit hu : children) {
-                       if (hu.isFunctional())
-                               res.add(hu);
-               }
-               return res;
+               if (functionalOnly)
+                       for (HierarchyUnit hu : children) {
+                               if (hu.isFunctional())
+                                       res.add(hu);
+                       }
+               else
+                       res.addAll(children);
+               return Collections.unmodifiableList(res);
        }
 
        @Override
@@ -71,7 +71,7 @@ class LdifHierarchyUnit implements HierarchyUnit {
        }
 
        @Override
-       public String getBasePath() {
+       public String getContext() {
                return dn.toString();
        }
 
index 224be18703b583c018422de11b6ea2276af5c816..0a925e4eb41b3612f531f3ace8534a198065d72d 100644 (file)
@@ -329,7 +329,7 @@ public class LdifUserAdmin extends AbstractUserDirectory {
        }
 
        @Override
-       public Iterable<HierarchyUnit> getRootHierarchyUnits(boolean functionalOnly) {
+       public Iterable<HierarchyUnit> getDirectHierarchyUnits(boolean functionalOnly) {
                if (functionalOnly) {
                        List<HierarchyUnit> res = new ArrayList<>();
                        for (HierarchyUnit hu : rootHierarchyUnits) {
index 329da91499165170ebb043dcf53cf938b6f20192..b0a52626aa4cda31602e18b37ae8208804600d2e 100644 (file)
@@ -23,7 +23,7 @@ public class OsUserDirectory extends AbstractUserDirectory {
        public OsUserDirectory(URI uriArg, Dictionary<String, ?> props) {
                super(uriArg, props, false);
                try {
-                       osUserDn = new LdapName(LdapAttrs.uid.name() + "=" + osUsername + "," + getUserBase() + "," + getBaseDn());
+                       osUserDn = new LdapName(LdapAttrs.uid.name() + "=" + osUsername + "," + getUserBaseRdn() + "," + getBaseDn());
                        Attributes attributes = new BasicAttributes();
                        attributes.put(LdapAttrs.uid.name(), osUsername);
                        osUser = newUser(osUserDn, attributes);
index 3631de40f45a9c26bed050b1f1f7445e73c7fc24..93ca94ca6deddead625a43acb0cfcbeee3813ba7 100644 (file)
@@ -34,6 +34,9 @@ public enum UserAdminConf {
        /** Relative base DN for users */
        groupBase("ou=Groups"),
 
+       /** Relative base DN for users */
+       systemRoleBase("ou=Roles"),
+
        /** Read-only source */
        readOnly(null),
 
index 727d9295da9e61fbb5d54e6b46e58327d31d4b25..18bb1a0a6eb6987e08c11c34633362f8aa2e7e57 100644 (file)
@@ -11,7 +11,7 @@ public interface UserDirectory {
         * The base of the hierarchy defined by this directory. This could typically be
         * an LDAP base DN.
         */
-       String getGlobalId();
+       String getContext();
        
        String getName();
 
@@ -35,7 +35,7 @@ public interface UserDirectory {
 
        Optional<String> getRealm();
 
-       Iterable<HierarchyUnit> getRootHierarchyUnits(boolean functionalOnly);
+       Iterable<HierarchyUnit> getDirectHierarchyUnits(boolean functionalOnly);
 
        HierarchyUnit getHierarchyUnit(String path);