Multi-referentials bind working
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 22 Sep 2022 06:51:25 +0000 (08:51 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 22 Sep 2022 06:51:25 +0000 (08:51 +0200)
org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java
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/osgi/useradmin/LdifAuthorization.java
org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java
org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdapDirectoryDao.java
org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java

index dd65755382ce727cc935374b87fdff52bace1841..0ae84ff8ac7f294b4b891ac5d77c7281eb10e162 100644 (file)
@@ -31,8 +31,6 @@ import org.argeo.osgi.useradmin.AuthenticatingUser;
 import org.argeo.osgi.useradmin.TokenUtils;
 import org.argeo.util.directory.ldap.IpaUtils;
 import org.argeo.util.naming.LdapAttrs;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
 import org.osgi.service.useradmin.Authorization;
 import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.User;
@@ -53,7 +51,7 @@ public class UserAdminLoginModule implements LoginModule {
                        LdapAttrs.uid.name(), LdapAttrs.employeeNumber.name(), LdapAttrs.authPassword.name() });
 
        // private state
-       private BundleContext bc;
+//     private BundleContext bc;
        private User authenticatedUser = null;
        private Locale locale;
 
@@ -67,7 +65,7 @@ public class UserAdminLoginModule implements LoginModule {
                        Map<String, ?> options) {
                this.subject = subject;
                try {
-                       bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext();
+//                     bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext();
                        this.callbackHandler = callbackHandler;
                        this.sharedState = (Map<String, Object>) sharedState;
                } catch (Exception e) {
index 0d4830663d04d5a298a2cca00b0f6e0b179fa0a9..e3eb442492055e7cbcc02b641ef8f44b2301355c 100644 (file)
@@ -76,6 +76,8 @@ public class UserAdminUtils {
                String dName = getProperty(user, LdapAttrs.displayName.name());
                if (isEmpty(dName))
                        dName = getProperty(user, LdapAttrs.cn.name());
+               if (isEmpty(dName))
+                       dName = getProperty(user, LdapAttrs.uid.name());
                if (isEmpty(dName))
                        dName = getUserLocalId(user.getName());
                return dName;
index 179099bad124ebc7cd4c5c049f7723a50177650f..83b2f170951b0b15951c93ee3e8e97a5d0d8c155 100644 (file)
@@ -9,6 +9,7 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -88,6 +89,7 @@ public class AggregatingUserAdmin implements UserAdmin {
                return res.size() == 1 ? res.get(0) : null;
        }
 
+       /** Builds an authorisation by scanning all referentials. */
        @Override
        public Authorization getAuthorization(User user) {
                if (user == null) {// anonymous
@@ -120,21 +122,17 @@ public class AggregatingUserAdmin implements UserAdmin {
                for (LdapName otherBaseDn : businessRoles.keySet()) {
                        if (otherBaseDn.equals(userReferentialOfThisUser.getBaseDn()))
                                continue;
-                       DirectoryUserAdmin otherUserAdmin = businessRoles.get(otherBaseDn);
+                       DirectoryUserAdmin otherUserAdmin = userAdminToUse(user, businessRoles.get(otherBaseDn));
+                       if (otherUserAdmin == null)
+                               continue;
                        Authorization auth = otherUserAdmin.getAuthorization(retrievedUser);
                        allRoles.addAll(Arrays.asList(auth.getRoles()));
 
                }
 
                // integrate system roles
-               final DirectoryUserAdmin userAdminToUse;// possibly scoped when authenticating
-               if (user instanceof DirectoryUser) {
-                       userAdminToUse = userReferentialOfThisUser;
-               } else if (user instanceof AuthenticatingUser) {
-                       userAdminToUse = (DirectoryUserAdmin) userReferentialOfThisUser.scope(user);
-               } else {
-                       throw new IllegalArgumentException("Unsupported user type " + user.getClass());
-               }
+               final DirectoryUserAdmin userAdminToUse = userAdminToUse(retrievedUser, userReferentialOfThisUser);
+               Objects.requireNonNull(userAdminToUse);
 
                try {
                        Set<String> sysRoles = new HashSet<String>();
@@ -159,6 +157,18 @@ public class AggregatingUserAdmin implements UserAdmin {
                }
        }
 
+       /** Decide whether to scope or not */
+       private DirectoryUserAdmin userAdminToUse(User user, DirectoryUserAdmin userAdmin) {
+               if (user instanceof DirectoryUser) {
+                       return userAdmin;
+               } else if (user instanceof AuthenticatingUser) {
+                       return userAdmin.scope(user).orElse(null);
+               } else {
+                       throw new IllegalArgumentException("Unsupported user type " + user.getClass());
+               }
+
+       }
+
        /**
         * Enrich with application-specific roles which are strictly programmatic, such
         * as anonymous/user semantics.
index 1ed3d7ccbf0f9d218012314d7fc8004d544e622d..3903a23f434212c6e5de3aac0c2c8f8fe1f3fa56 100644 (file)
@@ -14,6 +14,7 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 
 import javax.naming.Context;
 import javax.naming.InvalidNameException;
@@ -47,12 +48,8 @@ import org.osgi.service.useradmin.UserAdmin;
 public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdmin, UserDirectory {
 
        private UserAdmin externalRoles;
-       // private List<String> indexedUserProperties = Arrays
-       // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(),
-       // LdapAttrs.cn.name() });
 
        // Transaction
-//     private TransactionManager transactionManager;
        public DirectoryUserAdmin(URI uriArg, Dictionary<String, ?> props) {
                this(uriArg, props, false);
        }
@@ -69,7 +66,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
         * ABSTRACT METHODS
         */
 
-       protected AbstractLdapDirectory scope(User user) {
+       protected Optional<DirectoryUserAdmin> scope(User user) {
                if (getDirectoryDao() instanceof LdapDao) {
                        return scopeLdap(user);
                } else if (getDirectoryDao() instanceof LdifDao) {
@@ -79,7 +76,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                }
        }
 
-       protected DirectoryUserAdmin scopeLdap(User user) {
+       protected Optional<DirectoryUserAdmin> scopeLdap(User user) {
                Dictionary<String, Object> credentials = user.getCredentials();
                String username = (String) credentials.get(SHARED_STATE_USERNAME);
                if (username == null)
@@ -96,10 +93,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                }
                DirectoryUserAdmin scopedDirectory = new DirectoryUserAdmin(null, properties, true);
                scopedDirectory.init();
-               return scopedDirectory;
+               // check connection
+               if (!scopedDirectory.getDirectoryDao().checkConnection())
+                       return Optional.empty();
+               return Optional.of(scopedDirectory);
        }
 
-       protected DirectoryUserAdmin scopeLdif(User user) {
+       protected Optional<DirectoryUserAdmin> scopeLdif(User user) {
                Dictionary<String, Object> credentials = user.getCredentials();
                String username = (String) credentials.get(SHARED_STATE_USERNAME);
                if (username == null)
@@ -117,12 +117,11 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                Dictionary<String, Object> properties = cloneConfigProperties();
                properties.put(DirectoryConf.readOnly.name(), "true");
                DirectoryUserAdmin scopedUserAdmin = new DirectoryUserAdmin(null, properties, true);
-//             scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups);
-//             scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users);
                // FIXME do it better
                ((LdifDao) getDirectoryDao()).scope((LdifDao) scopedUserAdmin.getDirectoryDao());
+               // no need to check authentication
                scopedUserAdmin.init();
-               return scopedUserAdmin;
+               return Optional.of(scopedUserAdmin);
        }
 
        @Override
@@ -166,33 +165,6 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        if (e instanceof Role)
                                allRoles.add((Role) e);
                }
-//             Attributes attrs = user.getAttributes();
-//             // TODO centralize attribute name
-//             Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
-//             // if user belongs to this directory, we only check memberOf
-//             if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
-//                     try {
-//                             NamingEnumeration<?> values = memberOf.getAll();
-//                             while (values.hasMore()) {
-//                                     Object value = values.next();
-//                                     LdapName groupDn = new LdapName(value.toString());
-//                                     DirectoryUser group = doGetRole(groupDn);
-//                                     if (group != null)
-//                                             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
-//                             DirectoryUser group = doGetRole(groupDn);
-//                             if (group != null) {
-//                                     allRoles.add(group);
-//                                     collectRoles(group, allRoles);
-//                             }
-//                     }
-//             }
        }
 
        private void collectAnonymousRoles(List<Role> allRoles) {
@@ -234,21 +206,6 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        // no need to check modified users,
                        // since doGetRoles was already based on the modified attributes
                }
-
-               // if non deep we also search users and groups
-//             if (!deep) {
-//                     try {
-//                             if (!(searchBase.endsWith(new LdapName(getUserBase()))
-//                                             || searchBase.endsWith(new LdapName(getGroupBase())))) {
-//                                     LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase());
-//                                     res.addAll(getRoles(usersBase, filter, false));
-//                                     LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase());
-//                                     res.addAll(getRoles(groupsBase, filter, false));
-//                             }
-//                     } catch (InvalidNameException e) {
-//                             throw new IllegalStateException("Cannot search users and groups", e);
-//                     }
-//             }
                return res;
        }
 
@@ -299,9 +256,9 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                                // TODO not only Kerberos but also bind scope with kept password ?
                                Authorization auth = currentSubject.getPrivateCredentials(Authorization.class).iterator().next();
                                // bind with authenticating user
-                               DirectoryUserAdmin scopedUserAdmin = Subject.doAs(currentSubject,
-                                               (PrivilegedAction<DirectoryUserAdmin>) () -> (DirectoryUserAdmin) scope(
-                                                               new AuthenticatingUser(auth.getName(), new Hashtable<>())));
+                               DirectoryUserAdmin scopedUserAdmin = CurrentSubject.callAs(currentSubject, () -> {
+                                       return scope(new AuthenticatingUser(auth.getName(), new Hashtable<>())).orElseThrow();
+                               });
                                return getAuthorizationFromScoped(scopedUserAdmin, user);
                        }
 
@@ -309,7 +266,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                                return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
                        } else {
                                // bind with authenticating user
-                               DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user);
+                               DirectoryUserAdmin scopedUserAdmin = scope(user).orElseThrow();
                                return getAuthorizationFromScoped(scopedUserAdmin, user);
                        }
                }
@@ -384,22 +341,6 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
        @Override
        public boolean removeRole(String name) {
                return removeEntry(LdapNameUtils.toLdapName(name));
-//             checkEdit();
-//             LdapEntryWorkingCopy wc = getWorkingCopy();
-//             LdapName dn = toLdapName(name);
-//             boolean actuallyDeleted;
-//             if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) {
-//                     DirectoryUser user = (DirectoryUser) getRole(name);
-//                     wc.getDeletedData().put(dn, user);
-//                     actuallyDeleted = true;
-//             } else {// just removing from groups (e.g. system roles)
-//                     actuallyDeleted = false;
-//             }
-//             for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) {
-//                     LdapEntry group = doGetRole(groupDn);
-//                     group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
-//             }
-//             return actuallyDeleted;
        }
 
        /*
@@ -447,10 +388,6 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                this.externalRoles = externalRoles;
        }
 
-//     public void setTransactionManager(TransactionManager transactionManager) {
-//             this.transactionManager = transactionManager;
-//     }
-
        /*
         * STATIC UTILITIES
         */
index 80a9eea30e32ff19eeaae7aa2c94f1895f81da52..d7f6ad960b7a50854d864068755dc48f99db71e6 100644 (file)
@@ -71,11 +71,11 @@ class LdifAuthorization implements Authorization {
 
        final static String extractDisplayName(User user) {
                Dictionary<String, Object> props = user.getProperties();
-               Object displayName = props.get(LdapAttrs.displayName);
+               Object displayName = props.get(LdapAttrs.displayName.name());
                if (displayName == null)
-                       displayName = props.get(LdapAttrs.cn);
+                       displayName = props.get(LdapAttrs.cn.name());
                if (displayName == null)
-                       displayName = props.get(LdapAttrs.uid);
+                       displayName = props.get(LdapAttrs.uid.name());
                if (displayName == null)
                        displayName = user.getName();
                if (displayName == null)
index e1ad5edf5372a7ce5eb17996f6e7faffa4de194c..5d7e97ddeea487975ebd64609f63167a0720656d 100644 (file)
@@ -40,10 +40,15 @@ public class OsUserDirectory extends AbstractLdapDirectoryDao {
        }
 
        @Override
-       public Boolean entryExists(LdapName dn) {
+       public boolean entryExists(LdapName dn) {
                return osUserDn.equals(dn);
        }
 
+       @Override
+       public boolean checkConnection() {
+               return true;
+       }
+
        @Override
        public LdapEntry doGetEntry(LdapName key) throws NameNotFoundException {
                if (osUserDn.equals(key))
@@ -85,13 +90,13 @@ public class OsUserDirectory extends AbstractLdapDirectoryDao {
        @Override
        public void init() {
                // TODO Auto-generated method stub
-               
+
        }
 
        @Override
        public void destroy() {
                // TODO Auto-generated method stub
-               
+
        }
 
        @Override
@@ -102,5 +107,5 @@ public class OsUserDirectory extends AbstractLdapDirectoryDao {
                        throw new IllegalStateException(name + " doe not exist in " + getDirectory().getBaseDn(), e);
                }
        }
-       
+
 }
index 54d9776b5fd15106bf6de8b560ef81f863f1d470..04398bb4bff0cd74881e8da7e3a5ce1d632d46a0 100644 (file)
@@ -255,10 +255,6 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
                                        } else {
                                                // user doesn't have the right to retrieve role, but we know it exists
                                                // otherwise memberOf would not work
-//                                             Attributes a = new BasicAttributes();
-//                                             a.put(LdapNameUtils.getLastRdn(groupDn).getType(),
-//                                                             LdapNameUtils.getLastRdn(groupDn).getValue());
-//                                             a.put(LdapAttrs.objectClass.name(), LdapObjs.groupOfNames.name());
                                                group = newGroup(groupDn);
                                                allRoles.add(group);
                                        }
index e5ce0a4c16fafd7428ed69111026e5568e5fdf36..0f6e324ad258411a5da7f314e30283e8e1c31ffa 100644 (file)
@@ -27,10 +27,6 @@ import org.argeo.util.naming.LdapObjs;
 public class LdapDao extends AbstractLdapDirectoryDao {
        private LdapConnection ldapConnection;
 
-//     public LdapUserAdmin(Dictionary<String, ?> properties) {
-//             this(properties, false);
-//     }
-
        public LdapDao(AbstractLdapDirectory directory) {
                super(directory);
        }
@@ -44,31 +40,17 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                ldapConnection.destroy();
        }
 
-//     @Override
-//     protected AbstractUserDirectory scope(User user) {
-//             Dictionary<String, Object> credentials = user.getCredentials();
-//             String username = (String) credentials.get(SHARED_STATE_USERNAME);
-//             if (username == null)
-//                     username = user.getName();
-//             Dictionary<String, Object> properties = cloneProperties();
-//             properties.put(Context.SECURITY_PRINCIPAL, username.toString());
-//             Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
-//             byte[] pwd = (byte[]) pwdCred;
-//             if (pwd != null) {
-//                     char[] password = DirectoryDigestUtils.bytesToChars(pwd);
-//                     properties.put(Context.SECURITY_CREDENTIALS, new String(password));
-//             } else {
-//                     properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
-//             }
-//             return new LdapUserAdmin(properties, true);
-//     }
-
-//     protected InitialLdapContext getLdapContext() {
-//             return initialLdapContext;
-//     }
+       @Override
+       public boolean checkConnection() {
+               try {
+                       return ldapConnection.entryExists(getDirectory().getBaseDn());
+               } catch (NamingException e) {
+                       return false;
+               }
+       }
 
        @Override
-       public Boolean entryExists(LdapName dn) {
+       public boolean entryExists(LdapName dn) {
                try {
                        return ldapConnection.entryExists(dn);
                } catch (NameNotFoundException e) {
@@ -119,18 +101,6 @@ public class LdapDao extends AbstractLdapDirectoryDao {
                }
        }
 
-//     protected boolean isGroup(LdapName dn) {
-//             Rdn technicalRdn = LdapNameUtils.getParentRdn(dn);
-//             if (getDirectory().getGroupBaseRdn().equals(technicalRdn)
-//                             || getDirectory().getSystemRoleBaseRdn().equals(technicalRdn))
-//                     return true;
-//             else if (getDirectory().getUserBaseRdn().equals(technicalRdn))
-//                     return false;
-//             else
-//                     throw new IllegalArgumentException(
-//                                     "Cannot find role type, " + technicalRdn + " is not a technical RDN for " + dn);
-//     }
-
        @Override
        public Attributes doGetAttributes(LdapName name) {
                try {
index a4e65998c5df1e9eb10b370aaae71066467ec6f0..f317800110db4c9e04808f95cbc396a392502d7e 100644 (file)
@@ -10,7 +10,9 @@ import org.argeo.util.directory.HierarchyUnit;
 import org.argeo.util.transaction.WorkingCopyProcessor;
 
 public interface LdapDirectoryDao extends WorkingCopyProcessor<LdapEntryWorkingCopy> {
-       Boolean entryExists(LdapName dn);
+       boolean checkConnection();
+
+       boolean entryExists(LdapName dn);
 
        LdapEntry doGetEntry(LdapName name) throws NameNotFoundException;
 
index 27a934377b48b0277fecef7046174b7ea3e0c972..c200faa27123e6a473beed244a2332dfc5589dab 100644 (file)
@@ -160,7 +160,12 @@ public class LdifDao extends AbstractLdapDirectoryDao {
        }
 
        @Override
-       public Boolean entryExists(LdapName dn) {
+       public boolean checkConnection() {
+               return true;
+       }
+
+       @Override
+       public boolean entryExists(LdapName dn) {
                return entries.containsKey(dn);// || groups.containsKey(dn);
        }