X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Fauth%2FUserAdminLoginModule.java;h=e39918e4002d40d18584ad91819a2a86545c92e5;hb=35507e18257f2e9f59842ba5120fcd3f19c4cf4d;hp=b368b1de5345da3aad3118d217d5a0c3fb9ba3a0;hpb=2fc5ac92dd1de5d93328766fc86b37086b518b98;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 b368b1de5..e39918e40 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -2,8 +2,9 @@ package org.argeo.cms.auth; import java.io.IOException; import java.security.PrivilegedAction; -import java.util.ArrayList; +import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -19,7 +20,6 @@ import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.CredentialNotFoundException; -import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import javax.servlet.http.HttpServletRequest; @@ -27,9 +27,11 @@ 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.eclipse.ui.specific.UiContext; +import org.argeo.cms.internal.kernel.Activator; import org.argeo.naming.LdapAttrs; +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.service.useradmin.Authorization; @@ -43,14 +45,17 @@ public class UserAdminLoginModule implements LoginModule { private CallbackHandler callbackHandler; private Map sharedState = null; - // private boolean isAnonymous = false; private List indexedUserProperties = Arrays - .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(), LdapAttrs.cn.name() }); + .asList(new String[] { LdapAttrs.mail.name(), LdapAttrs.uid.name(), LdapAttrs.authPassword.name() }); // private state private BundleContext bc; - // private Authorization authorization; private User authenticatedUser = null; + private Locale locale; + + private Authorization bindAuthorization = null; + + private boolean singleUser = Activator.isSingleUser(); @SuppressWarnings("unchecked") @Override @@ -59,13 +64,8 @@ public class UserAdminLoginModule implements LoginModule { this.subject = subject; try { bc = FrameworkUtil.getBundle(UserAdminLoginModule.class).getBundleContext(); - assert bc != null; - // this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = (Map) sharedState; - // if (options.containsKey("anonymous")) - // isAnonymous = - // Boolean.parseBoolean(options.get("anonymous").toString()); } catch (Exception e) { throw new CmsException("Cannot initialize login module", e); } @@ -73,32 +73,27 @@ public class UserAdminLoginModule implements LoginModule { @Override public boolean login() throws LoginException { - Authorization sharedAuth = (Authorization) sharedState.get(CmsAuthUtils.SHARED_STATE_AUTHORIZATION); - if (sharedAuth != null) { - if (callbackHandler == null && sharedAuth.getName() != null) - throw new LoginException("Shared authorization should be anonymous"); - return false; - } UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class)); - if (callbackHandler == null) {// anonymous - // authorization = userAdmin.getAuthorization(null); - // sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, - // authorization); - return true; - } - final String username; final char[] password; + X509Certificate[] certificateChain = null; 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? - // // NB: raw user name is used - // AuthenticatingUser authenticatingUser = new - // AuthenticatingUser(username, password); - // authorization = userAdmin.getAuthorization(authenticatingUser); + } else if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME) + && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN)) { + // NB: required by Basic http auth + username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME); + certificateChain = (X509Certificate[]) sharedState.get(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN); + password = null; + } else if (singleUser) { + username = OsUserUtils.getOsUsername(); + password = null; } else { + // ask for username and password NameCallback nameCallback = new NameCallback("User"); PasswordCallback passwordCallback = new PasswordCallback("Password", false); @@ -112,10 +107,11 @@ public class UserAdminLoginModule implements LoginModule { } // i18n - Locale locale = langCallback.getLocale(); + locale = langCallback.getLocale(); if (locale == null) locale = Locale.getDefault(); - UiContext.setLocale(locale); + // FIXME add it to Subject + // Locale.setDefault(locale); username = nameCallback.getName(); if (username == null || username.trim().equals("")) { @@ -126,55 +122,75 @@ public class UserAdminLoginModule implements LoginModule { password = passwordCallback.getPassword(); else throw new CredentialNotFoundException("No credentials provided"); - // FIXME move Argeo specific convention from user admin to here } - - // User user = userAdmin.getUser(null, username); User user = searchForUser(userAdmin, username); if (user == null) return true;// expect Kerberos - // throw new FailedLoginException("Invalid credentials"); - if (!user.hasCredential(null, password)) - throw new FailedLoginException("Invalid credentials"); - authenticatedUser = user; - // return false; - // authorization = userAdmin.getAuthorization(user); - // assert authorization != null; - // - // sharedState.put(CmsAuthUtils.SHARED_STATE_AUTHORIZATION, - // authorization); + if (password != null) { + // 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; + } + } 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)) { + return false; + } + } else if (certificateChain != null) { + // 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 { + throw new CredentialNotFoundException("No credentials provided"); + } + + authenticatedUser = user; return true; } @Override public boolean commit() throws LoginException { - // if (authorization == null) { - // return false; - // // throw new LoginException("Authorization should not be null"); - // } else { - // CmsAuthUtils.addAuthentication(subject, authorization); - // return true; - // } + if (singleUser) { + OsUserUtils.loginAsSystemUser(subject); + } UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class)); - Authorization authorization = null; - User authenticatingUser; - Set kerberosPrincipals = subject.getPrincipals(KerberosPrincipal.class); - if (kerberosPrincipals.isEmpty()) { - if (callbackHandler == null) { - authorization = userAdmin.getAuthorization(null); - } - if (authenticatedUser == null) { - return false; + Authorization authorization; + if (callbackHandler == null) {// anonymous + authorization = userAdmin.getAuthorization(null); + } else if (bindAuthorization != null) {// bind + authorization = bindAuthorization; + } else {// Kerberos + User authenticatingUser; + Set kerberosPrincipals = subject.getPrincipals(KerberosPrincipal.class); + if (kerberosPrincipals.isEmpty()) { + if (authenticatedUser == null) { + if (log.isTraceEnabled()) + log.trace("Neither kerberos nor user admin login succeeded. Login failed."); + return false; + } else { + authenticatingUser = authenticatedUser; + } } else { - authenticatingUser = authenticatedUser; + KerberosPrincipal kerberosPrincipal = kerberosPrincipals.iterator().next(); + LdapName dn = IpaUtils.kerberosToDn(kerberosPrincipal.getName()); + authenticatingUser = new AuthenticatingUser(dn); + if (authenticatedUser != null && !authenticatingUser.getName().equals(authenticatedUser.getName())) + throw new LoginException("Kerberos login " + authenticatingUser.getName() + + " is inconsistent with user admin login " + authenticatedUser.getName()); } - } else { - KerberosPrincipal kerberosPrincipal = kerberosPrincipals.iterator().next(); - LdapName dn = IpaUtils.kerberosToDn(kerberosPrincipal.getName()); - authenticatingUser = new AuthenticatingUser(dn); - } - if (authorization == null) authorization = Subject.doAs(subject, new PrivilegedAction() { @Override @@ -184,29 +200,29 @@ public class UserAdminLoginModule implements LoginModule { } }); - if (authorization == null) - return false; + if (authorization == null) + throw new LoginException( + "User admin found no authorization for authenticated user " + authenticatingUser.getName()); + } // Log and monitor new login - CmsAuthUtils.addAuthentication(subject, authorization); + CmsAuthUtils.addAuthorization(subject, authorization, locale, + (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST)); if (log.isDebugEnabled()) log.debug("Logged in to CMS: " + subject); - HttpServletRequest request = (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST); - if (request != null) { - CmsAuthUtils.registerSessionAuthorization(bc, request, subject, authorization); - } return true; } @Override public boolean abort() throws LoginException { - // authorization = null; return true; } @Override public boolean logout() throws LoginException { - if (log.isDebugEnabled()) - log.debug("Logging out from CMS... " + subject); + if (log.isTraceEnabled()) + log.trace("Logging out from CMS... " + subject); + // boolean httpSessionLogoutOk = CmsAuthUtils.logoutSession(bc, + // subject); CmsAuthUtils.cleanUp(subject); return true; } @@ -214,26 +230,29 @@ public class UserAdminLoginModule implements LoginModule { protected User searchForUser(UserAdmin userAdmin, String providedUsername) { try { // TODO check value null or empty - List collectedUsers = new ArrayList(); + Set collectedUsers = new HashSet<>(); // try dn User user = null; - try { - user = (User) userAdmin.getRole(providedUsername); - if (user != null) - collectedUsers.add(user); - } catch (Exception e) { - // silent - } // try all indexes for (String attr : indexedUserProperties) { user = userAdmin.getUser(attr, providedUsername); if (user != null) collectedUsers.add(user); } - if (collectedUsers.size() == 1) - return collectedUsers.get(0); - else if (collectedUsers.size() > 1) + if (collectedUsers.size() == 1) { + user = collectedUsers.iterator().next(); + return user; + } else if (collectedUsers.size() > 1) { log.warn(collectedUsers.size() + " users for provided username" + providedUsername); + } + // try DN as a last resort + try { + user = (User) userAdmin.getRole(providedUsername); + if (user != null) + return user; + } catch (Exception e) { + // silent + } return null; } catch (Exception e) { if (log.isTraceEnabled())