From 3c1cdc594d954520b14646102b366290bdad58c7 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Fri, 4 Nov 2022 12:40:11 +0100 Subject: [PATCH] Multiple user referentials working with IPA. --- .../osgi/useradmin/AggregatingUserAdmin.java | 12 ++++-- .../osgi/useradmin/DirectoryUserAdmin.java | 1 - .../directory/ldap/AbstractLdapDirectory.java | 14 ++++++- .../ldap/AbstractLdapDirectoryDao.java | 3 +- .../util/directory/ldap/DefaultLdapEntry.java | 39 ++++++++++++++----- .../argeo/util/directory/ldap/IpaUtils.java | 24 ++++-------- .../argeo/util/directory/ldap/LdapDao.java | 7 ++-- .../util/directory/ldap/LdapDirectoryDao.java | 1 + 8 files changed, 65 insertions(+), 36 deletions(-) diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index 83b2f1709..c9479d51c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -118,15 +118,19 @@ public class AggregatingUserAdmin implements UserAdmin { } // gather roles from other referentials - List allRoles = new ArrayList<>(Arrays.asList(rawAuthorization.getRoles())); + List rawRoles = Arrays.asList(rawAuthorization.getRoles()); + List allRoles = new ArrayList<>(rawRoles); for (LdapName otherBaseDn : businessRoles.keySet()) { if (otherBaseDn.equals(userReferentialOfThisUser.getBaseDn())) continue; DirectoryUserAdmin otherUserAdmin = userAdminToUse(user, businessRoles.get(otherBaseDn)); if (otherUserAdmin == null) continue; - Authorization auth = otherUserAdmin.getAuthorization(retrievedUser); - allRoles.addAll(Arrays.asList(auth.getRoles())); + for (String roleStr : rawRoles) { + User role = (User) findUserAdmin(roleStr).getRole(roleStr); + Authorization auth = otherUserAdmin.getAuthorization(role); + allRoles.addAll(Arrays.asList(auth.getRoles())); + } } @@ -159,6 +163,8 @@ public class AggregatingUserAdmin implements UserAdmin { /** Decide whether to scope or not */ private DirectoryUserAdmin userAdminToUse(User user, DirectoryUserAdmin userAdmin) { + if (userAdmin.isAuthenticated()) + return userAdmin; if (user instanceof DirectoryUser) { return userAdmin; } else if (user instanceof AuthenticatingUser) { diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java index 3903a23f4..fbcff484c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java @@ -8,7 +8,6 @@ import static org.argeo.util.naming.LdapObjs.person; import static org.argeo.util.naming.LdapObjs.top; import java.net.URI; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; 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 74dd15ede..5cd4ac1a5 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 @@ -14,6 +14,7 @@ import java.util.Locale; import java.util.Optional; import java.util.StringJoiner; +import javax.naming.Context; import javax.naming.InvalidNameException; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; @@ -62,6 +63,9 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv private LdapDirectoryDao directoryDao; + /** Whether the the directory has is authenticated via a service user. */ + private boolean authenticated = false; + public AbstractLdapDirectory(URI uriArg, Dictionary props, boolean scoped) { this.configProperties = new Hashtable(); for (Enumeration keys = props.keys(); keys.hasMoreElements();) { @@ -130,10 +134,13 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv if (DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { directoryDao = new LdapDao(this); + authenticated = configProperties.get(Context.SECURITY_PRINCIPAL) != null; } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { directoryDao = new LdifDao(this); + authenticated = true; } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { directoryDao = new OsUserDirectory(this); + authenticated = true; // singleUser = true; } else { throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); @@ -378,7 +385,8 @@ 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 = path.startsWith("accounts/")/* IPA */ ? LdapAttrs.cn.name() : LdapAttrs.ou.name(); + String attr = getDirectory().getRealm().isPresent()/* IPA */ ? LdapAttrs.cn.name() + : LdapAttrs.ou.name(); if (parentRdn != null) { if (getUserBaseRdn().equals(parentRdn)) attr = LdapAttrs.uid.name(); @@ -490,6 +498,10 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv return disabled; } + public boolean isAuthenticated() { + return authenticated; + } + public Rdn getUserBaseRdn() { return userBaseRdn; } diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java index 8e132887d..e6d242fe8 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java @@ -1,15 +1,14 @@ package org.argeo.util.directory.ldap; -import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; +/** Base class for LDAP/LDIF directory DAOs. */ public abstract class AbstractLdapDirectoryDao implements LdapDirectoryDao { private AbstractLdapDirectory directory; public AbstractLdapDirectoryDao(AbstractLdapDirectory directory) { this.directory = directory; - } public AbstractLdapDirectory getDirectory() { diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java b/org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java index 6b6154dcc..c01d1c3c1 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java @@ -32,20 +32,44 @@ public class DefaultLdapEntry implements LdapEntry { private final LdapName dn; -// private Attributes publishedAttributes; - - // Temporarily expose the fields private AttributeDictionary properties; private AttributeDictionary credentials; +// private String primaryObjectClass; +// private List objectClasses = new ArrayList<>(); + protected DefaultLdapEntry(AbstractLdapDirectory directory, LdapName dn) { Objects.requireNonNull(directory); Objects.requireNonNull(dn); this.directory = directory; this.dn = dn; -// this.publishedAttributes = attributes; -// properties = new AttributeDictionary(false); -// credentials = new AttributeDictionary(true); + + // Object classes +// Objects.requireNonNull(initialAttributes); +// try { +// NamingEnumeration en = initialAttributes.get(LdapAttrs.objectClass.name()).getAll(); +// String first = null; +// attrs: while (en.hasMore()) { +// String v = en.next().toString(); +// if (v.equalsIgnoreCase(LdapObjs.top.name())) +// continue attrs; +// if (first == null) +// first = v; +// if (v.equalsIgnoreCase(getDirectory().getUserObjectClass())) +// primaryObjectClass = getDirectory().getUserObjectClass(); +// else if (v.equalsIgnoreCase(getDirectory().getGroupObjectClass())) +// primaryObjectClass = getDirectory().getGroupObjectClass(); +// objectClasses.add(v); +// } +// if (primaryObjectClass == null) { +// if (first == null) +// throw new IllegalStateException("Could not find primary object class"); +// primaryObjectClass = first; +// } +// } catch (NamingException e) { +// throw new IllegalStateException("Cannot find object classes", e); +// } + } @Override @@ -54,9 +78,6 @@ public class DefaultLdapEntry implements LdapEntry { } public synchronized Attributes getAttributes() { -// // lazy loading -// if (publishedAttributes == null) -// publishedAttributes = getDirectory().getDirectoryDao().doGetAttributes(dn); return isEditing() ? getModifiedAttributes() : getDirectory().getDirectoryDao().doGetAttributes(dn); } diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java b/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java index 68b40868a..99ad6dbe8 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java @@ -8,10 +8,10 @@ import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; +import java.util.StringJoiner; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.LdapAttrs; @@ -22,17 +22,9 @@ public class IpaUtils { public final static String IPA_USER_BASE = "cn=users"; public final static String IPA_GROUP_BASE = "cn=groups"; public final static String IPA_ROLE_BASE = "cn=roles"; - public final static String IPA_SERVICE_BASE = "cn=services,cn=accounts"; + public final static String IPA_SERVICE_BASE = "cn=services"; - public final static Rdn IPA_ACCOUNTS_RDN; - static { - try { - IPA_ACCOUNTS_RDN = new Rdn(LdapAttrs.cn.name(), "accounts"); - } catch (InvalidNameException e) { - // should not happen - throw new IllegalStateException(e); - } - } + public final static String IPA_ACCOUNTS_BASE = "cn=accounts"; private final static String KRB_PRINCIPAL_NAME = LdapAttrs.krbPrincipalName.name().toLowerCase(); @@ -56,14 +48,12 @@ public class IpaUtils { public static String domainToBaseDn(String domain) { String[] dcs = domain.split("\\."); - StringBuilder sb = new StringBuilder(); + StringJoiner sj = new StringJoiner(","); for (int i = 0; i < dcs.length; i++) { - if (i != 0) - sb.append(','); String dc = dcs[i]; - sb.append(LdapAttrs.dc.name()).append('=').append(dc.toLowerCase()); + sj.add(LdapAttrs.dc.name() + '=' + dc.toLowerCase()); } - return sb.toString(); + return IPA_ACCOUNTS_BASE + ',' + sj.toString(); } public static LdapName kerberosToDn(String kerberosName) { @@ -72,7 +62,7 @@ public class IpaUtils { String baseDn = domainToBaseDn(kname[1]); String dn; if (!username.contains("/")) - dn = LdapAttrs.uid + "=" + username + "," + IPA_USER_BASE + "," + IPA_ACCOUNTS_RDN + "," + baseDn; + dn = LdapAttrs.uid + "=" + username + "," + IPA_USER_BASE + "," + baseDn; else dn = KRB_PRINCIPAL_NAME + "=" + kerberosName + "," + IPA_SERVICE_BASE + "," + baseDn; try { diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java index 0f6e324ad..9157f23a4 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java @@ -213,10 +213,11 @@ public class LdapDao extends AbstractLdapDirectoryDao { public Iterable doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) { List res = new ArrayList<>(); try { + String structuralFilter = functionalOnly ? "" + : "(" + getDirectory().getUserBaseRdn() + ")(" + getDirectory().getGroupBaseRdn() + ")(" + + getDirectory().getSystemRoleBaseRdn() + ")"; String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass - + "=" + LdapObjs.organization.name() + "))"; -// String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass -// + "=" + LdapObjs.organization.name() + ")(cn=accounts)(cn=users)(cn=groups))"; + + "=" + LdapObjs.organization.name() + ")" + structuralFilter + ")"; SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE); diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java index f31780011..c70d8c54f 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java @@ -9,6 +9,7 @@ import javax.naming.ldap.LdapName; import org.argeo.util.directory.HierarchyUnit; import org.argeo.util.transaction.WorkingCopyProcessor; +/** Low-level access to an LDAP/LDIF directory. */ public interface LdapDirectoryDao extends WorkingCopyProcessor { boolean checkConnection(); -- 2.30.2