org.josso.gateway.signon.LoginAction.java Source code

Java tutorial

Introduction

Here is the source code for org.josso.gateway.signon.LoginAction.java

Source

/*
 * 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.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.*;
import org.josso.Lookup;
import org.josso.SecurityDomain;
import org.josso.auth.Credential;
import org.josso.auth.exceptions.AuthenticationFailureException;
import org.josso.gateway.SSOContext;
import org.josso.gateway.SSOGateway;
import org.josso.gateway.SSOWebConfiguration;
import org.josso.gateway.identity.SSOUser;
import org.josso.gateway.identity.SSORole;
import org.josso.gateway.assertion.AuthenticationAssertion;
import org.josso.gateway.session.SSOSession;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Base login action extended by concrete actions associated to specific authentication schemes.
 * This actions controls the auth. process and invokes subclasses methods (template method).
 *
 * @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a>
 * @version $Id: LoginAction.java 612 2008-08-22 12:17:20Z gbrigand $
 * @revision 07/05/2008 ajadzinsky
 */

public abstract class LoginAction extends SignonBaseAction {

    public static final String JOSSO_CMD_LOGIN = "login";

    private static final Log logger = LogFactory.getLog(LoginAction.class);

    /**
     * Executes the proper login method based on sso_command request parameter.
     */
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        if (logger.isDebugEnabled())
            logger.debug("JOSSO Command : [cmd=" + getSSOCmd(request) + "]");

        /**
         * SSO Context needs to resolve security domain
         */
        prepareContext(request);

        // Get current SSO Command ...
        String cmd = getSSOCmd(request);

        // Validate BACK TO URL to avoid XSR exploit
        String backTo = getBackTo(request);
        if (backTo != null) {

            backTo = backTo.toLowerCase();

            SecurityDomain domain = SSOContext.getCurrent().getSecurityDomain();
            SSOWebConfiguration cfg = domain.getSSOWebConfiguration();

            boolean trusted = false;
            if (cfg.getTrustedHosts().size() > 0) {
                String backToHost = null;
                if (backTo.startsWith("http://") || backTo.startsWith("https://")) {
                    try {
                        URL backToUrl = new URL(backTo);
                        backToHost = backToUrl.getHost();
                        backToHost = backToHost.substring(backToHost.lastIndexOf("@") + 1);
                    } catch (MalformedURLException e) {
                        if (logger.isDebugEnabled())
                            logger.debug("BackTo URL is malformed : [backTo=" + backTo + "]");
                    }
                }
                for (String trustedHost : cfg.getTrustedHosts()) {
                    if (StringUtils.isNotBlank(trustedHost) && trustedHost.equals(backToHost)) {
                        trusted = true;
                        break;
                    }
                }
            }

            if (!trusted && cfg.getTrustedHosts().size() > 0) {

                logger.warn("Attempt to use untrusted host in back_to URL " + backTo);

                response.setHeader("Cache-Control", "no-cache");
                response.setHeader("Pragma", "no-cache");
                response.setHeader("Expires", "0");

                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                // Add non-cache headers

                return null;
            }
        }

        if (canRelay(request))
            return relay(mapping, form, request, response);

        // If no command was specified, "ask-for-login" is the default value.
        if (cmd == null) {
            return askForLogin(mapping, form, request, response);
        }

