SAML support for CCM

git-svn-id: https://svn.libreccm.org/ccm/trunk@6168 8810af33-2d31-482b-a856-94f89814c4df
master^2^2^2^2^2^2
jensp 2019-08-29 08:52:02 +00:00 committed by Jens Pelzetter
parent 288dd00ca1
commit 401c71b6d6
4 changed files with 242 additions and 103 deletions

View File

@ -5,13 +5,24 @@
*/ */
package com.arsdigita.kernel.security; package com.arsdigita.kernel.security;
import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.kernel.UserAuthentication;
import com.onelogin.saml2.Auth;
import com.onelogin.saml2.exception.Error;
import com.onelogin.saml2.exception.SettingsException;
import org.apache.log4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.security.auth.Subject; import javax.security.auth.Subject;
import javax.security.auth.callback.Callback; import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException; import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule; import javax.security.auth.spi.LoginModule;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -23,10 +34,13 @@ import javax.servlet.http.HttpServletResponse;
*/ */
public class SamlLoginModule implements LoginModule { public class SamlLoginModule implements LoginModule {
private static final Logger LOGGER = Logger.getLogger(SamlLoginModule.class);
private CallbackHandler callbackHandler; private CallbackHandler callbackHandler;
private HttpServletRequest request; private HttpServletRequest request;
private HttpServletResponse response; private HttpServletResponse response;
private Subject subject; private Subject subject;
private BigDecimal userId;
@Override @Override
public void initialize(final Subject subject, public void initialize(final Subject subject,
@ -40,22 +54,54 @@ public class SamlLoginModule implements LoginModule {
@Override @Override
public boolean login() throws LoginException { public boolean login() throws LoginException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
final HttpServletRequest request = getRequest();
final HttpServletResponse response = getResponse();
final Auth auth;
try {
auth = new Auth(request, response);
} catch (IOException | SettingsException | Error ex) {
LOGGER.error("SAML Login failed.", ex);
throw new LoginException("SAML Login failed. Configuration error?");
}
final List<String> errors = auth.getErrors();
if (!errors.isEmpty()) {
LOGGER.error(String.format("SAML Login errors: %s",
String.join(";\n", errors)));
throw new LoginException(String.format("SAML Login errors: %s",
String.join(";\n", errors)));
}
if (!auth.isAuthenticated()) {
throw new LoginException("Not authenticated.");
}
userId = getUserId(auth.getNameId());
return true;
} }
@Override @Override
public boolean commit() throws LoginException { public boolean commit() throws LoginException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
if (userId != null) {
subject.getPrincipals().add(new PartyPrincipal(userId));
}
return true;
} }
@Override @Override
public boolean abort() throws LoginException { public boolean abort() throws LoginException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. return true;
} }
@Override @Override
public boolean logout() throws LoginException { public boolean logout() throws LoginException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
getRequest().getSession().invalidate();
return true;
} }
protected HttpServletRequest getRequest() throws LoginException { protected HttpServletRequest getRequest() throws LoginException {
@ -86,4 +132,18 @@ public class SamlLoginModule implements LoginModule {
} }
} }
protected BigDecimal getUserId(final String ssoLogin)
throws LoginException {
try {
final UserAuthentication userAuth = UserAuthentication
.retrieveForSSOlogin(ssoLogin);
return userAuth.getUser().getID();
} catch(DataObjectNotFoundException ex) {
throw new FailedLoginException(
String.format("SSO login %s not found", ssoLogin)
);
}
}
} }

View File

@ -126,7 +126,9 @@ public class SecurityConfig extends AbstractConfig {
"Register:com.arsdigita.kernel.security.UserIDLoginModule:requisite", "Register:com.arsdigita.kernel.security.UserIDLoginModule:requisite",
"Register:com.arsdigita.kernel.security.CookieLoginModule:optional", "Register:com.arsdigita.kernel.security.CookieLoginModule:optional",
"RegisterSSO:com.arsdigita.kernel.security.SimpleSSOLoginModule:requisite", "RegisterSSO:com.arsdigita.kernel.security.SimpleSSOLoginModule:requisite",
"RegisterSSO:com.arsdigita.kernel.security.CookieLoginModule:optional" "RegisterSSO:com.arsdigita.kernel.security.CookieLoginModule:optional",
"RegisterSAML:com.arsdigita.kernel.security.SamlLoginModule:requisite",
"RegisterSAML:com.arsdigita.kernel.security.CookieLoginModule:optional"
}); });
private final Parameter m_adminEmail = new StringParameter( private final Parameter m_adminEmail = new StringParameter(
"waf.admin.contact_email", Parameter.OPTIONAL, null); "waf.admin.contact_email", Parameter.OPTIONAL, null);

View File

