Fix error message
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / util / directory / ldap / AbstractLdapDirectory.java
index 55449a70748f3a0f7b4bf079123adc70988b4b7b..74dd15edec4c658ca9df5af3a3446e7b0cb1cf4e 100644 (file)
@@ -10,6 +10,7 @@ import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Optional;
 import java.util.StringJoiner;
 
@@ -19,6 +20,7 @@ import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 import javax.transaction.xa.XAResource;
@@ -33,12 +35,13 @@ import org.argeo.util.transaction.WorkControl;
 import org.argeo.util.transaction.WorkingCopyXaResource;
 import org.argeo.util.transaction.XAResourceProvider;
 
+/** A {@link Directory} based either on LDAP or LDIF. */
 public abstract class AbstractLdapDirectory implements Directory, XAResourceProvider {
        protected static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name";
        protected static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password";
 
-       protected final LdapName baseDn;
-       protected final Hashtable<String, Object> configProperties;
+       private final LdapName baseDn;
+       private final Hashtable<String, Object> configProperties;
        private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn;
        private final String userObjectClass, groupObjectClass;
        private String memberAttributeId = "member";
@@ -65,7 +68,11 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                        String key = keys.nextElement();
                        configProperties.put(key, props.get(key));
                }
-               baseDn = toLdapName(DirectoryConf.baseDn.getValue(configProperties));
+
+               String baseDnStr = DirectoryConf.baseDn.getValue(configProperties);
+               if (baseDnStr == null)
+                       throw new IllegalArgumentException("Base DN must be specified: " + configProperties);
+               baseDn = toLdapName(baseDnStr);
                this.scoped = scoped;
 
                if (uriArg != null) {
@@ -113,40 +120,35 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                        disabled = Boolean.parseBoolean(disabledStr);
                else
                        disabled = false;
-
-               URI u = URI.create(uri);
-               if (!getRealm().isEmpty() || DirectoryConf.SCHEME_LDAP.equals(u.getScheme())
-                               || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) {
+               if (!getRealm().isEmpty()) {
+                       // IPA multiple LDAP causes URI parsing to fail
+                       // TODO manage generic redundant LDAP case
                        directoryDao = new LdapDao(this);
-               } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) {
-                       directoryDao = new LdifDao(this);
-               } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) {
-                       directoryDao = new OsUserDirectory(this);
-                       // singleUser = true;
                } else {
-                       throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
+                       if (uri != null) {
+                               URI u = URI.create(uri);
+                               if (DirectoryConf.SCHEME_LDAP.equals(u.getScheme())
+                                               || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) {
+                                       directoryDao = new LdapDao(this);
+                               } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) {
+                                       directoryDao = new LdifDao(this);
+                               } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) {
+                                       directoryDao = new OsUserDirectory(this);
+                                       // singleUser = true;
+                               } else {
+                                       throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
+                               }
+                       } else {
+                               // in memory
+                               directoryDao = new LdifDao(this);
+                       }
                }
