From: Mathieu Baudier Date: Wed, 29 Jun 2022 06:55:11 +0000 (+0200) Subject: Support roles from multiple directories X-Git-Tag: v2.3.10~150 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=4d675e1c871cfd8e4481e03cd496ab975b8b367d;p=lgpl%2Fargeo-commons.git Support roles from multiple directories --- diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java index 54824e140..e8a0dc2b7 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java @@ -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); diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java index 19875e88a..05c5cf442 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java @@ -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 diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java index 2e074e778..e78567b07 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java @@ -116,10 +116,10 @@ public class CmsSessionImpl implements CmsSession, Serializable { return Subject.getSubject(accessControlContext); } - public Set getSecretKeys() { - checkValid(); - return getSubject().getPrivateCredentials(SecretKey.class); - } +// public Set getSecretKeys() { +// checkValid(); +// return getSubject().getPrivateCredentials(SecretKey.class); +// } @Override public boolean isValid() { 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 79d2bd3cb..c1727f746 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -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 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()) { 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 6f3bd1a68..ac076167e 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserAdmin.java @@ -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 getAllRoles(DirectoryUser user) { List allRoles = new ArrayList(); if (user != null) { - collectRoles(user, allRoles); + collectRoles((LdapEntry) user, allRoles); allRoles.add(user); } else collectAnonymousRoles(allRoles); return allRoles; } - private void collectRoles(DirectoryUser user, List allRoles) { + private void collectRoles(LdapEntry user, List allRoles) { List 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 allRoles = new ArrayList(); + 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) 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(); 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 9e70e84ea..71a87887b 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 @@ -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 configProperties; + private final LdapName baseDn; + private final Hashtable 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()); }