be.fedict.eid.idp.sp.protocol.openid.AuthenticationRequestServlet.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.idp.sp.protocol.openid.AuthenticationRequestServlet.java

Source

/*
 * eID Identity Provider Project.
 * Copyright (C) 2010-2013 FedICT.
 * Copyright (C) 2014 e-Contract.be BVBA.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * 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, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.idp.sp.protocol.openid;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.openid4java.OpenIDException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.discovery.Discovery;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.html.HtmlResolver;
import org.openid4java.discovery.xri.XriResolver;
import org.openid4java.discovery.yadis.YadisResolver;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.server.IncrementalNonceGenerator;
import org.openid4java.server.NonceGenerator;
import org.openid4java.server.RealmVerifierFactory;
import org.openid4java.util.HttpFetcherFactory;

import be.fedict.eid.idp.common.OpenIDAXConstants;
import be.fedict.eid.idp.common.ServiceLocator;
import be.fedict.eid.idp.sp.protocol.openid.spi.AuthenticationRequestService;

/**
 * Generates and sends out a OpenID Authentication Request.
 * <p/>
 * <p/>
 * Configuration can be provided either by providing:
 * <ul>
 * <li><tt>AuthenticationRequestService</tt>:
 * {@link AuthenticationRequestService} to provide the IdP protocol entry point,
 * SP response handling location, optional SSL certificate to trust, optional
 * list of preferred languages</li>
 * </ul>
 * or by provinding:
 * <ul>
 * <li><tt>SPDestination</tt> or <tt>SPDestinationPage</tt>: Service Provider
 * destination that will handle the returned SAML2 response. One of the 2
 * parameters needs to be specified.</li>
 * <li><tt>IdPDestination</tt>: SAML2 entry point of the eID IdP.</li>
 * <li><tt>TrustServer</tt>: optional boolean whether any SSL certificate is
 * regarded trusted.</li>
 * <li><tt>Language</tt>: optional comma-seperated list of preferred languages
 * to display the eID IdP webapp in (e.g.: "en,nl,fr"). If not specified, the
 * browsers's locale will be used.</li>
 * </ul>
 * 
 * @author Frank Cornelis
 */