-               xaResource = new WorkingCopyXaResource<>(directoryDao);
+               if (directoryDao != null)
+                       xaResource = new WorkingCopyXaResource<>(directoryDao);
        }
 
        /*
-        * ABSTRACT METHODS
-        */
-
-//     public abstract HierarchyUnit doGetHierarchyUnit(LdapName dn);
-//
-//     public abstract Iterable<HierarchyUnit> doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly);
-//
-//     protected abstract Boolean daoHasEntry(LdapName dn);
-//
-//     protected abstract LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException;
-//
-//     protected abstract List<LdapEntry> doGetEntries(LdapName searchBase, Filter f, boolean deep);
-//
-//     /** Returns the groups this user is a direct member of. */
-//     protected abstract List<LdapName> getDirectGroups(LdapName dn);
-       /*
-        * INITIALIZATION
+        * INITIALISATION
         */
 
        public void init() {
@@ -160,9 +162,9 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
        /*
         * CREATION
         */
-       protected abstract LdapEntry newUser(LdapName name, Attributes attrs);
+       protected abstract LdapEntry newUser(LdapName name);
 
-       protected abstract LdapEntry newGroup(LdapName name, Attributes attrs);
+       protected abstract LdapEntry newGroup(LdapName name);
 
        /*
         * EDITION
@@ -249,17 +251,26 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                                        Object value = values.next();
                                        LdapName groupDn = new LdapName(value.toString());
                                        LdapEntry group = doGetRole(groupDn);
-                                       if (group != null)
+                                       if (group != null) {
+                                               allRoles.add(group);
+                                       } else {
+                                               // user doesn't have the right to retrieve role, but we know it exists
+                                               // otherwise memberOf would not work
+                                               group = newGroup(groupDn);
                                                allRoles.add(group);
+                                       }
                                }
                        } catch (NamingException e) {
                                throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
                        }
                } else {
-                       for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
-                               // TODO check for loops
+                       directGroups: for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
                                LdapEntry group = doGetRole(groupDn);
                                if (group != null) {
+                                       if (allRoles.contains(group)) {
+                                               // important in order to avoi loops
+                                               continue directGroups;
+                                       }
                                        allRoles.add(group);
                                        collectGroups(group, allRoles);
                                }
@@ -286,6 +297,16 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                return getName();
        }
 
+       @Override
+       public String getHierarchyUnitLabel(Locale locale) {
+               String key = LdapNameUtils.getLastRdn(getBaseDn()).getType();
+               Object value = LdapEntry.getLocalized(asLdapEntry().getProperties(), key, locale);
+               if (value == null)
+                       value = getHierarchyUnitName();
+               assert value != null;
+               return value.toString();
+       }
+
        @Override
        public HierarchyUnit getParent() {
                return null;
@@ -301,12 +322,31 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                return this;
        }
 
+       @Override
+       public HierarchyUnit createHierarchyUnit(String path) {
+               checkEdit();
+               LdapEntryWorkingCopy wc = getWorkingCopy();
+               LdapName dn = pathToName(path);
+               if ((getDirectoryDao().entryExists(dn) && !wc.getDeletedData().containsKey(dn))
+                               || wc.getNewData().containsKey(dn))
+                       throw new IllegalArgumentException("Already a hierarchy unit " + path);
+               BasicAttributes attrs = new BasicAttributes(true);
+               attrs.put(LdapAttrs.objectClass.name(), LdapObjs.organizationalUnit.name());
+               Rdn nameRdn = dn.getRdn(dn.size() - 1);
+               // TODO deal with multiple attr RDN
+               attrs.put(nameRdn.getType(), nameRdn.getValue());
+               wc.getModifiedData().put(dn, attrs);
+               LdapHierarchyUnit newHierarchyUnit = new LdapHierarchyUnit(this, dn);
+               wc.getNewData().put(dn, newHierarchyUnit);
+               return newHierarchyUnit;
+       }
+
        /*
         * PATHS
         */
 
        @Override
-       public String getContext() {
+       public String getBase() {
                return getBaseDn().toString();
        }
 
@@ -338,7 +378,7 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                        for (int i = 0; i < segments.length; i++) {
                                String segment = segments[i];
                                // TODO make attr names configurable ?
-                               String attr = LdapAttrs.ou.name();
+                               String attr = path.startsWith("accounts/")/* IPA */ ? LdapAttrs.cn.name() : LdapAttrs.ou.name();
                                if (parentRdn != null) {
                                        if (getUserBaseRdn().equals(parentRdn))
                                                attr = LdapAttrs.uid.name();
@@ -353,7 +393,7 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                        }
                        return name;
                } catch (InvalidNameException e) {
-                       throw new IllegalStateException("Cannot get role " + path, e);
+                       throw new IllegalStateException("Cannot convert " + path + " to LDAP name", e);
                }
 
        }
@@ -361,6 +401,10 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
        /*
         * UTILITIES
         */
+       protected boolean isExternal(LdapName name) {
+               return !name.startsWith(baseDn);
+       }
+
        protected static boolean hasObjectClass(Attributes attrs, LdapObjs objectClass) {
                return hasObjectClass(attrs, objectClass.name());
        }