X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Fauth%2FUserAdminLoginModule.java;h=53161e58c5533103ad136fbd5cd829cca147d5fa;hb=e75cf778a87f1b2ef7cfc57339ccbf9657282e92;hp=83accceb4d6f1047a6a4eb9ba11b120a2f40db13;hpb=4ccae1bf1714f7adbb69a4505f424e70f2c39698;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index 83accceb4..53161e58c 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -1,12 +1,10 @@ package org.argeo.cms.auth; -import static org.argeo.naming.LdapAttrs.cn; -import static org.argeo.naming.LdapAttrs.description; +import static org.argeo.api.acr.ldap.LdapAttr.cn; import java.io.IOException; +import java.security.Principal; import java.security.PrivilegedAction; -import java.security.cert.X509Certificate; -import java.time.Instant; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -14,7 +12,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; @@ -27,45 +24,41 @@ import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.CredentialNotFoundException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.internal.kernel.Activator; -import org.argeo.naming.LdapAttrs; -import org.argeo.naming.NamingUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.CryptoKeyring; -import org.argeo.osgi.useradmin.AuthenticatingUser; -import org.argeo.osgi.useradmin.IpaUtils; -import org.argeo.osgi.useradmin.OsUserUtils; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; + +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.directory.ldap.IpaUtils; +import org.argeo.cms.internal.runtime.CmsContextImpl; +import org.argeo.cms.osgi.useradmin.AuthenticatingUser; +import org.argeo.cms.osgi.useradmin.TokenUtils; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.Group; import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; +/** + * Use the {@link UserAdmin} in the OSGi registry as the basis for + * authentication. + */ public class UserAdminLoginModule implements LoginModule { - private final static Log log = LogFactory.getLog(UserAdminLoginModule.class); + private final static CmsLog log = CmsLog.getLog(UserAdminLoginModule.class); private Subject subject; private CallbackHandler callbackHandler; private Map sharedState = null; - private List indexedUserProperties = Arrays - .asList(new String[] { LdapAttrs.mail.name(), LdapAttrs.uid.name(), LdapAttrs.authPassword.name() }); + private List indexedUserProperties = Arrays.asList(new String[] { LdapAttr.mail.name(), LdapAttr.uid.name(), + LdapAttr.employeeNumber.name(), LdapAttr.authPassword.name() }); // private state - private BundleContext bc; +// private BundleContext bc; private User authenticatedUser = null; private Locale locale; private Authorization bindAuthorization = null; - private boolean singleUser = Activator.isSingleUser(); +// private boolean singleUser = Activator.isSingleUser(); @SuppressWarnings("unchecked") @Override @@ -73,42 +66,48 @@ public class UserAdminLoginModule implements LoginModule { Map options) { this.subject = subject; try { - bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext(); +// bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext(); this.callbackHandler = callbackHandler; this.sharedState = (Map) sharedState; } catch (Exception e) { - throw new CmsException("Cannot initialize login module", e); + throw new IllegalStateException("Cannot initialize login module", e); } } @Override public boolean login() throws LoginException { - UserAdmin userAdmin = Activator.getUserAdmin(); + UserAdmin userAdmin = CmsContextImpl.getCmsContext().getUserAdmin(); final String username; final char[] password; - X509Certificate[] certificateChain = null; + Object certificateChain = null; + boolean preauth = false; if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_PWD)) { // NB: required by Basic http auth username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD); // // TODO locale? + } else if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) + && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_SPNEGO_TOKEN)) { + // SPNEGO login has succeeded, that's enough for us at this stage + return true; } else if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN)) { - String certificateName = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); - LdapName ldapName; - try { - ldapName = new LdapName(certificateName); - } catch (InvalidNameException e) { - e.printStackTrace(); - return false; - } - username = ldapName.getRdn(ldapName.size()-1).getValue().toString(); - certificateChain = (X509Certificate[]) sharedState.get(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN); + String certDn = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); + username = certDn; + certificateChain = sharedState.get(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN); + password = null; + } else if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) + && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_REMOTE_ADDR) + && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_REMOTE_PORT)) {// ident + username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); password = null; - } else if (singleUser) { - username = OsUserUtils.getOsUsername(); + preauth = true; + } else if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_OS_USERNAME)) { + // single user, we assume Kerberos or other mean for commit + username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_OS_USERNAME); password = null; + preauth = true; } else { // ask for username and password @@ -162,20 +161,24 @@ public class UserAdminLoginModule implements LoginModule { return true;// expect Kerberos if (password != null) { + // TODO disabling bind for the time being, + // as it requires authorisations to be set at LDAP level + boolean tryBind = false; // try bind first - try { - AuthenticatingUser authenticatingUser = new AuthenticatingUser(user.getName(), password); - bindAuthorization = userAdmin.getAuthorization(authenticatingUser); - // TODO check tokens as well - if (bindAuthorization != null) { - authenticatedUser = user; - return true; + if (tryBind) + try { + AuthenticatingUser authenticatingUser = new AuthenticatingUser(user.getName(), password); + bindAuthorization = userAdmin.getAuthorization(authenticatingUser); + // TODO check tokens as well + if (bindAuthorization != null) { + authenticatedUser = user; + return true; + } + } catch (Exception e) { + // silent + if (log.isTraceEnabled()) + log.trace("Bind failed", e); } - } catch (Exception e) { - // silent - if (log.isTraceEnabled()) - log.trace("Bind failed", e); - } // works only if a connection password is provided if (!user.hasCredential(null, password)) { @@ -185,8 +188,10 @@ public class UserAdminLoginModule implements LoginModule { // TODO check CRLs/OSCP validity? // NB: authorization in commit() will work only if an LDAP connection password // is provided - } else if (singleUser) { - // TODO verify IP address? +// } else if (singleUser) { +// // TODO verify IP address? + } else if (preauth) { + // ident } else { throw new CredentialNotFoundException("No credentials provided"); } @@ -197,17 +202,15 @@ public class UserAdminLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - if (locale == null) - subject.getPublicCredentials().add(Locale.getDefault()); - else + if (locale != null) subject.getPublicCredentials().add(locale); - if (singleUser) { - OsUserUtils.loginAsSystemUser(subject); - } - UserAdmin userAdmin = Activator.getUserAdmin(); +// if (singleUser) { +// OsUserUtils.loginAsSystemUser(subject); +// } + UserAdmin userAdmin = CmsContextImpl.getCmsContext().getUserAdmin(); Authorization authorization; - if (callbackHandler == null) {// anonymous + if (callbackHandler == null && !sharedState.containsKey(CmsAuthUtils.SHARED_STATE_OS_USERNAME)) {// anonymous authorization = userAdmin.getAuthorization(null); } else if (bindAuthorization != null) {// bind authorization = bindAuthorization; @@ -218,7 +221,7 @@ public class UserAdminLoginModule implements LoginModule { if (authenticatedUser == null) { if (log.isTraceEnabled()) log.trace("Neither kerberos nor user admin login succeeded. Login failed."); - return false; + throw new CredentialNotFoundException("Bad credentials."); } else { authenticatingUser = authenticatedUser; } @@ -230,6 +233,8 @@ public class UserAdminLoginModule implements LoginModule { throw new LoginException("Kerberos login " + authenticatingUser.getName() + " is inconsistent with user admin login " + authenticatedUser.getName()); } + if (log.isTraceEnabled()) + log.trace("Retrieve authorization for " + authenticatingUser + "... "); authorization = Subject.doAs(subject, new PrivilegedAction() { @Override @@ -245,38 +250,51 @@ public class UserAdminLoginModule implements LoginModule { } // Log and monitor new login - HttpServletRequest request = (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST); - CmsAuthUtils.addAuthorization(subject, authorization, locale, request); + RemoteAuthRequest request = (RemoteAuthRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST); + CmsAuthUtils.addAuthorization(subject, authorization); // Unlock keyring (underlying login to the JCR repository) - char[] password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD); - if (password != null) { - ServiceReference keyringSr = bc.getServiceReference(CryptoKeyring.class); - if (keyringSr != null) { - CryptoKeyring keyring = bc.getService(keyringSr); - Subject.doAs(subject, new PrivilegedAction() { - - @Override - public Void run() { - try { - keyring.unlock(password); - } catch (Exception e) { - e.printStackTrace(); - log.warn("Could not unlock keyring with the password provided by " + authorization.getName() - + ": " + e.getMessage()); - } - return null; - } - - }); - } - } +// char[] password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD); +// if (password != null) { +// ServiceReference keyringSr = bc.getServiceReference(CryptoKeyring.class); +// if (keyringSr != null) { +// CryptoKeyring keyring = bc.getService(keyringSr); +// Subject.doAs(subject, new PrivilegedAction() { +// +// @Override +// public Void run() { +// try { +// keyring.unlock(password); +// } catch (Exception e) { +// e.printStackTrace(); +// log.warn("Could not unlock keyring with the password provided by " + authorization.getName() +// + ": " + e.getMessage()); +// } +// return null; +// } +// +// }); +// } +// } // Register CmsSession with initial subject CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale); - if (log.isDebugEnabled()) - log.debug("Logged in to CMS: " + subject); + if (log.isDebugEnabled()) { + StringBuilder msg = new StringBuilder(); + msg.append("Logged in to CMS: " + authorization.getName() + "(" + authorization + ")\n"); + for (Principal principal : subject.getPrincipals()) { + msg.append(" Principal: " + principal.getName()).append(" (") + .append(principal.getClass().getSimpleName()).append(")\n"); + } + for (Object credential : subject.getPublicCredentials()) { + msg.append(" Public Credential: " + credential).append(" (") + .append(credential.getClass().getSimpleName()).append(")\n"); + } + log.debug(msg); + } +// if (log.isTraceEnabled()) +// log.trace(" Subject: " + subject); return true; } @@ -289,8 +307,6 @@ public class UserAdminLoginModule implements LoginModule { public boolean logout() throws LoginException { if (log.isTraceEnabled()) log.trace("Logging out from CMS... " + subject); - // boolean httpSessionLogoutOk = CmsAuthUtils.logoutSession(bc, - // subject); CmsAuthUtils.cleanUp(subject); return true; } @@ -301,6 +317,7 @@ public class UserAdminLoginModule implements LoginModule { Set collectedUsers = new HashSet<>(); // try dn User user = null; + user = null; // try all indexes for (String attr : indexedUserProperties) { user = userAdmin.getUser(attr, providedUsername); @@ -331,22 +348,26 @@ public class UserAdminLoginModule implements LoginModule { } protected Group searchForToken(UserAdmin userAdmin, String token) { - String dn = cn + "=" + token + "," + NodeConstants.TOKENS_BASEDN; + String dn = cn + "=" + token + "," + CmsConstants.TOKENS_BASEDN; Group tokenGroup = (Group) userAdmin.getRole(dn); return tokenGroup; } protected Authorization getAuthorizationFromToken(UserAdmin userAdmin, Group tokenGroup) { - String expiryDateStr = (String) tokenGroup.getProperties().get(description.name()); - if (expiryDateStr != null) { - Instant expiryDate = NamingUtils.ldapDateToInstant(expiryDateStr); - if (expiryDate.isBefore(Instant.now())) { - if (log.isDebugEnabled()) - log.debug("Token " + tokenGroup.getName() + " has expired."); - return null; - } - } - Authorization auth = userAdmin.getAuthorization(tokenGroup); + if (TokenUtils.isExpired(tokenGroup)) + return null; +// String expiryDateStr = (String) tokenGroup.getProperties().get(description.name()); +// if (expiryDateStr != null) { +// Instant expiryDate = NamingUtils.ldapDateToInstant(expiryDateStr); +// if (expiryDate.isBefore(Instant.now())) { +// if (log.isDebugEnabled()) +// log.debug("Token " + tokenGroup.getName() + " has expired."); +// return null; +// } +// } + String userDn = TokenUtils.userDn(tokenGroup); + User user = (User) userAdmin.getRole(userDn); + Authorization auth = userAdmin.getAuthorization(user); return auth; } }