/*
* JOSSO: Java Open Single Sign-On
*
* Copyright 2004-2009, Atricore, Inc.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.josso.gateway.signon;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.josso.Lookup;
import org.josso.auth.Credential;
import org.josso.auth.scheme.AuthenticationScheme;
import org.josso.auth.scheme.RememberMeAuthScheme;
import org.josso.auth.exceptions.SSOAuthenticationException;
import org.josso.gateway.*;
import org.josso.gateway.Constants;
import org.josso.gateway.assertion.AuthenticationAssertion;
import org.josso.gateway.session.exceptions.NoSuchSessionException;
import org.josso.gateway.session.SSOSession;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
/**
* This is the base action for all signon actions.
*
* @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a>
* @version $Id: SignonBaseAction.java 612 2008-08-22 12:17:20Z gbrigand $
*/
public abstract class SignonBaseAction extends Action implements org.josso.gateway.signon.Constants {
private static final Log logger = LogFactory.getLog(SignonBaseAction.class);
// private static final Log logger = LogFactory.getLog(SignonBaseAction.class);
/**
* Gets current sso gateway.
*/
protected SSOGateway getSSOGateway() {
SSOGateway g = (SSOGateway) getServlet().getServletContext().getAttribute(KEY_JOSSO_GATEWAY);
if (g == null) {
try {
g = Lookup.getInstance().lookupSSOGateway();
getServlet().getServletContext().setAttribute(KEY_JOSSO_GATEWAY, g);
} catch (Exception e) {
logger.error("Cannot get Gateway instance " + e.getMessage(), e);
}
}
return g;
}
/**
* Gets the received SSO Command. If command is empty (""), returns null.
*/
protected String getSSOCmd(HttpServletRequest request) {
String cmd = request.getParameter(PARAM_JOSSO_CMD);
if ("".equals(cmd))
cmd = null;
return cmd;
}
/**
* This method knows how to build a SSO Context based on HTTP state: request, session, etc.
* Some state is stored as sesion attributes
*
* @see #storeSSOParameters(javax.servlet.http.HttpServletRequest)
*/
protected void prepareContext(HttpServletRequest request) throws SSOException, SSOAuthenticationException {
// We need to store SSO parameters
storeSSOParameters(request);
// Use gateway to select a security domain
SSOGateway gwy = getSSOGateway();
// The first thing to do is to create the context and publish the security domain !!!
MutableSSOContext ctx = (MutableSSOContext) gwy.prepareSSOContext(new SSORequestImpl(request));
ctx.setUserLocation(request.getRemoteHost());
// Store current SD name in session
request.getSession().setAttribute(org.josso.gateway.signon.Constants.KEY_JOSSO_SECURITY_DOMAIN_NAME, ctx.getSecurityDomain().getName());
if (logger.isDebugEnabled())
logger.debug("[prepareContext()] Storing security domain name in session [" + KEY_JOSSO_SECURITY_DOMAIN_NAME + "] : " +
ctx.getSecurityDomain().getName() + " (" + request.getSession().getId() + ")");
// SSO Session
String sessionId = getJossoSessionId(request);
if (sessionId != null && !"".equals(sessionId)) {
try {
// If session is not valid, no current session will be available in context.
ctx.setCurrentSession(gwy.findSession(sessionId));
} catch (NoSuchSessionException e) {
if (logger.isDebugEnabled())
logger.debug("NoSuchSessionException : " + sessionId + " " + e.getMessage());
}
}
// TODO : Detect Authentication scheme when user is already logged ...!
String scheme = getSchemeName(request);
logger.debug("Using authentication scheme : " + scheme);
ctx.setScheme(scheme);
}
/**
* This method stores SSO relevant request parameters as http session attributes.
*
* @param request
* @see #clearSSOParameters(javax.servlet.http.HttpServletRequest)
* @see #PARAM_JOSSO_BACK_TO
* @see #KEY_JOSSO_BACK_TO
* @see #PARAM_JOSSO_ON_ERROR
* @see #KEY_JOSSO_ON_ERROR
* @see #KEY_JOSSO_SECURITY_DOMAIN_NAME
*/
protected void storeSSOParameters(HttpServletRequest request) {
// Get a session
HttpSession s = request.getSession(true);
// Store back_to url, if present.
String back_to = request.getParameter(PARAM_JOSSO_BACK_TO);
if (back_to != null && !"".equals(back_to)) {
s.setAttribute(KEY_JOSSO_BACK_TO, back_to);
if (logger.isDebugEnabled())
logger.debug("[storeSSOParameters()] Storing back-to url in session [" + KEY_JOSSO_BACK_TO + "] : " + back_to + " (" + s.getId() + ")");
}
// Store on_error url if present.
String on_error = request.getParameter(PARAM_JOSSO_ON_ERROR);
if (on_error != null && !"".equals(on_error)) {
s.setAttribute(KEY_JOSSO_ON_ERROR, on_error);
if (logger.isDebugEnabled())
logger.debug("[storeSSOParameters()] Storing on-error url in session [" + KEY_JOSSO_ON_ERROR + "] : " + on_error + " (" + s.getId() + ")");
}
}
/**
* Clears SSO relevant attributes from http session.
*
* @see #storeSSOParameters(javax.servlet.http.HttpServletRequest)
*/
protected void clearSSOParameters(HttpServletRequest req) {
req.getSession().removeAttribute(KEY_JOSSO_BACK_TO);
if (logger.isDebugEnabled())
logger.debug("[clearSSOParameters()] Removing " + KEY_JOSSO_BACK_TO + " from session (" + req.getSession().getId() + ")");
req.getSession().removeAttribute(KEY_JOSSO_ON_ERROR);
if (logger.isDebugEnabled())
logger.debug("[clearSSOParameters()] Removing " + KEY_JOSSO_ON_ERROR + " from session (" + req.getSession().getId() + ")");
req.getSession().removeAttribute(KEY_JOSSO_SECURITY_DOMAIN_NAME);
if (logger.isDebugEnabled())
logger.debug("[clearSSOParameters()] Removing " + KEY_JOSSO_SECURITY_DOMAIN_NAME + " from session (" + req.getSession().getId() + ")");
}
protected String getBackTo(HttpServletRequest request,
SSOSession session,
AuthenticationAssertion authAssertion) {
HttpSession httpSession = request.getSession();
String back_to = (String) httpSession.getAttribute(KEY_JOSSO_BACK_TO);
if (back_to == null) {
try {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
if (logger.isDebugEnabled())
logger.debug(" No 'BACK TO' URL found in session " + httpSession.getId());
if (logger.isDebugEnabled())
logger.debug(" Using configured 'BACK TO' URL : " + cfg.getLoginBackToURL());
back_to = cfg.getLoginBackToURL();
} catch (Exception ex) {
if (logger.isDebugEnabled())
logger.debug(" [getBackTo()] cant find SSOWebConfiguration");
}
}
if (back_to == null) {
// No back to URL received or configured ... use configured success page.
logger.warn("No 'BACK TO' URL received or configured ... using default forward rule !");
// Return to controller.
return null;
}
back_to += (back_to.indexOf("?") >= 0 ? "&" : "?") + "josso_assertion_id=" + authAssertion.getId();
return back_to;
}
/**
* The 'back_to' url used when authentaction failed and the "" command was received
* @param request
* @return
*/
protected String getBackTo(HttpServletRequest request) {
HttpSession httpSession = request.getSession();
String back_to = (String) httpSession.getAttribute(KEY_JOSSO_BACK_TO);
if (back_to == null) {
try {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
if (logger.isDebugEnabled())
logger.debug(" No 'BACK TO' URL found in session " + httpSession.getId());
if (logger.isDebugEnabled())
logger.debug(" Using configured 'BACK TO' URL : " + cfg.getLoginBackToURL());
back_to = cfg.getLoginBackToURL();
} catch (Exception ex) {
if (logger.isDebugEnabled())
logger.debug(" [getBackTo()] cant find SSOWebConfiguration");
}
}
if (back_to == null) {
// No back to URL received or configured ... use configured success page.
logger.warn("No 'BACK TO' URL received or configured ... using default forward rule !");
// Return to controller.
return null;
}
return back_to;
}
protected Cookie getJossoCookie(HttpServletRequest request, String securityDomainName) {
Cookie[] cookies = request.getCookies();
if (cookies == null)
return null;
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals(JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + securityDomainName)) {
return cookie;
}
}
return null;
}
protected Cookie getCookie(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies == null)
return null;
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals(cookieName)) {
return cookie;
}
}
return null;
}
/**
* Gets the josso session id value
* <p/>
* participantparam request
*
* @return null, if JOSSO_SINGLE_SIGN_ON_COOKIE is not found in reqeust.
*/
protected String getJossoSessionId(HttpServletRequest request) {
SSOContext ctx = SSOContext.getCurrent();
String jossoSessionId = null;
try {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
if (cfg.isSessionTokenOnClient()) {
Cookie c = getJossoCookie(request, ctx.getSecurityDomain().getName());
if (c != null)
jossoSessionId = c.getValue();
} else {
HttpSession session = request.getSession();
return (String) session.getAttribute(JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName());
}
} catch (Exception ex) {
if (logger.isDebugEnabled())
logger.debug(" [getJossoSessionId()] cant find SSOWebConfiguration");
}
return jossoSessionId;
}
/**
* Stores session id
*
* @param request http request
* @param session SSO session instance
*/
protected void storeSSOInformation(HttpServletRequest request, HttpServletResponse response, SSOSession session) {
MutableSSOContext ctx = (MutableSSOContext) SSOContext.getCurrent();
ctx.setCurrentSession(session);
try {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
if (cfg.isSessionTokenOnClient()) {
logger.debug("Storing SSO Session ID on clinet");
Cookie ssoCookie = newJossoCookie(
request.getContextPath(),
JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName(),
session.getId());
response.addCookie(ssoCookie);
} else {
logger.debug("Storing SSO Session ID on server");
HttpSession hsession = request.getSession();
hsession.setAttribute(JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName(), session.getId());
}
logger.debug("Remember Me:" + request.getParameter(org.josso.gateway.signon.Constants.PARAM_JOSSO_REMEMBERME));
logger.debug("Command:" + request.getParameter(org.josso.gateway.signon.Constants.PARAM_JOSSO_CMD));
// Remember user authentication.
if (cfg.isRememberMeEnabled() && request.getParameter(org.josso.gateway.signon.Constants.PARAM_JOSSO_REMEMBERME) != null) {
// Storing remember me information (always on client)
logger.debug("Storing SSO Rememberme Token on Client");
String cipherSuite = (String) request.getAttribute
("javax.servlet.request.cipher_suite");
if (cipherSuite == null)
logger.error("SSL Required for 'remember me' feature");
// We need this auth scheme to build the proper token
// TODO : Check this when implementing the "Password Recovery" becauase it's a similar case. We will have to acces the password value from the store
RememberMeAuthScheme scheme = (RememberMeAuthScheme) ctx.getSecurityDomain().getAuthenticator().getAuthenticationScheme("rememberme-authentication");
String token = scheme.getRemembermeTokenForUser(session.getUsername());
// This will provide the credential string value ...
Cookie rememberMeCookie = new Cookie(JOSSO_REMEMBERME_TOKEN + "_" + ctx.getSecurityDomain().getName(), token);
// If max age was not specified, assume a year.
rememberMeCookie.setMaxAge(60 * (cfg.getRememberMeMaxAge() > 0 ? cfg.getRememberMeMaxAge() : 60 * 24 *365)); // The cookie will live for a year ...
rememberMeCookie.setPath("/");
if (cfg.isSessionTokenSecure()) {
rememberMeCookie.setSecure(true);
} else {
logger.error("Remember Me funcion requires SSL Transport!");
}
// Store cookie in response
response.addCookie(rememberMeCookie);
}
} catch (Exception ex) {
logger.error("Error while storing SSO Information : " + ex.getMessage(), ex);
}
}
protected void removeJossoSessionId(HttpServletRequest request, HttpServletResponse response) {
SSOContext ctx = SSOContext.getCurrent();
try {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
if (cfg.isSessionTokenOnClient()) {
Cookie ssoCookie = newJossoCookie(
request.getContextPath(),
JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName(),
"-");
ssoCookie.setMaxAge(0);
response.addCookie(ssoCookie);
} else {
HttpSession session = request.getSession();
session.removeAttribute(JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName());
}
if (cfg.isRememberMeEnabled()) {
// Clear the remember me cookie
Cookie rememberMeCookie = new Cookie(Constants.JOSSO_REMEMBERME_TOKEN + "_" + SSOContext.getCurrent().getSecurityDomain().getName(), "-");
rememberMeCookie.setMaxAge(0);
rememberMeCookie.setSecure(cfg.isSessionTokenSecure());
rememberMeCookie.setPath("/");
response.addCookie(rememberMeCookie);
}
} catch (Exception ex) {
if (logger.isDebugEnabled())
logger.debug(" [removeJossoSessionId()] cant find SSOWebConfiguration");
}
}
protected Cookie newJossoCookie(String path, String name, String value) throws Exception {
SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
Cookie ssoCookie = new Cookie(name, value);
ssoCookie.setMaxAge(-1);
if (cfg.isSessionTokenSecure()) {
ssoCookie.setSecure(true);
}
ssoCookie.setPath(path);
return ssoCookie;
// if (cfg.getSessionTokenScope() != null) {
// ssoCookie.setDomain(cfg.getSessionTokenScope());
// }
}
/**
* Subclasses should provide proper credentials based on specific authentication schemes.
*/
protected Credential[] getCredentials(HttpServletRequest request) throws SSOAuthenticationException {
return new Credential[0];
}
protected String getSchemeName(HttpServletRequest request) throws SSOAuthenticationException {
return "";
}
}