Multiple user referentials working with IPA.
authorMathieu <mbaudier@argeo.org>
Fri, 4 Nov 2022 11:40:11 +0000 (12:40 +0100)
committerMathieu <mbaudier@argeo.org>
Fri, 4 Nov 2022 11:40:11 +0000 (12:40 +0100)
org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java
org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java
org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectoryDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/DefaultLdapEntry.java
org.argeo.util/src/org/argeo/util/directory/ldap/IpaUtils.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java

index 83b2f170951b0b15951c93ee3e8e97a5d0d8c155..c9479d51cd40ad7703bd0b9db09a42fc73951fe1 100644 (file)
@@ -118,15 +118,19 @@ public class AggregatingUserAdmin implements UserAdmin {
                }
 
                // gather roles from other referentials
-               List<String> allRoles = new ArrayList<>(Arrays.asList(rawAuthorization.getRoles()));
+               List<String> rawRoles = Arrays.asList(rawAuthorization.getRoles());
+               List<String> 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) {
index 3903a23f434212c6e5de3aac0c2c8f8fe1f3fa56..fbcff484c7938a77cd781968b3e8529e2532f1f1 100644 (file)
@@ -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;
index 74dd15edec4c658ca9df5af3a3446e7b0cb1cf4e..5cd4ac1a57e30da57efb844e1358a15b8efd79d8 100644 (file)
@@ -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<String, ?> props, boolean scoped) {
                this.configProperties = new Hashtable<String, Object>();
                for (Enumeration<String> 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;
        }
index 8e132887dec377ac8ebab4cb14f8d380ccd7b5d7..e6d242fe8f1fbc42d38de9385e7f4f7a33403309 100644 (file)
@@ -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() {
index 6b6154dcc0094792d19a2d3290574b29ddbfd829..c01d1c3c1a3e5b898f34b4b0cc48bf9a0b532034 100644 (file)
@@ -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<String> 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);
        }
 
index 68b40868ab9dda0a2b7c223ce23f514d6e4cb10c..99ad6dbe89214da160dc9857f9d7de5a33d07b15 100644 (file)
@@ -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 {
index 0f6e324ad258411a5da7f314e30283e8e1c31ffa..9157f23a4d8ad3033d91bd79bc6db2f24daddf4f 100644 (file)
@@ -213,10 +213,11 @@ public class LdapDao extends AbstractLdapDirectoryDao {
        public Iterable<HierarchyUnit> doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) {
                List<HierarchyUnit> 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);
index f317800110db4c9e04808f95cbc396a392502d7e..c70d8c54f08a32d0e2ebcc8f98115289fdd630a0 100644 (file)
@@ -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<LdapEntryWorkingCopy> {
        boolean checkConnection();