public class AuthenticationRequestServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final Log LOG = LogFactory.getLog(AuthenticationRequestServlet.class);

    private static final String AUTHN_REQUEST_SERVICE_PARAM = "AuthenticationRequestService";
    private static final String USER_IDENTIFIER_PARAM = "UserIdentifier";
    private static final String SP_DESTINATION_PARAM = "SPDestination";
    private static final String SP_DESTINATION_PAGE_PARAM = SP_DESTINATION_PARAM + "Page";
    private static final String LANGUAGES_PARAM = "Language";

    private static final String TRUST_SERVER_PARAM = "TrustServer";

    public static final String CONSUMER_MANAGER_ATTRIBUTE = AuthenticationRequestServlet.class.getName()
            + ".ConsumerManager";

    public static final String RETURN_TO_SESSION_ATTRIBUTE = AuthenticationRequestServlet.class.getName()
            + ".ReturnToNonce";
    public static final String RETURN_TO_NONCE_PARAM = "janrain_nonce";

    private String userIdentifier;
    private String spDestination;
    private String spDestinationPage;
    private String languages;

    private ServiceLocator<AuthenticationRequestService> authenticationRequestServiceLocator;

    private ConsumerManager consumerManager;

    private boolean trustServer;

    /**
     * {@inheritDoc}
     */
    @Override
    public void init(ServletConfig config) throws ServletException {

        this.userIdentifier = config.getInitParameter(USER_IDENTIFIER_PARAM);
        this.spDestination = config.getInitParameter(SP_DESTINATION_PARAM);
        this.spDestinationPage = config.getInitParameter(SP_DESTINATION_PAGE_PARAM);
        this.languages = config.getInitParameter(LANGUAGES_PARAM);
        this.authenticationRequestServiceLocator = new ServiceLocator<AuthenticationRequestService>(
                AUTHN_REQUEST_SERVICE_PARAM, config);

        // validate necessary configuration params
        if (null == this.userIdentifier && !this.authenticationRequestServiceLocator.isConfigured()) {
            throw new ServletException("need to provide either " + USER_IDENTIFIER_PARAM + " or "
                    + AUTHN_REQUEST_SERVICE_PARAM + "(Class) init-params");
        }

        if (null == this.spDestination && null == this.spDestinationPage
                && !this.authenticationRequestServiceLocator.isConfigured()) {
            throw new ServletException("need to provide either " + SP_DESTINATION_PARAM + " or "
                    + SP_DESTINATION_PAGE_PARAM + " or " + AUTHN_REQUEST_SERVICE_PARAM + "(Class) init-param");
        }

        // SSL configuration
        String trustServer = config.getInitParameter(TRUST_SERVER_PARAM);
        if (null != trustServer) {
            this.trustServer = Boolean.parseBoolean(trustServer);
        }
        X509Certificate serverCertificate = null;
        if (this.authenticationRequestServiceLocator.isConfigured()) {
            AuthenticationRequestService service = this.authenticationRequestServiceLocator.locateService();
            serverCertificate = service.getServerCertificate();
        }

        if (this.trustServer) {

            LOG.warn("Trusting all SSL server certificates!");
            try {
                OpenIDSSLSocketFactory.installAllTrusted();
            } catch (Exception e) {
                throw new ServletException("could not install OpenID SSL Socket Factory: " + e.getMessage(), e);
            }
        } else if (null != serverCertificate) {

            LOG.info("Trusting specified SSL certificate: " + serverCertificate);
            try {
                OpenIDSSLSocketFactory.install(serverCertificate);
            } catch (Exception e) {
                throw new ServletException("could not install OpenID SSL Socket Factory: " + e.getMessage(), e);
            }
        }

        ServletContext servletContext = config.getServletContext();
        this.consumerManager = (ConsumerManager) servletContext.getAttribute(CONSUMER_MANAGER_ATTRIBUTE);

        if (null == this.consumerManager) {
            try {
                if (this.trustServer || null != serverCertificate) {

                    TrustManager trustManager;
                    if (this.trustServer) {
                        trustManager = new OpenIDTrustManager();
                    } else {
                        trustManager = new OpenIDTrustManager(serverCertificate);
                    }

                    SSLContext sslContext = SSLContext.getInstance("SSL");
                    TrustManager[] trustManagers = { trustManager };
                    sslContext.init(null, trustManagers, null);
                    HttpFetcherFactory httpFetcherFactory = new HttpFetcherFactory(sslContext,
                            SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                    YadisResolver yadisResolver = new YadisResolver(httpFetcherFactory);
                    RealmVerifierFactory realmFactory = new RealmVerifierFactory(yadisResolver);
                    HtmlResolver htmlResolver = new HtmlResolver(httpFetcherFactory);
                    XriResolver xriResolver = Discovery.getXriResolver();
                    Discovery discovery = new Discovery(htmlResolver, yadisResolver, xriResolver);
                    this.consumerManager = new ConsumerManager(realmFactory, discovery, httpFetcherFactory);

                } else {
                    this.consumerManager = new ConsumerManager();
                }
            } catch (Exception e) {
                throw new ServletException("could not init OpenID ConsumerManager");
            }
            servletContext.setAttribute(CONSUMER_MANAGER_ATTRIBUTE, this.consumerManager);
        }
    }

    /**
     * Used by the {@link AuthenticationResponseServlet} for processing the
     * returned OpenID response
     * 
     * @param request
     *            HTTP Servlet Request, used to get the OpenID
     *            {@link ConsumerManager} from the {@link ServletContext}
     * @return the OpenID {@link ConsumerManager}
     */
    public static ConsumerManager getConsumerManager(HttpServletRequest request) {
        HttpSession httpSession = request.getSession();
        ServletContext servletContext = httpSession.getServletContext();
        ConsumerManager consumerManager = (ConsumerManager) servletContext.getAttribute(CONSUMER_MANAGER_ATTRIBUTE);
        if (null == consumerManager) {
            throw new IllegalStateException("no ConsumerManager found in ServletContext");
        }
        return consumerManager;
    }

    private String getReturnTo(String spDestination, HttpSession httpSession) throws UnsupportedEncodingException {

        // generate nonce for protection against CSRF
        NonceGenerator _consumerNonceGenerator = new IncrementalNonceGenerator();
        String nonce = _consumerNonceGenerator.next();

        // add to "return_to"
        String returnTo = spDestination;
        returnTo += (returnTo.indexOf('?') != -1) ? '&' : '?';
        returnTo += RETURN_TO_NONCE_PARAM + "=" + URLEncoder.encode(nonce, "UTF-8");

        // store return_to on session for response validation
        httpSession.setAttribute(RETURN_TO_SESSION_ATTRIBUTE, returnTo);

        return returnTo;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String spDestination;
        String userIdentifier;
        String languages;

        AuthenticationRequestService service = this.authenticationRequestServiceLocator.locateService();
        if (null != service) {
            userIdentifier = service.getUserIdentifier();
            spDestination = service.getSPDestination();
            languages = service.getPreferredLanguages();
        } else {
            userIdentifier = this.userIdentifier;
            if (null != this.spDestination) {
                spDestination = this.spDestination;
            } else {
                spDestination = request.getScheme() + "://" + request.getServerName() + ":"
                        + request.getServerPort() + request.getContextPath() + this.spDestinationPage;
            }
            languages = this.languages;
        }

        try {
            LOG.debug("discovering the identity...");
            LOG.debug("user identifier: " + userIdentifier);
            List discoveries = this.consumerManager.discover(userIdentifier);
            LOG.debug("associating with the IdP...");
            DiscoveryInformation discovered = this.consumerManager.associate(discoveries);
            request.getSession().setAttribute("openid-disc", discovered);

            LOG.debug("SP destination: " + spDestination);

            AuthRequest authRequest = this.consumerManager.authenticate(discovered,
                    getReturnTo(spDestination, request.getSession()), spDestination);

            /*
             * We also piggy-back an attribute fetch request.
             */
            FetchRequest fetchRequest = FetchRequest.createFetchRequest();

            // required attributes
            fetchRequest.addAttribute(OpenIDAXConstants.AX_FIRST_NAME_PERSON_TYPE, true);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_LAST_NAME_PERSON_TYPE, true);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_NAME_PERSON_TYPE, true);

            // optional attributes
            fetchRequest.addAttribute(OpenIDAXConstants.AX_GENDER_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_POSTAL_CODE_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_POSTAL_ADDRESS_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_CITY_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_NATIONALITY_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_PLACE_OF_BIRTH_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_BIRTHDATE_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_CARD_NUMBER_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_CARD_VALIDITY_BEGIN_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_CARD_VALIDITY_END_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_PHOTO_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_RRN_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_CERT_AUTHN_TYPE, false);
            fetchRequest.addAttribute(OpenIDAXConstants.AX_AGE_TYPE, false);

            authRequest.addExtension(fetchRequest, "ax");

            /*
             * Piggy back UI Extension if any languages were specified
             */
            if (null != languages) {
                UserInterfaceMessage uiMessage = new UserInterfaceMessage();
                uiMessage.setLanguages(languages);
                authRequest.addExtension(uiMessage, "ui");
            }

            LOG.debug("redirecting to producer with authn request...");
            response.sendRedirect(authRequest.getDestinationUrl(true));
        } catch (OpenIDException e) {
            throw new ServletException("OpenID error: " + e.getMessage(), e);
        }
    }
}