org.viafirma.cliente.openid.OpenIdHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.viafirma.cliente.openid.OpenIdHandler.java

Source

/* Copyright (C) 2007 Flix Garca Borrego (borrego at gmail.com)
      
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
      
   This library 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
   Library General Public License for more details.
      
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA 
 */
package org.viafirma.cliente.openid;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.openid4java.OpenIDException;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.InMemoryNonceVerifier;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.AxMessage;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.ax.FetchResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.viafirma.cliente.exception.CodigoError;
import org.viafirma.cliente.exception.InternalException;
import org.viafirma.cliente.util.TypeRequest;

/**
 * Contiene mtodos de utilidad. Nota: Este Cdigo esta basado en el cdigo del
 * proyecto Openid4Java:
 * http://openid4java.googlecode.com/svn/trunk/src/net/openid/consumer/SampleConsumer
 * 
 * @author Felix Garcia Borrego (borrego at gmail.com)
 */
public class OpenIdHandler {

    /**
     * Manejador del protocolo OpenId
     */
    ConsumerManager manager;

    private Log log = LogFactory.getLog(OpenIdHandler.class);

    /**
     * Inicia el proceso de autenticacin utilizando el protocolo OpenID.
     * 
     * @param urlId
     * @param requestProperties
     * @param request
     * @param response
     * @param uri,
     *            path relativo donde se recupera el resultado de la
     *            autenticacin.
     * @throws InternalException
     */
    public void autenticar(String urlId, Set<String> requestProperties, HttpServletRequest request,
            HttpServletResponse response) throws InternalException {
        String urlRetorno = generarUrlRetorno(request, null);

        // Iniciamos el proceso de autenticacin OpenId
        iniciarProtocoloOpenId(urlId, requestProperties, request, response, urlRetorno);
    }

    /**
     * Inicia el proceso de firmar utilizando el protocolo OpenID.
     * 
     * @param urlId
     * @param requestProperties
     * @param request
     * @param response
     * @param uriRetorno
     *            Url de retorno, cuando el proceso termine.
     * @throws InternalException
     */
    public void firmar(String urlId, Set<String> requestProperties, HttpServletRequest request,
            HttpServletResponse response, String uriRetorno) throws InternalException {
        String urlRetorno = generarUrlRetorno(request, uriRetorno);
        iniciarProtocoloOpenId(urlId, requestProperties, request, response, urlRetorno);
    }