@ -57,7 +57,8 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* Provides methods for logging in and logging out the current user and accessing the user ID. * Provides methods for logging in and logging out the current user and
* accessing the user ID.
* *
* @author Sameer Ajmani * @author Sameer Ajmani
* @version $Id: UserContext.java 1498 2007-03-19 16:22:15Z apevec $ * @version $Id: UserContext.java 1498 2007-03-19 16:22:15Z apevec $
@ -81,6 +82,11 @@ public class UserContext {
*/ */
public static final String REGISTER_SSO_LOGIN_CONTEXT = "RegisterSSO"; public static final String REGISTER_SSO_LOGIN_CONTEXT = "RegisterSSO";
/**
* Name of the SAML login context
*/
public static final String REGISTER_SAML_LOGIN_CONTEXT = "RegisterSAML";
// fields // fields
private HttpServletRequest m_req; private HttpServletRequest m_req;
private HttpServletResponse m_res; private HttpServletResponse m_res;
@ -101,7 +107,8 @@ public class UserContext {
} }
/** /**
* Loads the values for this <code>UserContext</code> from the given Subject. * Loads the values for this <code>UserContext</code> from the given
* Subject.
*/ */
private void loadValues(Subject subject) private void loadValues(Subject subject)
throws LoginException { throws LoginException {
@ -118,11 +125,12 @@ public class UserContext {
} }
/** /**
* Creates a user context from an HTTP request. Attempts to log in the user automatically to * Creates a user context from an HTTP request. Attempts to log in the user
* load the user ID. Code should access this class using * automatically to load the user ID. Code should access this class using
* <code>KernelHelper.getKernelRequestContext(req).getUserContext()</code>. * <code>KernelHelper.getKernelRequestContext(req).getUserContext()</code>.
* *
* @throws RedirectException if the user should be redirected to the login page. * @throws RedirectException if the user should be redirected to the login
* page.
*/ */
public UserContext(HttpServletRequest req, public UserContext(HttpServletRequest req,
HttpServletResponse res) HttpServletResponse res)
@ -149,7 +157,8 @@ public class UserContext {
login(UserAuthentication.retrieveForLoginName(username).getUser()); login(UserAuthentication.retrieveForLoginName(username).getUser());
s_log.debug("SUCCESS login(username)"); s_log.debug("SUCCESS login(username)");
} catch (DataObjectNotFoundException e) { } catch (DataObjectNotFoundException e) {
throw new AccountNotFoundException("user " + username + " does not exist", e); throw new AccountNotFoundException("user " + username
+ " does not exist", e);
} }
} }
@ -169,7 +178,8 @@ public class UserContext {
login(User.retrieve(userID)); login(User.retrieve(userID));
s_log.debug("SUCCESS login(userID)"); s_log.debug("SUCCESS login(userID)");
} catch (DataObjectNotFoundException e) { } catch (DataObjectNotFoundException e) {
throw new AccountNotFoundException("user " + userID + " does not exist", e); throw new AccountNotFoundException("user " + userID
+ " does not exist", e);
} }
} }
@ -178,8 +188,9 @@ public class UserContext {
* *
* @param target the User to become * @param target the User to become
* *
* @throws FailedLoginException if the current user is not logged in, doesn't exist, or doesn't * @throws FailedLoginException if the current user is not logged in,
* have admin privileges on the target user. * doesn't exist, or doesn't have admin
* privileges on the target user.
* *
* @throws LoginException if an error occurs. * @throws LoginException if an error occurs.
*/ */
@ -205,8 +216,9 @@ public class UserContext {
throw new LoginException("This user is currently banned"); throw new LoginException("This user is currently banned");
} }
PermissionDescriptor superuser = new PermissionDescriptor(PrivilegeDescriptor.ADMIN, target, PermissionDescriptor superuser = new PermissionDescriptor(
user); PrivilegeDescriptor.ADMIN, target,
user);
if (!PermissionService.checkPermission(superuser)) { if (!PermissionService.checkPermission(superuser)) {
s_log.debug("FAILURE login(User): insufficient privileges"); s_log.debug("FAILURE login(User): insufficient privileges");
@ -214,14 +226,16 @@ public class UserContext {
+ " failed to log in as user " + " failed to log in as user "
+ target.getID() + target.getID()
+ " due to insufficient privileges"); + " due to insufficient privileges");
throw new FailedLoginException("insufficient privileges to become target user"); throw new FailedLoginException(
"insufficient privileges to become target user");
} }
// set the target user ID in the Subject and login // set the target user ID in the Subject and login
Subject subject = new Subject(); Subject subject = new Subject();
subject.getPrincipals().add(new PartyPrincipal(target.getID())); subject.getPrincipals().add(new PartyPrincipal(target.getID()));
CallbackHandler handler = new RequestCallbackHandler(); CallbackHandler handler = new RequestCallbackHandler();
LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT, subject, handler); LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT, subject,
handler);
clearValues(); clearValues();
context.login(); context.login();
loadValues(context.getSubject()); loadValues(context.getSubject());
@ -241,7 +255,8 @@ public class UserContext {
/** /**
* Determines whether the user is logged in. * Determines whether the user is logged in.
* *
* @return <code>true</code> if the user is logged in, <code>false</code> otherwise. * @return <code>true</code> if the user is logged in, <code>false</code>
* otherwise.
*/ */
public boolean isLoggedIn() { public boolean isLoggedIn() {
return (m_userID != null); return (m_userID != null);
@ -250,7 +265,8 @@ public class UserContext {
/** /**
* Determines whether the user is recovering a forgotten password. * Determines whether the user is recovering a forgotten password.
* *
* @return <code>true</code> if the user is recovering, <code>false</code> otherwise. * @return <code>true</code> if the user is recovering, <code>false</code>
* otherwise.
*/ */
public boolean isRecovering() { public boolean isRecovering() {
return m_recovering; return m_recovering;
@ -266,7 +282,8 @@ public class UserContext {
} }
/** /**
* Returns the set of all possible URL params used by UserContext. Package-private. * Returns the set of all possible URL params used by UserContext.
* Package-private.
* *
* @return an unmodifiable set of bebop ParameterModels. * @return an unmodifiable set of bebop ParameterModels.
*/ */
@ -274,8 +291,9 @@ public class UserContext {
try { try {
// LoginModules add ParameterModels to Subject in initialize() // LoginModules add ParameterModels to Subject in initialize()
LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT); LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT);
return Collections.unmodifiableSet(context.getSubject().getPublicCredentials( return Collections.unmodifiableSet(context.getSubject()
ParameterModel.class)); .getPublicCredentials(
ParameterModel.class));
} catch (LoginException e) { } catch (LoginException e) {
throw new UncheckedWrapperException("Could not load context", e); throw new UncheckedWrapperException("Could not load context", e);
} }
@ -296,10 +314,12 @@ public class UserContext {
} }
/** /**
* Returns a User object for the current user. Subsequent calls to this method return references * Returns a User object for the current user. Subsequent calls to this
* to the same User object until the <code>logout</code> method is called. * method return references to the same User object until the
* <code>logout</code> method is called.
* *
* @return the User object for the logged in user or null if the user is not found. * @return the User object for the logged in user or null if the user is not
* found.
* *
* @throws IllegalStateException if the user is not logged in. * @throws IllegalStateException if the user is not logged in.
*/ */
@ -321,10 +341,11 @@ public class UserContext {
} }
/** /**
* Logs in the user from data in the current request. Checks the session ID using * Logs in the user from data in the current request. Checks the session ID
* <code>SessionContext</code>. * using <code>SessionContext</code>.
* *
* @throws RedirectException if the user should be redirected to the login page. * @throws RedirectException if the user should be redirected to the login
* page.
*/ */
private void login() private void login()
throws RedirectException { throws RedirectException {
@ -333,7 +354,8 @@ public class UserContext {
boolean success = false; boolean success = false;
try { try {
// log in user from request parameters // log in user from request parameters
LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT, handler); LoginContext context = new LoginContext(REQUEST_LOGIN_CONTEXT,
handler);
clearValues(); clearValues();
context.login(); context.login();
loadValues(context.getSubject()); loadValues(context.getSubject());
@ -362,7 +384,7 @@ public class UserContext {
// common code for all exception cases // common code for all exception cases
if (Util.getSecurityHelper().requiresLogin(m_req)) { if (Util.getSecurityHelper().requiresLogin(m_req)) {
s_log.debug("This request requires logging in; " s_log.debug("This request requires logging in; "
+ "requesting redirect to login UI"); + "requesting redirect to login UI");
redirectToLoginPage(m_req); redirectToLoginPage(m_req);
} else { } else {
s_log.debug("This request does not require logging in"); s_log.debug("This request does not require logging in");
@ -404,7 +426,8 @@ public class UserContext {
return; return;
} }
m_username = userpass.substring(0, colon); m_username = userpass.substring(0, colon);
m_password = userpass.substring(colon + 1, userpass.length()).toCharArray(); m_password = userpass.substring(colon + 1, userpass.length())
.toCharArray();
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedWrapperException(e); throw new UncheckedWrapperException(e);
} }
@ -417,10 +440,12 @@ public class UserContext {
for (int i = 0; i < callbacks.length; i++) { for (int i = 0; i < callbacks.length; i++) {
Callback cb = callbacks[i]; Callback cb = callbacks[i];
if (cb instanceof HTTPRequestCallback) { if (cb instanceof HTTPRequestCallback) {
((HTTPRequestCallback) cb).setRequest(UserContext.this.m_req); ((HTTPRequestCallback) cb)
.setRequest(UserContext.this.m_req);
} else if (cb instanceof HTTPResponseCallback) { } else if (cb instanceof HTTPResponseCallback) {
((HTTPResponseCallback) cb).setResponse(UserContext.this.m_res); ((HTTPResponseCallback) cb).setResponse(
UserContext.this.m_res);
} else if (cb instanceof LifetimeCallback) { } else if (cb instanceof LifetimeCallback) {
((LifetimeCallback) cb).setForever(false); ((LifetimeCallback) cb).setForever(false);
@ -440,7 +465,8 @@ public class UserContext {
} }
/** /**
* Creates a URL to send the user to the login page and then return to the current page. * Creates a URL to send the user to the login page and then return to the
* current page.
* *
* @throws com.arsdigita.web.LoginSignal * @throws com.arsdigita.web.LoginSignal
*/ */
@ -449,18 +475,19 @@ public class UserContext {
} }
/** /**
* Name of the request parameter that stores the URL to return to after redirecting to the login * Name of the request parameter that stores the URL to return to after
* page. * redirecting to the login page.
* *
* @deprecated Use com.arsdigita.ui.login.LoginHelper.RETURN_URL_PARAM_NAME instead * @deprecated Use com.arsdigita.ui.login.LoginHelper.RETURN_URL_PARAM_NAME
* instead
*/ */
public final static String RETURN_URL_PARAM_NAME = "return_url"; public final static String RETURN_URL_PARAM_NAME = "return_url";
/** /**
* Encodes the given request into a return URL parameter. Returns * Encodes the given request into a return URL parameter. Returns
* <code>URLencode(returnURL)</code> where returnURL is * <code>URLencode(returnURL)</code> where returnURL is
* <code>returnURI?key=URLencode(val)&...</code>. The original parameter values are * <code>returnURI?key=URLencode(val)&...</code>. The original parameter
* doubly-encoded so that they are decoded appropriately. * values are doubly-encoded so that they are decoded appropriately.
* *
* *
* @param req the request to encode * @param req the request to encode
@ -496,7 +523,8 @@ public class UserContext {
} }
/** /**
* Logs in the user. Checks the session ID using <code>SessionContext</code>. * Logs in the user. Checks the session ID using
* <code>SessionContext</code>.
* *
* @param username the user's username * @param username the user's username
* @param password the user's password * @param password the user's password
@ -510,16 +538,19 @@ public class UserContext {
throws LoginException { throws LoginException {
s_log.debug("START login(username, password, forever)"); s_log.debug("START login(username, password, forever)");
try { try {
CallbackHandler handler = new LoginCallbackHandler(username, password, forever); CallbackHandler handler = new LoginCallbackHandler(username,
LoginContext context = new LoginContext(REGISTER_LOGIN_CONTEXT, handler); password, forever);
LoginContext context = new LoginContext(REGISTER_LOGIN_CONTEXT,
handler);
clearValues(); clearValues();
context.login(); context.login();
// We now check if the user is banned and, if so, we don't allow // We now check if the user is banned and, if so, we don't allow
// the user to login. // the user to login.
if (Kernel.getSecurityConfig().isUserBanOn() if (Kernel.getSecurityConfig().isUserBanOn()
&& UserAuthentication.retrieveForLoginName(username).getUser() && UserAuthentication.retrieveForLoginName(username)
.isBanned()) { .getUser()
.isBanned()) {
throw new LoginException("This user is currently banned"); throw new LoginException("This user is currently banned");
} }
@ -533,8 +564,8 @@ public class UserContext {
} }
/** /**
* Logs in the user using alternative "RegisterSSO" login context. It is expected that SSO token * Logs in the user using alternative "RegisterSSO" login context. It is
* is present in the request * expected that SSO token is present in the request
* *
* @see SimpleSSOLoginModule * @see SimpleSSOLoginModule
* @throws LoginException * @throws LoginException
@ -560,6 +591,30 @@ public class UserContext {
} }
} }
/**
* Logs in the user using a SAML response (which should be available in the
* request).
* @throws javax.security.auth.login.LoginException
*/
public void loginSAML() throws LoginException {
try {
final CallbackHandler handler = new RequestCallbackHandler();
final LoginContext context = new LoginContext(
REGISTER_SAML_LOGIN_CONTEXT,
handler);
clearValues();
context.login();
loadValues(context.getSubject());
m_session.loadSessionID(handler);
s_log.debug("SUCCESS loginSAML()");
} catch (LoginException ex) {
SecurityLogger.info("register SAML login failed: ", ex);
throw ex;
}
}
/** /**
* Implements callbacks for interactive (register-based) login. * Implements callbacks for interactive (register-based) login.
*/ */
@ -597,10 +652,12 @@ public class UserContext {
for (int i = 0; i < callbacks.length; i++) { for (int i = 0; i < callbacks.length; i++) {
Callback cb = callbacks[i]; Callback cb = callbacks[i];
if (cb instanceof HTTPRequestCallback) { if (cb instanceof HTTPRequestCallback) {
((HTTPRequestCallback) cb).setRequest(UserContext.this.m_req); ((HTTPRequestCallback) cb)
.setRequest(UserContext.this.m_req);
} else if (cb instanceof HTTPResponseCallback) { } else if (cb instanceof HTTPResponseCallback) {
((HTTPResponseCallback) cb).setResponse(UserContext.this.m_res); ((HTTPResponseCallback) cb).setResponse(
UserContext.this.m_res);
} else if (cb instanceof LifetimeCallback) { } else if (cb instanceof LifetimeCallback) {
((LifetimeCallback) cb).setForever(m_forever); ((LifetimeCallback) cb).setForever(m_forever);
@ -639,8 +696,8 @@ public class UserContext {
} }
/** /**
* Logs out the user. Clears the cached User object. Loads a new session ID using * Logs out the user. Clears the cached User object. Loads a new session ID
* <code>SessionContext</code>. * using <code>SessionContext</code>.
* *
* @throws LoginException if logout fails. * @throws LoginException if logout fails.
*/ */
@ -657,7 +714,8 @@ public class UserContext {
} }
/** /**
* Reports an unsupported callback to the debug log and throws an exception. Package-private. * Reports an unsupported callback to the debug log and throws an exception.
* Package-private.
* *
* @throws UnsupportedCallbackException with appropriate error message * @throws UnsupportedCallbackException with appropriate error message
*/ */

View File

@ -75,12 +75,15 @@ import java.net.URISyntaxException;
* *
* @version $Id: UserLoginForm.java 1230 2006-06-22 11:50:59Z apevec $ * @version $Id: UserLoginForm.java 1230 2006-06-22 11:50:59Z apevec $
*/ */
public class UserLoginForm extends Form public class UserLoginForm
implements LoginConstants, FormInitListener, extends Form
FormValidationListener, FormProcessListener { implements LoginConstants,
FormInitListener,
FormValidationListener,
FormProcessListener {
private static final Logger s_log private static final Logger s_log
= Logger.getLogger(UserLoginForm.class); = Logger.getLogger(UserLoginForm.class);
// package friendly static form name makes writing HttpUnitTest easier // package friendly static form name makes writing HttpUnitTest easier
final static String FORM_NAME = "user-login"; final static String FORM_NAME = "user-login";
@ -91,7 +94,7 @@ public class UserLoginForm extends Form
private TextField m_loginName; private TextField m_loginName;
private Password m_password; private Password m_password;
private boolean m_autoRegistrationOn; private boolean m_autoRegistrationOn;
private SecurityConfig securityConfig = SecurityConfig.getConfig(); private final SecurityConfig securityConfig = SecurityConfig.getConfig();
/** /**
* Default constructor delegates to a constructor which creates a LoginForm * Default constructor delegates to a constructor which creates a LoginForm
@ -129,14 +132,14 @@ public class UserLoginForm extends Form
add(m_timestamp); add(m_timestamp);
m_returnURL = new Hidden(new URLParameter( m_returnURL = new Hidden(new URLParameter(
LoginHelper.RETURN_URL_PARAM_NAME)); LoginHelper.RETURN_URL_PARAM_NAME));
m_returnURL.setPassIn(true); m_returnURL.setPassIn(true);
add(m_returnURL); add(m_returnURL);
setupLogin(); setupLogin();
add(new Label(LoginHelper.getMessage( add(new Label(LoginHelper.getMessage(
"login.userRegistrationForm.password"))); "login.userRegistrationForm.password")));
m_password = new Password(new StringParameter(FORM_PASSWORD)); m_password = new Password(new StringParameter(FORM_PASSWORD));
// Since new users should not enter a password, allow null. // Since new users should not enter a password, allow null.
//m_password.addValidationListener(new NotNullValidationListener()); //m_password.addValidationListener(new NotNullValidationListener());
@ -145,7 +148,7 @@ public class UserLoginForm extends Form
SimpleContainer cookiePanel = new BoxPanel(BoxPanel.HORIZONTAL); SimpleContainer cookiePanel = new BoxPanel(BoxPanel.HORIZONTAL);
m_isPersistent = new CheckboxGroup(FORM_PERSISTENT_LOGIN_P); m_isPersistent = new CheckboxGroup(FORM_PERSISTENT_LOGIN_P);
Label optLabel = new Label(LoginHelper.getMessage( Label optLabel = new Label(LoginHelper.getMessage(
"login.userRegistrationForm.cookieOption")); "login.userRegistrationForm.cookieOption"));
Option opt = new Option(FORM_PERSISTENT_LOGIN_P_DEFAULT, optLabel); Option opt = new Option(FORM_PERSISTENT_LOGIN_P_DEFAULT, optLabel);
m_isPersistent.addOption(opt); m_isPersistent.addOption(opt);
if (Kernel.getConfig().isLoginRemembered()) { if (Kernel.getConfig().isLoginRemembered()) {
@ -154,8 +157,8 @@ public class UserLoginForm extends Form
cookiePanel.add(m_isPersistent); cookiePanel.add(m_isPersistent);
cookiePanel.add(new DynamicLink( cookiePanel.add(new DynamicLink(
"login.userRegistrationForm.explainCookieLink", "login.userRegistrationForm.explainCookieLink",
LoginServlet.getCookiesExplainPageURL())); LoginServlet.getCookiesExplainPageURL()));
add(cookiePanel); add(cookiePanel);
//add(new Submit(SUBMIT), ColumnPanel.CENTER | ColumnPanel.FULL_WIDTH); //add(new Submit(SUBMIT), ColumnPanel.CENTER | ColumnPanel.FULL_WIDTH);
@ -165,16 +168,16 @@ public class UserLoginForm extends Form
if (securityConfig.getEnableQuestion()) { if (securityConfig.getEnableQuestion()) {
add(new DynamicLink("login.userRegistrationForm.forgotPasswordLink", add(new DynamicLink("login.userRegistrationForm.forgotPasswordLink",
LoginServlet.getRecoverPasswordPageURL())); LoginServlet.getRecoverPasswordPageURL()));
} }
if (m_autoRegistrationOn) { if (m_autoRegistrationOn) {
add(new DynamicLink("login.userRegistrationForm.newUserRegister", add(new DynamicLink("login.userRegistrationForm.newUserRegister",
LoginServlet.getNewUserPageURL())); LoginServlet.getNewUserPageURL()));
} }
add(new ElementComponent("subsite:promptToEnableCookiesMsg", add(new ElementComponent("subsite:promptToEnableCookiesMsg",
LoginServlet.SUBSITE_NS_URI)); LoginServlet.SUBSITE_NS_URI));
} }
/** /**
@ -182,8 +185,8 @@ public class UserLoginForm extends Form
*/ */
private void setupLogin() { private void setupLogin() {
SimpleContainer loginMessage SimpleContainer loginMessage
= new SimpleContainer("subsite:loginPromptMsg", = new SimpleContainer("subsite:loginPromptMsg",
LoginServlet.SUBSITE_NS_URI); LoginServlet.SUBSITE_NS_URI);
if (Kernel.getConfig().emailIsPrimaryIdentifier()) { if (Kernel.getConfig().emailIsPrimaryIdentifier()) {
loginMessage.setClassAttr("email"); loginMessage.setClassAttr("email");
@ -195,15 +198,16 @@ public class UserLoginForm extends Form
if (Kernel.getConfig().emailIsPrimaryIdentifier()) { if (Kernel.getConfig().emailIsPrimaryIdentifier()) {
add(new Label(LoginHelper.getMessage( add(new Label(LoginHelper.getMessage(
"login.userRegistrationForm.email"))); "login.userRegistrationForm.email")));
m_loginName = new TextField(new EmailParameter(FORM_LOGIN)); m_loginName = new TextField(new EmailParameter(FORM_LOGIN));
addInitListener(new EmailInitListener((EmailParameter) m_loginName. addInitListener(new EmailInitListener((EmailParameter) m_loginName.
getParameterModel())); getParameterModel()));
} else { } else {
add(new Label(LoginHelper.getMessage( add(new Label(LoginHelper.getMessage(
"login.userRegistrationForm.screenName"))); "login.userRegistrationForm.screenName")));
m_loginName = new TextField(new StringParameter(FORM_LOGIN)); m_loginName = new TextField(new StringParameter(FORM_LOGIN));
addInitListener(new ScreenNameInitListener((StringParameter) m_loginName. addInitListener(new ScreenNameInitListener(
(StringParameter) m_loginName.
getParameterModel())); getParameterModel()));
} }
//m_loginName.addValidationListener(new NotNullValidationListener()); //m_loginName.addValidationListener(new NotNullValidationListener());
@ -213,11 +217,12 @@ public class UserLoginForm extends Form
/** /**
* *
* @param event * @param event
*
* @throws FormProcessException * @throws FormProcessException
*/ */
@Override @Override
public void init(FormSectionEvent event) public void init(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
s_log.info("In init"); s_log.info("In init");
if (Kernel.getConfig().isSSOenabled()) { if (Kernel.getConfig().isSSOenabled()) {
// try SSO login // try SSO login
@ -232,27 +237,31 @@ public class UserLoginForm extends Form
s_log.debug("SSO failed", le); s_log.debug("SSO failed", le);
} }
} }
if (securityConfig.getEnableSaml()) {
// ToDo Try SAML login via OneLogin
}
try { try {
// create timestamp // create timestamp
String value = Credential.create(FORM_TIMESTAMP, String value = Credential.create(FORM_TIMESTAMP,
1000 * TIMESTAMP_LIFETIME_SECS). 1000 * TIMESTAMP_LIFETIME_SECS).
toString(); toString();
m_timestamp.setValue(event.getPageState(), value); m_timestamp.setValue(event.getPageState(), value);
} catch (CredentialException e) { } catch (CredentialException e) {
s_log.debug("Could not create timestamp", e); s_log.debug("Could not create timestamp", e);
throw new FormProcessException(LoginGlobalizationUtil.globalize( throw new FormProcessException(LoginGlobalizationUtil.globalize(
"login.userLoginForm.couldnt_create_timestamp")); "login.userLoginForm.couldnt_create_timestamp"));
} }
} }
/** /**
* *
* @param event * @param event
*
* @throws FormProcessException * @throws FormProcessException
*/ */
@Override @Override
public void validate(FormSectionEvent event) public void validate(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
s_log.debug("In validate"); s_log.debug("In validate");
@ -274,8 +283,9 @@ public class UserLoginForm extends Form
s_log.info("Invalid credential"); s_log.info("Invalid credential");
final String path = LoginServlet.getLoginExpiredPageURL(); final String path = LoginServlet.getLoginExpiredPageURL();
final URL url = com.arsdigita.web.URL.there(state.getRequest(), final URL url = com.arsdigita.web.URL.there(state
path); .getRequest(),
path);
throw new RedirectSignal(url, false); throw new RedirectSignal(url, false);
} }
@ -295,8 +305,10 @@ public class UserLoginForm extends Form
/** /**
* *
* @param event * @param event
*
* @throws FormProcessException * @throws FormProcessException
*/ */
@Override
public void process(FormSectionEvent event) throws FormProcessException { public void process(FormSectionEvent event) throws FormProcessException {
s_log.debug("In process"); s_log.debug("In process");
@ -315,15 +327,17 @@ public class UserLoginForm extends Form
} }
//Cancel: //Cancel:
if (m_saveCancelSection.getCancelButton().isSelected(state)) { if (m_saveCancelSection.getCancelButton().isSelected(state)) {
//redirect the user to the place they came from. //redirect the user to the place they came from.
try { try {
String refererURI = new URI(req.getHeader("referer")).getPath(); String refererURI = new URI(req.getHeader("referer")).getPath();
if (refererURI.equals("/ccm/register/")) { if (refererURI.equals("/ccm/register/")) {
final String path = UI.getRootPageURL(req); final String path = UI.getRootPageURL(req);
throw new RedirectSignal(com.arsdigita.web.URL.there(req, path), true); throw new RedirectSignal(com.arsdigita.web.URL.there(req,
path),
true);
} }
throw new ReturnSignal(req, refererURI); throw new ReturnSignal(req, refererURI);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
@ -341,11 +355,12 @@ public class UserLoginForm extends Form
* onBadPassword, onAccountNotFound, onLoginException). * onBadPassword, onAccountNotFound, onLoginException).
* *
* @param event * @param event
*
* @throws FormProcessException if there is an unexpected login error * @throws FormProcessException if there is an unexpected login error
* *
*/ */
protected void loginUser(FormSectionEvent event) protected void loginUser(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
PageState state = event.getPageState(); PageState state = event.getPageState();
try { try {
@ -353,15 +368,15 @@ public class UserLoginForm extends Form
String username = null; String username = null;
if (Kernel.getConfig().emailIsPrimaryIdentifier()) { if (Kernel.getConfig().emailIsPrimaryIdentifier()) {
username = ((InternetAddress) m_loginName.getValue(state)). username = ((InternetAddress) m_loginName.getValue(state)).
getAddress(); getAddress();
} else { } else {
username = (String) m_loginName.getValue(state); username = (String) m_loginName.getValue(state);
} }
char[] password = ((String) m_password.getValue(state)).trim(). char[] password = ((String) m_password.getValue(state)).trim().
toCharArray(); toCharArray();
boolean forever = getPersistentLoginValue(event.getPageState(), boolean forever = getPersistentLoginValue(event.getPageState(),
false); false);
// attempt to log in user // attempt to log in user
ctx.login(username, password, forever); ctx.login(username, password, forever);
onLoginSuccess(event); onLoginSuccess(event);
@ -382,11 +397,12 @@ public class UserLoginForm extends Form
* Executed when login succeeds. Default implementation does nothing. * Executed when login succeeds. Default implementation does nothing.
* *
* @param event * @param event
*
* @throws com.arsdigita.bebop.FormProcessException * @throws com.arsdigita.bebop.FormProcessException
* *
*/ */
protected void onLoginSuccess(FormSectionEvent event) protected void onLoginSuccess(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
// do nothing // do nothing
} }
@ -394,11 +410,12 @@ public class UserLoginForm extends Form
* *
* @param event * @param event
* @param e * @param e
*
* @throws FormProcessException * @throws FormProcessException
*/ */
protected void onBadPassword(FormSectionEvent event, protected void onBadPassword(FormSectionEvent event,
FailedLoginException e) FailedLoginException e)
throws FormProcessException { throws FormProcessException {
onLoginFail(event, e); onLoginFail(event, e);
} }
@ -409,15 +426,16 @@ public class UserLoginForm extends Form
* *
* @param event * @param event
* @param e * @param e
*
* @throws com.arsdigita.bebop.FormProcessException * @throws com.arsdigita.bebop.FormProcessException
* *
*/ */
protected void onLoginFail(FormSectionEvent event, protected void onLoginFail(FormSectionEvent event,
LoginException e) LoginException e)
throws FormProcessException { throws FormProcessException {
s_log.debug("Login fail"); s_log.debug("Login fail");
event.getFormData().addError((String) ERROR_LOGIN_FAIL.localize(event. event.getFormData().addError((String) ERROR_LOGIN_FAIL.localize(event.
getPageState().getRequest())); getPageState().getRequest()));
} }
/** /**
@ -427,8 +445,8 @@ public class UserLoginForm extends Form
* *
*/ */
protected void onAccountNotFound(FormSectionEvent event, protected void onAccountNotFound(FormSectionEvent event,
AccountNotFoundException e) AccountNotFoundException e)
throws FormProcessException { throws FormProcessException {
PageState state = event.getPageState(); PageState state = event.getPageState();
// no such user, so bring up form for new users // no such user, so bring up form for new users
@ -449,8 +467,8 @@ public class UserLoginForm extends Form
* *
*/ */
protected void onLoginException(FormSectionEvent event, protected void onLoginException(FormSectionEvent event,
LoginException e) LoginException e)
throws FormProcessException { throws FormProcessException {
// unexpected error happened during login // unexpected error happened during login
s_log.error("Login failed", e); s_log.error("Login failed", e);
throw new FormProcessException(e); throw new FormProcessException(e);
@ -460,12 +478,13 @@ public class UserLoginForm extends Form
* Determines whether a persistent cookie is requested in the given form. * Determines whether a persistent cookie is requested in the given form.
* *
* @return true if the specified formdata has a field named * @return true if the specified formdata has a field named
* FORM_PERSISTENT_LOGIN_P whose value is equal to "1". If there is no such * FORM_PERSISTENT_LOGIN_P whose value is equal to "1". If there is
* field in the form data, returns the specified default value. * no such field in the form data, returns the specified default
* value.
* *
*/ */
protected boolean getPersistentLoginValue(PageState state, protected boolean getPersistentLoginValue(PageState state,
boolean defaultValue) { boolean defaultValue) {
// Problem: // Problem:
// getValue(state) returns an Object of type StringArray, if the // getValue(state) returns an Object of type StringArray, if the
// Checkbox is marked. // Checkbox is marked.
@ -502,15 +521,15 @@ public class UserLoginForm extends Form
ParameterMap map = new ParameterMap(); ParameterMap map = new ParameterMap();
map.setParameter(LoginHelper.RETURN_URL_PARAM_NAME, map.setParameter(LoginHelper.RETURN_URL_PARAM_NAME,
m_returnURL.getValue(state)); m_returnURL.getValue(state));
map.setParameter(FORM_PERSISTENT_LOGIN_P, map.setParameter(FORM_PERSISTENT_LOGIN_P,
m_isPersistent.getValue(state)); m_isPersistent.getValue(state));
map.setParameter(FORM_EMAIL, map.setParameter(FORM_EMAIL,
m_loginName.getValue(state)); m_loginName.getValue(state));
final URL dest = com.arsdigita.web.URL.there(state.getRequest(), final URL dest = com.arsdigita.web.URL.there(state.getRequest(),
url, url,
map); map);
throw new RedirectSignal(dest, true); throw new RedirectSignal(dest, true);