        // All other commands mean "perform-login"
        return login(mapping, form, request, response);

    }

    /**
     * Ask the user for login information.
     */
    protected ActionForward askForLogin(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {

        try {

            // Ask user for login information.
            SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();

            String loginUrl = cfg.getCustomLoginURL();
            String backTo = getBackTo(request);

            if (loginUrl != null) {

                if (backTo != null) {
                    loginUrl += (loginUrl.indexOf("?") >= 0 ? "&" : "?") + "josso_back_to=" + backTo;
                }

                // The authentication interface is not the default ...
                if (logger.isDebugEnabled())
                    logger.debug("Redirecting to custom login : " + loginUrl);

                response.sendRedirect(response.encodeRedirectURL(loginUrl));
                return null; // No action forward needed, we

            }

            return mapping.findForward("login-page");

        } catch (Exception e) {
            if (this.onFatalError(e, request, response))
                return null;

            return mapping.findForward("error");
        }
    }

    /**
     * Logins the user in the SSO infrastructure
     */
    protected ActionForward login(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        try {

            SSOGateway gwy = getSSOGateway();

            Credential[] c = getCredentials(request);

            try {
                // 1 - Handle Outbound relaying by generating an assertion for the authentication request
                SSOContext ctx = SSOContext.getCurrent();
                AuthenticationAssertion authAssertion = gwy.assertIdentity(c, ctx.getScheme());

                String sessionId = authAssertion.getSSOSessionId();
                SSOSession session = gwy.findSession(sessionId);

                // Cookie ssoCookie = newJossoCookie(request.getContextPath(), session.getProcessId());
                // response.addCookie(ssoCookie);

                storeSSOInformation(request, response, session);

                if (logger.isDebugEnabled())
                    logger.debug("[login()], authentication successfull.");

                // 2 - Restore BACK TO URL ...
                String back_to = this.getBackTo(request, session, authAssertion);
                if (back_to == null) {

                    // Return to controller, if we do not have a back-to url, add more information to the context ;)
                    SSOUser user = gwy.findUserInSession(sessionId);
                    SSORole[] roles = gwy.findRolesByUsername(user.getName());

                    // so that pages can access this bean
                    request.setAttribute(KEY_JOSSO_SESSION, session);
                    request.setAttribute(KEY_JOSSO_USER, user);
                    request.setAttribute(KEY_JOSSO_USER_ROLES, roles);

                    return mapping.findForward("login-result");
                }

                // If authentication succeds, remove al SSO session data.
                this.clearSSOParameters(request);

                // We're going back to the partner app.
                if (logger.isDebugEnabled())
                    logger.debug("[login()], Redirecting user to : " + back_to);

                response.sendRedirect(response.encodeRedirectURL(back_to));

                return null; // No forward is needed, we perfomed a 'sendRedirect'.

            } catch (AuthenticationFailureException e) {

                if (logger.isDebugEnabled())
                    logger.debug("[AuthenticationFailureException] " + e.getMessage(), e);

                // logs the error
                ActionErrors errors = new ActionErrors();
                errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("sso.login.failed"));
                saveErrors(request, errors);

                // Invalid login attempt, redirect to ON ERROR URL, if any.
                boolean ok = this.onLoginAuthenticationException(e, request, response, c);
                if (ok) {
                    return null; // No forward is needed, we perfomed a 'sendRedirect'.
                }

                SSOWebConfiguration cfg = SSOContext.getCurrent().getSecurityDomain().getSSOWebConfiguration();
                if (cfg.isBasicAuthenticationEnabled()) {
                    return mapping.findForward("login-page");
                } else {
                    response.setHeader("Cache-Control", "no-cache");
                    response.setHeader("Pragma", "no-cache");
                    response.setHeader("Expires", "0");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    return null;
                }
            }

        } catch (Exception e) {
            if (this.onFatalError(e, request, response))
                return null;

            return mapping.findForward("error");
        }
    }

    /**
     * @param e           is the <AuthenticationFailureException> Exception to br handled
     * @param request     is the <HttpServletRequest> context
     * @param response    is the <HttpServletResponse> context
     * @param credentials contains the <Crdential> used to perform de authentication
     * @return false will execute the default mapping.findForward otherwise no action will be taken
     */
    protected boolean onLoginAuthenticationException(AuthenticationFailureException e, HttpServletRequest request,
            HttpServletResponse response, Credential[] credentials) throws IOException {

        String cmd = getSSOCmd(request);
        if (cmd != null && cmd.equals("login_optional")) {

            // Go back to agent ...
            String back_to = getBackTo(request);

            // We're going back to the partner app.
            if (logger.isDebugEnabled())
                logger.debug("[login()], Login Optional failed, redirecting user to : " + back_to);

            response.sendRedirect(response.encodeRedirectURL(back_to));
            return true; // We handled the redirect
        }

        String on_error = (String) request.getSession(true).getAttribute(KEY_JOSSO_ON_ERROR);

        if (on_error == null) {
            // Check for a configured custom login url
            try {
                SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
                if (cfg.isBasicAuthenticationEnabled()) {
                    on_error = cfg.getCustomLoginURL();
                }
            } catch (Exception ex) {
                logger.error(e.getMessage(), e);
            }
        }

        if (on_error != null) {

            // TODO : Improve error information handling, this could be managed with an outbound mechanism, like assertions.

            // Add error type and received username to ERROR URL.
            SSOGateway g = getSSOGateway();
            on_error += (on_error.indexOf("?") >= 0 ? "&" : "?") + "josso_error_type=" + e.getErrorType();
            try {
                SSOContext ctx = SSOContext.getCurrent();
                on_error += "&josso_username=" + g.getPrincipalName(ctx.getScheme(), credentials);
            } catch (Exception ex) {
                if (logger.isDebugEnabled())
                    logger.error("  [onLoginAuthenticationException()] cant find PrincipalName");
            }

            response.sendRedirect(response.encodeRedirectURL(on_error));
            if (logger.isDebugEnabled())
                logger.debug("[login()], authentication failure. Redirecting user to : " + on_error);

            return true;
        }

        return false;
    }

    /**
     * Relay using a previously opened and valid SSO session.
     */
    protected ActionForward relay(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {

        try {

            SSOGateway g = getSSOGateway();

            // 1 - Recover session and create a new assertion.
            SSOSession session = SSOContext.getCurrent().getSession();
            AuthenticationAssertion authAssertion = g.assertIdentity(session.getId());

            if (logger.isDebugEnabled())
                logger.debug("[relay()], authentication successfull.");

            // 2 - Restore BACK TO URL ...
            String back_to = this.getBackTo(request, session, authAssertion);
            if (back_to == null) {
                // Return to controller.
                return mapping.findForward("login-result");
            }

            this.clearSSOParameters(request);

            // We're going back to the partner app.
            if (logger.isDebugEnabled())
                logger.debug("[relay()], Redirecting user to : " + back_to);

            response.sendRedirect(response.encodeRedirectURL(back_to));

            return null; // No forward is needed, we perfomed a 'sendRedirect'.

        } catch (Exception e) {
            if (this.onFatalError(e, request, response))
                return null;

            return mapping.findForward("error");
        }
    }

    protected boolean onFatalError(Exception e, HttpServletRequest request, HttpServletResponse response) {
        // Fatal error ...
        logger.error(e.getMessage(), e);
        ActionErrors errors = new ActionErrors();
        errors.add(ActionErrors.GLOBAL_ERROR,
                new ActionError("sso.error", e.getMessage() != null ? e.getMessage() : e.toString()));
        saveErrors(request, errors);
        return false;
    }

    /**
     * Check if the request can be relayed to the requesting party without having to reauthenticate.
     *
     * @param request
     * @return true if a relay can be achieved or false in case a new authentication assertion must be issued.
     */
    protected boolean canRelay(HttpServletRequest request) {

        SSOSession s = SSOContext.getCurrent().getSession();
        return s != null && s.isValid();

        /*
        boolean canRelay = false;
            
            
        try {
            
            
            
        String jossoSessionId = getJossoSessionId(request);
        if  ( jossoSessionId != null ) {
            SSOSessionManager ssoSessionManager = Lookup.getInstance().lookupSecurityDomain().getSessionManager();
            SSOSession s = ssoSessionManager.getSession( jossoSessionId);
            if (s != null)
                canRelay = true;
        }
            
        } catch (NoSuchSessionException e) {
        // Ingore this error ....  we probably got an old SESSION id ...
        if (logger.isDebugEnabled())
            logger.debug(e.getMessage());
            
        } catch (Exception e) {
        logger.error(e.getMessage(), e);
        }
            
        return canRelay;
        */
    }

}