    /**
     * Inicia el proceso de autenticacin utilizando el protocolo OpenID.
     * 
     * @param urlId
     *            url OpenId( utilizada)
     * @param requestProperties
     *            Conjunto de propiedades que se solicitaran
     * @param request
     * @param response
     * @param urlReturn
     *            url de retorno cuando el proceso termine.
     * @throws InternalException
     *             No se puede iniciar la autenticacin
     */
    @SuppressWarnings("unchecked")
    public void iniciarProtocoloOpenId(String urlId, Set<String> requestProperties, HttpServletRequest request,
            HttpServletResponse response, String returUrl) throws InternalException {
        String userSuppliedString = urlId;
        try {
            // descubrimos el punto de entrada para autenticar al usuario
            // esto se conecta a la url indicada, parsea el html retornado
            // en busca de la cabecera estandar OpenId
            // ej:<link rel="openid.server"
            // href="http://localhost:8080/viafirma/conectorOpenID" />
            List discoveries = manager.discover(userSuppliedString);

            // invoca a la url descubierta para asociarla a esta peticin
            // para realizar la autenticacin
            DiscoveryInformation discovered = manager.associate(discoveries);

            // almacena el resultado en sessin para recuperarlo en la fase de
            // validacin
            request.getSession().setAttribute(OPEN_ID_DISC, discovered);

            // Desactivamos la validacin RP. ( Necesaria para que funcione en
            // entornos no OpenID2 )
            manager.getRealmVerifier().setEnforceRpId(false);

            // obtengo el resultado de la autenticacin, esto redireccionaria el
            // navegador al usuario.
            // obtengo AuthRequest message y lo envia OpenID provider
            // indica la url de retorno a utilizar cuando el proceso de
            // autenticacin termine
            AuthRequest authReq = manager.authenticate(discovered, returUrl);

            // Aadimos los datos que deseamos solicitar( ej: email, nombre,
            // etc)
            addRequestRequired(requestProperties, authReq);

            if (!discovered.isVersion2()) {
                // Opcion 1: redireccionamos (http-redirect) al proveedor de
                // servicios OpenId
                // Es el nico mtodo soportado en OpenID 1.x limitado a ~2048
                // bytes

                // Redireccin
                String url = authReq.getDestinationUrl(true);
                response.sendRedirect(url);
            } else {
                // Option 2: HTML FORM Redirection (Allows payloads >2048 bytes)

                // RequestDispatcher dispatcher =
                // getServletContext().getRequestDispatcher("formredirection.jsp");
                // httpReq.setAttribute("prameterMap",
                // response.getParameterMap());
                // httpReq.setAttribute("destinationUrl",
                // response.getDestinationUrl(false));
                // dispatcher.forward(request, response);
                throw new UnsupportedOperationException(
                        "Operacin no soportada.Option 2: HTML FORM Redirection (Allows payloads >2048 bytes)");
            }

        } catch (DiscoveryException e) {
            log.warn(
                    "El servidor de autenticacin no esta disponible en este momento. No se ha podido localizar un servicio OpenId en la url solicitada "
                            + urlId + ". " + e.getMessage());
            throw new InternalException(CodigoError.ERROR_AUTENTICACION_NO_DISPONIBLE, "Url Solicitada " + urlId,
                    e);
        } catch (MessageException e) {
            log.warn("El mensaje contiene errores. " + e.getMessage());
            throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION, "Url Solicitada " + urlId, e);
        } catch (ConsumerException e) {
            log.warn("No se puede enviar la redireccin al usuario. " + e.getMessage());
            throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION,
                    "Url Solicitada " + urlId, e);
        } catch (IOException e) {
            log.debug("No se puede enviar la redireccin al usuario. " + e.getMessage());
            throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION,
                    "Url Solicitada " + urlId, e);
        }

    }

    /**
     * Procesa el resultado de la autenticacin para extraer los datos del
     * usuario.
     * 
     * @param httpReq
     * @param httpRes
     * @throws InternalException
     *             No se pueden recuperar los datos resultado de la
     *             autenticacin.
     */
    @SuppressWarnings("unchecked")
    public Map<String, String> processResponseAuthentication(HttpServletRequest httpReq,
            HttpServletResponse httpRes) throws InternalException {
        try {
            // Obtengo todos los parametros de la peticin
            ParameterList response = new ParameterList(httpReq.getParameterMap());

            // recupero los parametros almacenados en sessin
            DiscoveryInformation discovered = (DiscoveryInformation) httpReq.getSession()
                    .getAttribute(OPEN_ID_DISC);

            // Recuperamos la url de retorno esperada
            String urlRetorno = (String) httpReq.getSession().getAttribute(VARIABLE_URL_RETORNO_OPENID);
            log.debug("Url de retorno: " + urlRetorno);
            if (urlRetorno == null) {
                log.warn("No hay url de retorno. Probablemente problema con sesiones distintas en en navegador. "
                        + urlRetorno);
                throw new org.viafirma.cliente.exception.InternalException(
                        CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION,
                        "No hay url de retorno. Probablemente problema con sesiones distintas en en navegador. ");

            }

            // recupero la uri utilizada desde sessin.
            // extract the receiving URL from the HTTP request
            // para asegurarnos de que funcione correctamente detras de un
            // mod-proxy apache, modificamos la url del tipo:
            StringBuilder receivingURL = new StringBuilder(urlRetorno);// httpReq.getRequestURL();

            String queryString = httpReq.getQueryString();
            if (receivingURL.indexOf("?") != -1) {
                receivingURL.delete(receivingURL.indexOf("?"), receivingURL.length());
            }
            if (queryString != null && queryString.length() > 0) {
                receivingURL.append("?").append(httpReq.getQueryString());
            }

            // verify the response; ConsumerManager needs to be the same
            // (static) instance used to place the authentication request
            VerificationResult verification = manager.verify(receivingURL.toString(), response, discovered);

            // examine the verification result and extract the verified
            // identifier
            Identifier verified = verification.getVerifiedId();
            if (verified != null) {
                AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse();

                if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
                    FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);

                    // retorno todos los atributos recueperados
                    return fetchResp.getAttributes();
                } else {
                    // no tiene las extensiones requeridas.
                    return Collections.EMPTY_MAP;
                }
            } else {
                log.warn("No se puede verificar la autenticacin. " + receivingURL);
                throw new org.viafirma.cliente.exception.InternalException(
                        CodigoError.ERROR_AUTENTICACION_VERIFICACION, receivingURL.toString());
            }
        } catch (OpenIDException e) {
            throw new org.viafirma.cliente.exception.InternalException(CodigoError.ERROR_AUTENTICACION_VERIFICACION,
                    e.getMessage(), e);
        }

    }

    // ***************************************
    // Mtodos de utilidad
    // ***************************************

    /**
     * Aade una solicitud para cada propiedad solicitada.
     * 
     * Este mtodo determina que propiedades seran solicitadas a viafirma.
     * 
     * @param propiedadesSolicitadas
     * @param authReq
     * @throws MessageException
     */
    public void addRequestRequired(Set<String> propiedadesSolicitadas, AuthRequest authReq)
            throws MessageException {
        // nueva solicitud
        FetchRequest fetch = FetchRequest.createFetchRequest();
        // iteramos sobre los typos de datos disponibles
        for (TypeRequest tipo : TypeRequest.values()) {
            // si el tipo es solicitado
            if (propiedadesSolicitadas.contains(tipo.getAlias())) {
                fetch.addAttribute(tipo.getAlias(), tipo.getTypeUri(), true);
                log.debug("Aadida solicitud de " + tipo.getAlias());
            }
        }
        // aado a la peticin
        authReq.addExtension(fetch);
    }

    /**
     * Indica si la peticin contiene datos de retorno OpenID
     * 
     * @param request
     * @return
     */
    public boolean isResponseAuthentication(HttpServletRequest request) {
        if (request.getParameter("openid.ext1.mode") != null) {
            // hay datos retornados por viafirma
            return true;
        } else {
            return false;
        }
    }

    /**
     * Indica si la peticin es un retorno de autenticacin cancelada por el
     * usuario.
     * 
     * @param request
     * @return
     */
    public boolean isResponseCancel(HttpServletRequest request) {
        if (request.getParameter("openid.rpsig") != null && !isResponseAuthentication(request)) {
            // hay datos retornados por viafirma
            return true;
        } else {
            return false;
        }
    }

    /**
     * Inicializa un manejador OpenId.
     * 
     * @param urlAplicacin
     *            url pblica de la aplicacin.
     * @param urlFirmaReturnOpenId
     * @throws ConsumerException
     */
    OpenIdHandler(String urlAplicacion) throws ConsumerException {
        // creamos una instancia del manejador OpenId
        manager = new ConsumerManager();
        // A diferencia de la versin por defecto, damos 5 minutos al usuario
        // para que complete la autenticacin.
        // Por defecto son 60 segundos.
        manager.setNonceVerifier(new InMemoryNonceVerifier(60 * 5));

        this.urlAplicacion = urlAplicacion;
    }

    /**
     * Genera la url de retorno. Soluciona problemas causados por mod_proxys.
     * 
     * @param uriRetorno Si no se indica se genera utilizando el uri actual.
     * @param request
     * @return
     */
    private String generarUrlRetorno(HttpServletRequest request, String uriRetorno) {
        // Generamos la uri de la solicitud
        if (uriRetorno == null) {
            // antes de enviar a afirma cacheamos la url destino a la que se
            // dirigia el usuario para redireccioanrlo posteriormente
            uriRetorno = StringUtils.substringAfterLast(request.getRequestURI(), request.getContextPath());
            // le aadimos los parametros si los tiene.
            uriRetorno += request.getQueryString() == null ? "" : ("?" + request.getQueryString());
        }

        // si la url de retorno ha sido indicada en sessin, la utilizamos.
        String urlRetorno = urlAplicacion + uriRetorno;
        log.debug("Url de retorno: " + urlRetorno);
        // almacenamos en sessin la uri de retorno para recordarla al finalizar
        // la autenticacin
        request.getSession().setAttribute(VARIABLE_URL_RETORNO_OPENID, urlRetorno);
        return urlRetorno;
    }

    // ***************************************
    // Constantes relacionadas con OpenID
    // ***************************************

    // almacena el resultado en sessin para recuperarlo en la fase de
    // validacin
    public static String OPEN_ID_DISC = "openid-disc";

    /**
     * Url que pblica desde la que es visible la aplicacin.
     */
    private String urlAplicacion;

    /**
     * Url a la que retornar cuando el proceso de firma OpenId termine. El valor
     * de esta propiedad se recupera del parametro PARAM
     * URL_FIRMA_RETURN_OPEN_ID al inicializar el cliente.
     * 
     * private String urlFirmaReturnOpenId;
     */

    /**
     * Nombre de la variable de sessin en la que se almacena la url de retorno.
     */
    private String VARIABLE_URL_RETORNO_OPENID = "urlRetornoOpenID";

}