org.aselect.server.request.handler.openid.op.OpenID_RequestHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.aselect.server.request.handler.openid.op.OpenID_RequestHandler.java

Source

/*
 * * Copyright (c) Anoigo. All rights reserved.
 *
 * A-Select is a trademark registered by SURFnet bv.
 *
 * This program is distributed under the EUPL 1.0 (http://osor.eu/eupl)
 * See the included LICENSE file for details.
 *
 * If you did not receive a copy of the LICENSE
 * please contact Anoigo. (http://www.anoigo.nl) 
 */
package org.aselect.server.request.handler.openid.op;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

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

import org.apache.commons.lang.StringUtils;
import org.aselect.server.request.RequestState;
import org.aselect.server.request.handler.AbstractRequestHandler;
import org.aselect.server.request.handler.openid.Utils;
import org.aselect.system.error.Errors;
import org.aselect.system.exception.ASelectCommunicationException;
import org.aselect.system.exception.ASelectConfigException;
import org.aselect.system.exception.ASelectException;
import org.openid4java.association.AssociationException;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.DirectError;
import org.openid4java.message.Message;
import org.openid4java.message.ParameterList;
import org.openid4java.server.InMemoryServerAssociationStore;
import org.openid4java.server.ServerException;
import org.openid4java.server.ServerManager;

/**
 * OpenID RequestHandler. <br>
 * <br>
 * <b>Description:</b><br>
 * This class serves as a an OpenID Provider request handler 
 *  It handles authentication requests from Relying Parties
 * <code>AbstractAPIRequestHandler</code> creates an appropriate message creator. <br>
 * <br>
 * <b>Concurrency issues:</b> <br>
 * Use one <code>OpenID_RequestHandler</code> implementation for a single request. <br>
 * 
 * @author Remy Hanswijk
 */

public class OpenID_RequestHandler extends AbstractRequestHandler {
    private static final String SESSIONID_PREFIX = "OpenID_";
    private final static String MODULE = "OpenID_RequestHandler";
    private ServerManager serverManager; // Singleton per OpenID_RequestHandler
    private String opEndpointUrl = null;
    private String _sMyServerID = null;
    private String _sMyOrg = null;
    private String appID = null;
    private String sharedSecret = null;
    private String verifySignature = null;
    private String defaultUID = null;
    private String aselectServerURL = null;

