X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.util%2Fsrc%2Forg%2Fargeo%2Futil%2Fdirectory%2Fldap%2FAbstractLdapDirectory.java;h=28d8d081ccdcfb875e5ce8941086f01cc3be3c00;hb=138e686fbf65683c3c94a52f1cfbaf8e02362e19;hp=eab82e0ec3a0ba2ea2426b01d62a6d7ab20f4c56;hpb=0ce8ecfe974cec9f524c16884209cd08544d890d;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java index eab82e0ec..28d8d081c 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java @@ -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 configProperties; + private final LdapName baseDn; + private final Hashtable 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 doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly); -// -// protected abstract Boolean daoHasEntry(LdapName dn); -// -// protected abstract LdapEntry daoGetEntry(LdapName key) throws NameNotFoundException; -// -// protected abstract List doGetEntries(LdapName searchBase, Filter f, boolean deep); -// -// /** Returns the groups this user is a direct member of. */ -// protected abstract List 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); } @@ -281,12 +292,61 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv return directoryDao.doGetDirectHierarchyUnits(baseDn, functionalOnly); } + @Override + public String getHierarchyUnitName() { + 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; + } + + @Override + public boolean isFunctional() { + return true; + } + + @Override + public Directory getDirectory() { + 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(); } @@ -314,9 +374,11 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv LdapName name = (LdapName) getBaseDn().clone(); String[] segments = path.split("/"); Rdn parentRdn = null; - for (String segment : segments) { + // segments[0] is the directory itself + 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(); @@ -339,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()); }