Support roles from multiple directories
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 29 Jun 2022 06:55:11 +0000 (08:55 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 29 Jun 2022 06:55:11 +0000 (08:55 +0200)
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.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/util/directory/ldap/AbstractLdapDirectory.java

index 54824e140763b07787ab8b8fef83153ad98243f2..e8a0dc2b72f82d01f3b68da95ea7d3ab44c43b28 100644 (file)
@@ -160,8 +160,10 @@ class CmsAuthUtils {
                                        } else {
                                                // keep current session
                                                cmsSession = currentLocalSession;
-                                               // keyring
-                                               subject.getPrivateCredentials().addAll(cmsSession.getSecretKeys());
+                                               // credentials
+                                               // TODO control it more??
+                                               subject.getPrivateCredentials().addAll(cmsSession.getSubject().getPrivateCredentials());
+                                               subject.getPublicCredentials().addAll(cmsSession.getSubject().getPublicCredentials());
                                        }
                                } else {// anonymous
                                        if (!currentLocalSessionAnonymous) {
@@ -187,9 +189,9 @@ class CmsAuthUtils {
                                subject.getPrivateCredentials().add(nodeSessionId);
                        } else {
                                UUID storedSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next().getUuid();
-                               // if (storedSessionId.equals(httpSessionId.getValue()))
-                               throw new IllegalStateException(
-                                               "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
+                               if (!storedSessionId.equals(nodeSessionId.getUuid()))
+                                       throw new IllegalStateException(
+                                                       "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
                        }
                } else {
                        CmsSessionImpl cmsSession = CmsContextImpl.getCmsContext().getCmsSessionByLocalId(SINGLE_USER_LOCAL_ID);
index 19875e88ae4b9a83b5cd636e595e3a3e44137a99..05c5cf4422de810d817e3c919b4cfd66ee96b9de 100644 (file)
@@ -18,7 +18,6 @@ import org.argeo.api.cms.CmsConstants;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.cms.internal.auth.CmsSessionImpl;
 import org.argeo.cms.internal.runtime.CmsContextImpl;
-import org.argeo.cms.internal.runtime.KernelUtils;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.useradmin.Authorization;
 
@@ -53,17 +52,17 @@ public class RemoteSessionLoginModule implements LoginModule {
        public boolean login() throws LoginException {
                if (callbackHandler == null)
                        return false;
-               RemoteAuthCallback httpCallback = new RemoteAuthCallback();
+               RemoteAuthCallback remoteAuthCallback = new RemoteAuthCallback();
                try {
-                       callbackHandler.handle(new Callback[] { httpCallback });
+                       callbackHandler.handle(new Callback[] { remoteAuthCallback });
                } catch (IOException e) {
                        throw new LoginException("Cannot handle http callback: " + e.getMessage());
                } catch (UnsupportedCallbackException e) {
                        return false;
                }
-               request = httpCallback.getRequest();
+               request = remoteAuthCallback.getRequest();
                if (request == null) {
-                       RemoteAuthSession httpSession = httpCallback.getHttpSession();
+                       RemoteAuthSession httpSession = remoteAuthCallback.getHttpSession();
                        if (httpSession == null)
                                return false;
                        // TODO factorize with below
index 2e074e7781e57c5438272a6fc11221164512994f..e78567b07df03ebcbfd44d286f5f8c424ea36615 100644 (file)
@@ -116,10 +116,10 @@ public class CmsSessionImpl implements CmsSession, Serializable {
                return Subject.getSubject(accessControlContext);
        }
 
-       public Set<SecretKey> getSecretKeys() {
-               checkValid();
-               return getSubject().getPrivateCredentials(SecretKey.class);
-       }
+//     public Set<SecretKey> getSecretKeys() {
+//             checkValid();
+//             return getSubject().getPrivateCredentials(SecretKey.class);
+//     }
 
        @Override
        public boolean isValid() {
index 79d2bd3cbc2f21142f1801a1df5c5fbc307c2095..c1727f7465d2e4fbdde2bdeea3671cc51da90fc0 100644 (file)
@@ -93,6 +93,7 @@ public class AggregatingUserAdmin implements UserAdmin {
                }
                DirectoryUserAdmin userReferentialOfThisUser = findUserAdmin(user.getName());
                Authorization rawAuthorization = userReferentialOfThisUser.getAuthorization(user);
+               User retrievedUser = (User) userReferentialOfThisUser.getRole(user.getName());
                String usernameToUse;
                String displayNameToUse;
                if (user instanceof Group) {
@@ -113,6 +114,17 @@ public class AggregatingUserAdmin implements UserAdmin {
                }
 
                // gather roles from other referentials
+               List<String> allRoles = new ArrayList<>(Arrays.asList(rawAuthorization.getRoles()));
+               for (LdapName otherBaseDn : businessRoles.keySet()) {
+                       if (otherBaseDn.equals(userReferentialOfThisUser.getBaseDn()))
+                               continue;
+                       DirectoryUserAdmin otherUserAdmin = businessRoles.get(otherBaseDn);
+                       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;
@@ -136,7 +148,7 @@ public class AggregatingUserAdmin implements UserAdmin {
                        }
                        addAbstractSystemRoles(rawAuthorization, sysRoles);
                        Authorization authorization = new AggregatingAuthorization(usernameToUse, displayNameToUse, sysRoles,
-                                       rawAuthorization.getRoles());
+                                       allRoles.toArray(new String[allRoles.size()]));
                        return authorization;
                } finally {
                        if (userAdminToUse != null && userAdminToUse.isScoped()) {
index 6f3bd1a6865695cbdbc48729c10a7fda745fc156..ac076167e02bb2983a266db60716eb942b5b467d 100644 (file)
@@ -8,9 +8,10 @@ import static org.argeo.util.naming.LdapObjs.person;
 import static org.argeo.util.naming.LdapObjs.top;
 
 import java.net.URI;
-import java.nio.channels.UnsupportedAddressTypeException;
+import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 
@@ -21,7 +22,11 @@ import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.BasicAttributes;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosTicket;
 
+import org.argeo.util.CurrentSubject;
 import org.argeo.util.directory.DirectoryConf;
 import org.argeo.util.directory.DirectoryDigestUtils;
 import org.argeo.util.directory.HierarchyUnit;
@@ -146,16 +151,16 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
        protected List<Role> getAllRoles(DirectoryUser user) {
                List<Role> allRoles = new ArrayList<Role>();
                if (user != null) {
-                       collectRoles(user, allRoles);
+                       collectRoles((LdapEntry) user, allRoles);
                        allRoles.add(user);
                } else
                        collectAnonymousRoles(allRoles);
                return allRoles;
        }
 
-       private void collectRoles(DirectoryUser user, List<Role> allRoles) {
+       private void collectRoles(LdapEntry user, List<Role> allRoles) {
                List<LdapEntry> allEntries = new ArrayList<>();
-               LdapEntry entry = (LdapEntry) user;
+               LdapEntry entry = user;
                collectGroups(entry, allEntries);
                for (LdapEntry e : allEntries) {
                        if (e instanceof Role)
@@ -275,24 +280,52 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
 
        @Override
        public Authorization getAuthorization(User user) {
-               if (user == null || user instanceof DirectoryUser) {
-                       return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
+               if (user == null) {// anonymous
+                       return new LdifAuthorization(user, getAllRoles(null));
+               }
+               LdapName userName = toLdapName(user.getName());
+               if (isExternal(userName) && user instanceof LdapEntry) {
+                       List<Role> allRoles = new ArrayList<Role>();
+                       collectRoles((LdapEntry) user, allRoles);
+                       return new LdifAuthorization(user, allRoles);
                } else {
-                       // bind
-                       DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user);
-                       try {
-                               DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
-                               if (directoryUser == null)
-                                       throw new IllegalStateException("No scoped user found for " + user);
-                               LdifAuthorization authorization = new LdifAuthorization(directoryUser,
-                                               scopedUserAdmin.getAllRoles(directoryUser));
-                               return authorization;
-                       } finally {
-                               scopedUserAdmin.destroy();
+
+                       Subject currentSubject = CurrentSubject.current();
+                       if (currentSubject != null //
+                                       && !currentSubject.getPrivateCredentials(Authorization.class).isEmpty() //
+                                       && !currentSubject.getPrivateCredentials(KerberosTicket.class).isEmpty()) {
+                               // 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<>())));
+                               return getAuthorizationFromScoped(scopedUserAdmin, user);
+                       }
+
+                       if (user instanceof DirectoryUser) {
+                               return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
+                       } else {
+                               // bind with authenticating user
+                               DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user);
+                               return getAuthorizationFromScoped(scopedUserAdmin, user);
                        }
                }
        }
 
+       private Authorization getAuthorizationFromScoped(DirectoryUserAdmin scopedUserAdmin, User user) {
+               try {
+                       DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
+                       if (directoryUser == null)
+                               throw new IllegalStateException("No scoped user found for " + user);
+                       LdifAuthorization authorization = new LdifAuthorization(directoryUser,
+                                       scopedUserAdmin.getAllRoles(directoryUser));
+                       return authorization;
+               } finally {
+                       scopedUserAdmin.destroy();
+               }
+       }
+
        @Override
        public Role createRole(String name, int type) {
                checkEdit();
index 9e70e84ea34de2a96e7d928b8a35dec5bcf866f4..71a87887b85c7fd066d0230b341a312315caa1c7 100644 (file)
@@ -38,8 +38,8 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv
        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";
@@ -375,6 +375,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());
        }