    /* @param oServletConfig
     *            the o servlet config
     * @param oConfig
     *            the o config
     * @throws ASelectException
     *             the a select exception
     * @see org.aselect.server.request.handler.AbstractRequestHandler#init(javax.servlet.ServletConfig, java.lang.Object)
     */
    @Override
    public void init(ServletConfig oServletConfig, Object oConfig) throws ASelectException {
        String sMethod = "init";

        try {
            super.init(oServletConfig, oConfig);
            Object oASelect = null;
            try {
                oASelect = _configManager.getSection(null, "aselect");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "Could not find 'aselect' config section in config file", e);
                throw e;
            }

            try {
                _sMyServerID = _configManager.getParam(oASelect, "server_id");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "Could not retrieve 'server_id' config parameter in 'aselect' config section", e);
                throw e;
            }
            try {
                aselectServerURL = _configManager.getParam(oASelect, "redirect_url");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "Could not retrieve 'server_id' config parameter in 'redirect_url' config section", e);
                throw e;
            }

            try {
                _sMyOrg = _configManager.getParam(oASelect, "organization");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "Could not retrieve 'organization' config parameter in 'aselect' config section", e);
                throw e;
            }

            try {
                // RM_31_01
                opEndpointUrl = _configManager.getParam(oConfig, "opendpointurl");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "No config item 'opendpointurl' found", e);
                throw new ASelectException(Errors.ERROR_ASELECT_INIT_ERROR, e);
            }

            try {
                appID = _configManager.getParam(oConfig, "application");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "No config item 'application' found", e);
                throw new ASelectException(Errors.ERROR_ASELECT_INIT_ERROR, e);
            }

            try {
                sharedSecret = _configManager.getParam(oConfig, "shared_secret");
            } catch (ASelectConfigException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "No config item 'shared_secret' found", e);
                throw new ASelectException(Errors.ERROR_ASELECT_INIT_ERROR, e);
            }

            //         try {
            //            verifySignature = _configManager.getParam(oConfig, "verify_signature");
            //         }
            //         catch (ASelectConfigException e) {
            //            _systemLogger.log(Level.WARNING, MODULE, sMethod, "No config item 'verify_signature' found", e);
            //            throw new ASelectException(Errors.ERROR_ASELECT_INIT_ERROR, e);
            //         }

            try {
                defaultUID = _configManager.getParam(oConfig, "uid");
            } catch (ASelectConfigException e) {
                defaultUID = "siam_user";
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "No config item 'uid' found, using default (used for testing only):" + defaultUID);
                //            throw new ASelectException(Errors.ERROR_ASELECT_INIT_ERROR, e);
            }

            // Initialize ServerManager
            if (serverManager == null) {
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Initializing ServerManager");
                serverManager = new ServerManager();
                setOpEndpointUrl(opEndpointUrl);
                serverManager.setOPEndpointUrl(getOpEndpointUrl());
                serverManager.setPrivateAssociations(new InMemoryServerAssociationStore());
                serverManager.setSharedAssociations(new InMemoryServerAssociationStore());
            }
        } catch (ASelectException e) {
            throw e;
        } catch (Exception e) {
            _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Could not initialize", e);
            throw new ASelectException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e);
        }

    }

    /**
     * Process incoming request <br>
     * .
     * 
     * @param request
     *            HttpServletRequest.
     * @param response
     *            HttpServletResponse.
     * @return the request state
     * @throws ASelectException
     *             If processing of  data request fails.
     */

    public RequestState process(HttpServletRequest request, HttpServletResponse response) throws ASelectException {
        String sMethod = "process";
        //// maybe use "on the fly" endPointURL ?
        //         String handlerTarget = "/openidop_request";   // Don't forget to fill in the target
        //         String epu = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + handlerTarget;
        //         setOpEndpointUrl(epu);
        //         serverManager.setOPEndpointUrl(getOpEndpointUrl());
        /////

        ParameterList requestp = null;
        Message opResponse = null;
        String mode = null;

        Map reqParms = request.getParameterMap();
        if (reqParms.isEmpty()) {
            // assume discovery request...
            _systemLogger.log(Level.INFO, MODULE, sMethod, "Parameterlist is empty, assuming discovery request");

            try {
                String localID = request.getRequestURL().toString();
                localID = StringUtils.substringAfter(localID, getOpEndpointUrl());
                localID = localID.replaceFirst("^/", ""); // strip starting slash

                _systemLogger.log(Level.INFO, MODULE, sMethod, "localID:" + localID);
                Utils.sendDiscoveryResponse(request, response, createXrdsResponse(localID), _systemLogger);
            } catch (IOException e) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "Problem sending XRDS response");
                throw new ASelectCommunicationException("Problem sending XRDS response", e);
            }
        } else {
            requestp = new ParameterList(request.getParameterMap());
            mode = reqParms.containsKey("openid.mode") ? requestp.getParameterValue("openid.mode") : null;
        }

        if ("associate".equals(mode)) {
            // --- process an association request ---
            _systemLogger.log(Level.INFO, MODULE, sMethod, "Process an association request");
            opResponse = serverManager.associationResponse(requestp);
            Utils.logRequestParameters(requestp, _systemLogger);
            Utils.sendPlainTextResponse(request, response, opResponse, _systemLogger);
        } else if ("checkid_setup".equals(mode) || "checkid_immediate".equals(mode)) {
            _systemLogger.log(Level.INFO, MODULE, sMethod, "Process a checkid_setup or checkid_immediate request");
            Utils.logRequestParameters(requestp, _systemLogger);

            // RM_31_02, RM_31_03
            // authenticate to the aselect server
            String ridReqURL = aselectServerURL;
            String ridSharedSecret = sharedSecret;
            String ridAselectServer = _sMyServerID;
            String ridrequest = "authenticate";
            String ridAppURL = opEndpointUrl;

            //             String ridCheckSignature = verifySignature; 
            // maybe also forced_userid ?

            String claimedUID = requestp.getParameterValue("openid.claimed_id");
            String identity = requestp.getParameterValue("openid.identity");
            if (claimedUID == null && identity == null) {
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "return error, extensions not supported (yet) ");
                Utils.logRequestParameters(requestp, _systemLogger);
                opResponse = DirectError.createDirectError("Extensions not supported (yet)");
                Utils.sendPlainTextResponse(request, response, opResponse, _systemLogger);
            }
            String uid = null;
            if ("http://specs.openid.net/auth/2.0/identifier_select".equals(identity)) {
                // RM_31_04
                // This should trigger a username input in  aselect
                // Either by presenting a choice or an input box
                // We let aselectserver handle this
                uid = defaultUID;
            } else {
                String localID = (identity == null) ? "" : identity;
                localID = StringUtils.substringAfter(localID, getOpEndpointUrl());
                localID = localID.replaceFirst("^/", ""); // strip starting slash
                uid = localID;
            }

            String ridResponse = "";
            // Send data 
            BufferedReader in = null;
            try {
                //Construct request data 
                String ridURL = ridReqURL + "?" + "shared_secret=" + URLEncoder.encode(ridSharedSecret, "UTF-8")
                        + "&a-select-server=" + URLEncoder.encode(ridAselectServer, "UTF-8") + "&request="
                        + URLEncoder.encode(ridrequest, "UTF-8") + "&uid=" + URLEncoder.encode(uid, "UTF-8") +
                        // RM_31_05
                        "&app_url=" + URLEncoder.encode(ridAppURL, "UTF-8") +
                        //                         "&check-signature=" + URLEncoder.encode(ridCheckSignature, "UTF-8") +
                        "&app_id=" + URLEncoder.encode(appID, "UTF-8");
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Requesting rid through: " + ridURL);

                URL url = new URL(ridURL);
                in = new BufferedReader(new InputStreamReader(url.openStream()));

                String inputLine = null;
                while ((inputLine = in.readLine()) != null) {
                    ridResponse += inputLine;
                }
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Requesting rid response: " + ridResponse);
            } catch (Exception e) {
                _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                        "Could not retrieve rid from aselectserver: " + ridAselectServer);
                throw new ASelectCommunicationException(Errors.ERROR_ASELECT_IO, e);
            } finally {
                if (in != null)
                    try {
                        in.close();
                    } catch (IOException e) {
                        _systemLogger.log(Level.WARNING, MODULE, sMethod,
                                "Could not close stream to aselectserver : " + ridAselectServer);
                    }
            }

            //out.println("<br/>ridResponse=" + ridResponse); 
            String extractedRid = ridResponse.replaceFirst(".*rid=([^&]*).*$", "$1");
            _systemLogger.log(Level.INFO, MODULE, sMethod, "rid retrieved: " + extractedRid);

            String sessionID = SESSIONID_PREFIX + extractedRid;
            _systemLogger.log(Level.INFO, MODULE, sMethod, "Storing requestparameters with id: " + sessionID);
            Utils.logRequestParameters(requestp, _systemLogger);

            HashMap<String, Object> htSessionContext = new HashMap<String, Object>();
            htSessionContext.put("openid_requestp", requestp);
            // 20120404, Bauke replaced: _oSessionManager.updateSession(sessionID, htSessionContext);
            _oSessionManager.createSession(sessionID, htSessionContext, true/*start paused*/); // new session with predefined RID

            String loginrequest = "login1";

            //Construct request data 
            String redirectURL = null;
            try {
                redirectURL = ridReqURL + "?" + "request=" + URLEncoder.encode(loginrequest, "UTF-8")
                        + "&a-select-server=" + URLEncoder.encode(ridAselectServer, "UTF-8") + "&rid="
                        + extractedRid;
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Requesting login through redirect with redirectURL: " + redirectURL);

                response.sendRedirect(redirectURL);
            } catch (UnsupportedEncodingException e1) {
                _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                        "Could not URLEncode to UTF-8, this should not happen!");
                throw new ASelectCommunicationException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e1);
            } catch (IOException e) {
                _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Could not redirect to: " + redirectURL);
                throw new ASelectCommunicationException(Errors.ERROR_ASELECT_IO, e);
            }

        } else if ("check_authentication".equals(mode)) {
            // --- processing a verification request ---
            _systemLogger.log(Level.INFO, MODULE, sMethod, "Process a check_authentication request");
            Utils.logRequestParameters(requestp, _systemLogger);
            opResponse = serverManager.verify(requestp);
            Utils.sendPlainTextResponse(request, response, opResponse, _systemLogger);
        } else {
            // This should be the aselectserver response
            if (request.getParameter("aselect_credentials") != null) {
                // handle the aselectserver response
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Handle the aselectserver response");

                // This could also be done by getting request parametermap
                String queryData = request.getQueryString();
                String extractedAselect_credentials = queryData.replaceFirst(".*aselect_credentials=([^&]*).*$",
                        "$1");
                String extractedRid = queryData.replaceFirst(".*rid=([^&]*).*$", "$1");
                String finalReqURL = aselectServerURL;
                String finalReqSharedSecret = sharedSecret;
                String finalReqAselectServer = _sMyServerID;
                String finalReqrequest = "verify_credentials";
                //                String ridCheckSignature = verifySignature; // this does not help for verify_credentials if <applications>
                // in aselect.xml has require_signing="true"

                //Construct request data
                // RM_31_06
                String finalRequestURL = null;
                try {
                    finalRequestURL = finalReqURL + "?" + "shared_secret="
                            + URLEncoder.encode(finalReqSharedSecret, "UTF-8") + "&a-select-server="
                            + URLEncoder.encode(finalReqAselectServer, "UTF-8") + "&request="
                            + URLEncoder.encode(finalReqrequest, "UTF-8") + "&aselect_credentials="
                            + extractedAselect_credentials +
                            //                        "&check-signature=" + URLEncoder.encode(ridCheckSignature, "UTF-8") +
                            "&rid=" + extractedRid;

                } catch (UnsupportedEncodingException e3) {
                    _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                            "Could not URLEncode to UTF-8, this should not happen!");
                    throw new ASelectCommunicationException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e3);
                }
                String finalResult = "";

                //Send data
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Retrieving attributes through: " + finalRequestURL);

                BufferedReader in = null;
                try {
                    URL url = new URL(finalRequestURL);

                    in = new BufferedReader(new InputStreamReader(url.openStream()));

                    String inputLine = null;
                    while ((inputLine = in.readLine()) != null) {
                        finalResult += inputLine;
                    }
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "Retrieved attributes in: " + finalResult);

                } catch (Exception e) {
                    _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                            "Could not retrieve attributes from aselectserver: " + finalReqAselectServer);
                    throw new ASelectCommunicationException(Errors.ERROR_ASELECT_IO, e);
                } finally {
                    if (in != null)
                        try {
                            in.close();
                        } catch (IOException e) {
                            _systemLogger.log(Level.WARNING, MODULE, sMethod,
                                    "Could not close stream to aselectserver : " + finalReqAselectServer);
                        }
                }

                String extractedAttributes = finalResult.replaceFirst(".*attributes=([^&]*).*$", "$1");
                String extractedResultCode = finalResult.replaceFirst(".*result_code=([^&]*).*$", "$1");

                String urlDecodedAttributes = null;
                ;
                String decodedAttributes = null;
                try {
                    urlDecodedAttributes = URLDecoder.decode(extractedAttributes, "UTF-8");
                    decodedAttributes = URLDecoder.decode(new String(
                            org.apache.commons.codec.binary.Base64.decodeBase64(urlDecodedAttributes.getBytes())),
                            "UTF-8");
                    String attribs[] = decodedAttributes.split("&");
                    for (int i = 0; i < attribs.length; i++) {
                        _systemLogger.log(Level.INFO, MODULE, sMethod,
                                "Retrieved attribute from aselectserver: " + attribs[i]);
                    }

                } catch (UnsupportedEncodingException e2) {
                    _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                            "Could not URLDecode from UTF-8, this should not happen!");
                    throw new ASelectCommunicationException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e2);
                }

                String sessionID = SESSIONID_PREFIX + extractedRid;
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Retrieving requestparameters with sessionID: " + sessionID);

                HashMap<String, Object> htSessionContext = (HashMap<String, Object>) _oSessionManager
                        .get(sessionID);
                requestp = (ParameterList) htSessionContext.get("openid_requestp");

                Utils.logRequestParameters(requestp, _systemLogger);
                // RM_31_07
                //                _oSessionManager.killSession(sessionID);

                String userSelectedId = null;
                String userSelectedClaimedId = null;
                userSelectedId = getOpEndpointUrl() + "/" + finalResult.replaceFirst(".*uid=([^&]*).*$", "$1");
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Retrieved uid from aselect query string (userSelectedId): " + userSelectedId);
                userSelectedClaimedId = getOpEndpointUrl() + "/"
                        + decodedAttributes.replaceFirst(".*uid=([^&]*).*$", "$1");
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Retrieved uid from aselect attributes  (userSelectedClaimedId): " + userSelectedClaimedId);

                Boolean authenticatedAndApproved = false;
                try {
                    authenticatedAndApproved = Boolean.valueOf(Integer.parseInt(extractedResultCode) == 0);
                } catch (NumberFormatException nfe) {
                    _systemLogger.log(Level.WARNING, MODULE, sMethod,
                            "Resultcode from aselectserver was non-numeric: " + extractedResultCode);
                }

                // --- process an authentication request ---
                // RM_31_08
                opResponse = serverManager.authResponse(requestp, userSelectedId, userSelectedClaimedId,
                        authenticatedAndApproved.booleanValue());
                if (authenticatedAndApproved.booleanValue()) {
                    try {
                        serverManager.sign((AuthSuccess) opResponse);
                        _systemLogger.log(Level.INFO, MODULE, sMethod, "Response signed");
                    } catch (ServerException e1) {
                        _systemLogger.log(Level.WARNING, MODULE, sMethod,
                                "Unable to sign response, corresponding association cannot be found in store");
                    } catch (AssociationException e1) {
                        _systemLogger.log(Level.WARNING, MODULE, sMethod,
                                "Unable to sign response, signature cannot be computed");
                    }
                }

                // caller will need to decide which of the following to use:
                // - GET HTTP-redirect to the return_to URL
                // - HTML FORM Redirection

                String sRedirectUrl = opResponse.getDestinationUrl(true);
                // FOR GET-redirect
                _systemLogger.log(Level.INFO, MODULE, sMethod, "REDIR " + sRedirectUrl);
                try {
                    response.sendRedirect(sRedirectUrl);
                } catch (IOException e) {
                    StringBuffer sbWarning = new StringBuffer("Failed to redirect user to: ");
                    sbWarning.append(sRedirectUrl);
                    _systemLogger.log(Level.WARNING, MODULE, sMethod, sbWarning.toString());
                    throw new ASelectCommunicationException(Errors.ERROR_ASELECT_IO, e);
                }

                // FOR FORM POST, set up form and...
                //      responseText = opResponse.wwwFormEncoding();
                //      String sRedirectUrl = opResponse.getDestinationUrl(false); 
                //      redirect(opResponse.getDestinationUrl(false));

            } else {
                // oops, it was not an aselectserver response
                // --- error response ---
                _systemLogger.log(Level.WARNING, MODULE, sMethod, "Unknown request: " + mode);
                Utils.logRequestParameters(requestp, _systemLogger);
                opResponse = DirectError.createDirectError("Unknown request");
                Utils.sendPlainTextResponse(request, response, opResponse, _systemLogger);
            }
        }
        return null;
    }

    public void destroy() {
    }

    public synchronized String getOpEndpointUrl() {
        return opEndpointUrl;
    }

    public synchronized void setOpEndpointUrl(String opEndpointUrl) {
        this.opEndpointUrl = opEndpointUrl;
    }

    /**
     * @return
     */
    public String createXrdsResponse() {
        return createXrdsResponse(null);
    }

    /**
     * @param the
     *            (optional) localID to return in the XRDS
     * @return
     */
    public String createXrdsResponse(String localID) {
        String sMethod = "createXrdsResponse";

        _systemLogger.log(Level.INFO, MODULE, sMethod, "BEGIN");

        org.aselect.server.request.handler.openid.XrdsDocumentBuilder documentBuilder = new org.aselect.server.request.handler.openid.XrdsDocumentBuilder();
        if (localID == null || "".equals(localID)) {
            documentBuilder.addServiceElement("http://specs.openid.net/auth/2.0/server",
                    serverManager.getOPEndpointUrl(), "0");
            documentBuilder.addServiceElement("http://specs.openid.net/auth/2.0/signon",
                    serverManager.getOPEndpointUrl(), "10");
        } else {
            documentBuilder.addServiceElement("http://specs.openid.net/auth/2.0/signon",
                    serverManager.getOPEndpointUrl(), "10", localID);
        }

        // next lines needed when we implement extensions
        // documentBuilder.addServiceElement(AxMessage.OPENID_NS_AX,
        // serverManager.getOPEndpointUrl(), "30");
        // documentBuilder.addServiceElement(SRegMessage.OPENID_NS_SREG,
        // serverManager.getOPEndpointUrl(), "40");
        String xmlString = documentBuilder.toXmlString();
        _systemLogger.log(Level.INFO, MODULE, sMethod, "created XrdsResponse: " + xmlString);
        return xmlString;
    }
}