fr.univlille2.ecm.platform.ui.web.auth.cas2.Cas2Authenticator.java Source code

Java tutorial

Introduction

Here is the source code for fr.univlille2.ecm.platform.ui.web.auth.cas2.Cas2Authenticator.java

Source

/*
 * (C) Copyright 2006-2009 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * 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
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     Nuxeo - initial API and implementation
 *     Academie de Rennes - proxy CAS support
 *
 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
 */

package fr.univlille2.ecm.platform.ui.web.auth.cas2;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.common.utils.URIUtils;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.repository.Repository;
import org.nuxeo.ecm.core.api.repository.RepositoryManager;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.LoginResponseHandler;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension;
import org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService;
import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
import org.nuxeo.runtime.api.Framework;
import org.xml.sax.SAXException;

import edu.yale.its.tp.cas.client.ProxyTicketValidator;
import edu.yale.its.tp.cas.client.ServiceTicketValidator;

/**
 * @author Thierry Delprat
 * @author Olivier Adam
 * @author M.-A. Darche
 * @author Benjamin Jalon
 * @author Thierry Martins
 * @author acordier
 */

public class Cas2Authenticator
        implements NuxeoAuthenticationPlugin, NuxeoAuthenticationPluginLogoutExtension, LoginResponseHandler {

    protected static final String CAS_SERVER_HEADER_KEY = "CasServer";

    protected static final String CAS_SERVER_PATTERN_KEY = "$CASSERVER";

    protected static final String NUXEO_SERVER_PATTERN_KEY = "$NUXEO";

    protected static final String LOGIN_ACTION = "Login";

    protected static final String LOGOUT_ACTION = "Logout";

    protected static final String VALIDATE_ACTION = "Valid";

    protected static final String PROXY_VALIDATE_ACTION = "ProxyValid";

    protected static final Log log = LogFactory.getLog(Cas2Authenticator.class);

    protected static final String EXCLUDE_PROMPT_KEY = "excludePromptURL";

    protected String ticketKey = "ticket";

    protected String proxyKey = "proxy";

    protected String appURL = "http://127.0.0.1:8080/nuxeo/";

    protected String serviceLoginURL = "http://127.0.0.1:8080/cas/login";

    protected String serviceValidateURL = "http://127.0.0.1:8080/cas/serviceValidate";

    /**
     * We tell the CAS server whether we want a plain text (CAS 1.0) or XML (CAS
     * 2.0) response by making the request either to the '.../validate' or
     * '.../serviceValidate' URL. The older protocol supports only the CAS 1.0
     * functionality, which is left around as the legacy '.../validate' URL.
     */
    protected String proxyValidateURL = "http://127.0.0.1:8080/cas/proxyValidate";

    protected String serviceKey = "service";

    protected String logoutURL = "";

    protected String defaultCasServer = "";

    protected String ticketValidatorClassName = "edu.yale.its.tp.cas.client.ServiceTicketValidator";

    protected String proxyValidatorClassName = "edu.yale.its.tp.cas.client.ProxyTicketValidator";

    protected boolean promptLogin = true;

    protected List<String> excludePromptURLs;

    protected String errorPage;

    private boolean guestIsGranted;

    public List<String> getUnAuthenticatedURLPrefix() {
        // CAS login screen is not part of Nuxeo5 Web App
        return null;
    }

    protected String getServiceURL(HttpServletRequest httpRequest, String action) {
        String url = "";
        if (action.equals(LOGIN_ACTION)) {
            url = serviceLoginURL;
        } else if (action.equals(LOGOUT_ACTION)) {
            url = logoutURL;
        } else if (action.equals(VALIDATE_ACTION)) {
            url = serviceValidateURL;
        } else if (action.equals(PROXY_VALIDATE_ACTION)) {
            url = proxyValidateURL;
        }

        if (url.contains(CAS_SERVER_PATTERN_KEY)) {
            String serverURL = httpRequest.getHeader(CAS_SERVER_HEADER_KEY);
            if (serverURL != null) {
                url = url.replace(CAS_SERVER_PATTERN_KEY, serverURL);
            } else {
                if (url.contains(CAS_SERVER_PATTERN_KEY)) {
                    url = url.replace(CAS_SERVER_PATTERN_KEY, defaultCasServer);
                }
            }
        }
        log.debug("serviceUrl: " + url);
        return url;
    }

    public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
            String baseURL) {
        // Redirect to CAS Login screen
        // passing our application URL as service name
        String location = null;
        try {
            Map<String, String> urlParameters = new HashMap<String, String>();
            urlParameters.put("service", getAppURL(httpRequest));
            location = URIUtils.addParametersToURIQuery(getServiceURL(httpRequest, LOGIN_ACTION), urlParameters);
            httpResponse.sendRedirect(location);
        } catch (IOException e) {
            log.error("Unable to redirect to CAS login screen to " + location, e);
            return false;
        }
        return true;
    }

    protected String getAppURL(HttpServletRequest httpRequest) {
        log.debug("getAppURL");

        if (isValidStartupPage(httpRequest)) {
            StringBuffer sb = new StringBuffer(VirtualHostHelper.getServerURL(httpRequest));
            if (VirtualHostHelper.getServerURL(httpRequest).endsWith("/")) {
                sb.deleteCharAt(sb.length() - 1);
            }
            sb.append(httpRequest.getRequestURI());
            if (httpRequest.getQueryString() != null) {
                sb.append("?");
                sb.append(httpRequest.getQueryString());

                // remove ticket parameter from URL to correctly validate the
                // service
                int indexTicketKey = sb.lastIndexOf(ticketKey + "=");
                if (indexTicketKey != -1) {
                    sb.delete(indexTicketKey - 1, sb.length());
                }
            }
            log.debug(sb.toString());
            return sb.toString();
        }
        if (appURL == null || appURL.equals("")) {
            log.debug("null app url");
            appURL = NUXEO_SERVER_PATTERN_KEY;
        }
        if (appURL.contains(NUXEO_SERVER_PATTERN_KEY)) {
            String nxurl = BaseURL.getBaseURL(httpRequest);
            log.debug(String.format("nxurl app url : %s", appURL.replace(NUXEO_SERVER_PATTERN_KEY, nxurl)));
            return appURL.replace(NUXEO_SERVER_PATTERN_KEY, nxurl);
        } else {
            log.debug(String.format("appurl: %s", appURL));
            return appURL;
        }
    }

    private boolean isValidStartupPage(HttpServletRequest httpRequest) {
        if (httpRequest.getRequestURI() == null) {
            return false;
        }
        PluggableAuthenticationService service = (PluggableAuthenticationService) Framework.getRuntime()
                .getComponent(PluggableAuthenticationService.NAME);
        if (service == null) {
            return false;
        }
        String startPage = httpRequest.getRequestURI().replace(VirtualHostHelper.getContextPath(httpRequest) + "/",
                "");
        for (String prefix : service.getStartURLPatterns()) {
            if (startPage.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }

    public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) {
        /* This is output object*/
        UserIdentificationInfo userIdentificationInfo = null;

        String casTicket = httpRequest.getParameter(ticketKey);

        /*beginning of custom
         * 
         */
        String url = httpRequest.getRequestURL().toString();
        log.debug(String.format("handleRetrieveIdentity %s", url));
        String esupDocId;
        if (url.contains("/esupversions/") || url.contains("/nxdoc/")) {
            int from = url.indexOf("nuxeo") + "nuxeo".length() + 1;
            String[] urlNodes = (url.substring(from)).split("/");
            esupDocId = urlNodes[2];
            log.debug(String.format("Doc id : %s", esupDocId));
            LoginContext loginContext = null;

            try {
                loginContext = Framework.login();
                RepositoryManager repoManager = Framework.getService(RepositoryManager.class);
                Repository repository = repoManager.getDefaultRepository();
                CoreSession session;
                if (repository != null) {
                    session = repository.open();
                    DocumentRef docRef = new IdRef(esupDocId);
                    DocumentModelList proxies = session.getProxies(docRef, null);
                    DocumentModel doc = null;
                    if (proxies.size() > 0) {
                        doc = proxies.get(0);
                    } else {
                        doc = session.getDocument(docRef);
                    }

                    UserManager userManager = Framework.getService(UserManager.class);
                    String anonymousId = userManager.getAnonymousUserId();
                    log.debug(String.format("Anonymous id is %s", anonymousId));
                    guestIsGranted = doc.getACP().getAccess(userManager.getAnonymousUserId(), "Read").toBoolean();

                    if (guestIsGranted) {
                        log.debug("Guest is granted");
                        userIdentificationInfo = new UserIdentificationInfo(anonymousId, anonymousId);
                        guestIsGranted = false;
                        return userIdentificationInfo;
                    } else {
                        log.debug("Guest is not granted");

                    }
                    loginContext.logout();
                }

            } catch (LoginException e) {
                log.error("login as system failed");
            } catch (ClientException e) {
                log.error(String.format("Document %s not found", esupDocId));
            } catch (Exception e) {
                log.error(String.format("Error getting service: %s", e.getMessage()));
            }

        }
        /*end of custom
         */

        String proxy = httpRequest.getParameter(proxyKey);

        if (casTicket == null) {
            log.debug("No ticket found");
            return null;
        }

        String userName;

        if (proxy == null) {
            userName = checkCasTicket(casTicket, httpRequest);
        } else {
            userName = checkProxyCasTicket(casTicket, httpRequest);
        }

        if (userName == null) {
            return null;
        }

        userIdentificationInfo = new UserIdentificationInfo(userName, casTicket);
        userIdentificationInfo.setToken(casTicket);

        return userIdentificationInfo;
    }

    public void initPlugin(Map<String, String> parameters) {
        if (parameters.containsKey(CAS2Parameters.TICKET_NAME_KEY)) {
            ticketKey = parameters.get(CAS2Parameters.TICKET_NAME_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.PROXY_NAME_KEY)) {
            proxyKey = parameters.get(CAS2Parameters.PROXY_NAME_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.NUXEO_APP_URL_KEY)) {
            appURL = parameters.get(CAS2Parameters.NUXEO_APP_URL_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.SERVICE_LOGIN_URL_KEY)) {
            serviceLoginURL = parameters.get(CAS2Parameters.SERVICE_LOGIN_URL_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.SERVICE_VALIDATE_URL_KEY)) {
            serviceValidateURL = parameters.get(CAS2Parameters.SERVICE_VALIDATE_URL_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.PROXY_VALIDATE_URL_KEY)) {
            proxyValidateURL = parameters.get(CAS2Parameters.PROXY_VALIDATE_URL_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.SERVICE_NAME_KEY)) {
            serviceKey = parameters.get(CAS2Parameters.SERVICE_NAME_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.LOGOUT_URL_KEY)) {
            logoutURL = parameters.get(CAS2Parameters.LOGOUT_URL_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.DEFAULT_CAS_SERVER_KEY)) {
            defaultCasServer = parameters.get(CAS2Parameters.DEFAULT_CAS_SERVER_KEY);
        }
        if (parameters.containsKey(CAS2Parameters.SERVICE_VALIDATOR_CLASS)) {
            ticketValidatorClassName = parameters.get(CAS2Parameters.SERVICE_VALIDATOR_CLASS);
        }
        if (parameters.containsKey(CAS2Parameters.PROXY_VALIDATOR_CLASS)) {
            proxyValidatorClassName = parameters.get(CAS2Parameters.PROXY_VALIDATOR_CLASS);
        }
        if (parameters.containsKey(CAS2Parameters.PROMPT_LOGIN)) {
            promptLogin = Boolean.parseBoolean(parameters.get(CAS2Parameters.PROMPT_LOGIN));
        }
        excludePromptURLs = new ArrayList<String>();
        for (String key : parameters.keySet()) {
            if (key.startsWith(EXCLUDE_PROMPT_KEY)) {
                excludePromptURLs.add(parameters.get(key));
            }
        }
        if (parameters.containsKey(CAS2Parameters.ERROR_PAGE)) {
            errorPage = parameters.get(CAS2Parameters.ERROR_PAGE);
        }
    }

    public Boolean needLoginPrompt(HttpServletRequest httpRequest) {

        log.debug(String.format("needLoginPrompt %s", httpRequest.getRequestURL().toString()));
        String requestedURI = httpRequest.getRequestURI();
        String context = httpRequest.getContextPath() + '/';
        requestedURI = requestedURI.substring(context.length());
        for (String prefixURL : excludePromptURLs) {
            if (requestedURI.startsWith(prefixURL)) {
                return false;
            }
        }
        return promptLogin;
    }

    public Boolean handleLogout(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        log.debug(String.format("handleLogout %s", httpRequest.getRequestURL().toString()));
        if (logoutURL == null || logoutURL.equals("")) {
            log.debug("No CAS logout params, skipping CAS2Logout");
            return false;
        }
        try {
            httpResponse.sendRedirect(getServiceURL(httpRequest, LOGOUT_ACTION));
        } catch (IOException e) {
            log.error("Unable to redirect to CAS logout screen:", e);
            return false;
        }
        return true;
    }

    protected String checkProxyCasTicket(String ticket, HttpServletRequest httpRequest) {
        log.debug(String.format("checkProxyCasTicket %s", httpRequest.getRequestURL().toString()));
        String service = httpRequest.getParameter(serviceKey);
        if (service == null) {
            // added ESUP => does not work otherwise. 
            service = getAppURL(httpRequest);
        }

        ProxyTicketValidator proxyValidator;
        try {
            proxyValidator = (ProxyTicketValidator) Framework.getRuntime().getContext()
                    .loadClass(proxyValidatorClassName).newInstance();
        } catch (InstantiationException e) {
            log.error(
                    "checkProxyCasTicket during the ProxyTicketValidator initialization with InstantiationException:",
                    e);
            return null;
        } catch (IllegalAccessException e) {
            log.error(
                    "checkProxyCasTicket during the ProxyTicketValidator initialization with IllegalAccessException:",
                    e);
            return null;
        } catch (ClassNotFoundException e) {
            log.error(
                    "checkProxyCasTicket during the ProxyTicketValidator initialization with ClassNotFoundException:",
                    e);
            return null;
        }

        proxyValidator.setCasValidateUrl(getServiceURL(httpRequest, PROXY_VALIDATE_ACTION));
        proxyValidator.setService(service);
        proxyValidator.setServiceTicket(ticket);
        try {
            proxyValidator.validate();
        } catch (IOException e) {
            log.error("checkProxyCasTicket failed with IOException:", e);
            return null;
        } catch (SAXException e) {
            log.error("checkProxyCasTicket failed with SAXException:", e);
            return null;
        } catch (ParserConfigurationException e) {
            log.error("checkProxyCasTicket failed with ParserConfigurationException:", e);
            return null;
        }
        log.debug("checkProxyCasTicket: validation executed without error");
        String username = proxyValidator.getUser();
        log.debug("checkProxyCasTicket: validation returned username = " + username);

        return username;
    }

    // Cas2 Ticket management
    protected String checkCasTicket(String ticket, HttpServletRequest httpRequest) {
        log.debug(String.format("checkCasTicket %s", httpRequest.getRequestURL().toString()));
        ServiceTicketValidator ticketValidator;
        try {
            ticketValidator = (ServiceTicketValidator) Framework.getRuntime().getContext()
                    .loadClass(ticketValidatorClassName).newInstance();
        } catch (InstantiationException e) {
            log.error(
                    "checkCasTicket during the ServiceTicketValidator initialization with InstantiationException:",
                    e);
            return null;
        } catch (IllegalAccessException e) {
            log.error(
                    "checkCasTicket during the ServiceTicketValidator initialization with IllegalAccessException:",
                    e);
            return null;
        } catch (ClassNotFoundException e) {
            log.error(
                    "checkCasTicket during the ServiceTicketValidator initialization with ClassNotFoundException:",
                    e);
            return null;
        }

        ticketValidator.setCasValidateUrl(getServiceURL(httpRequest, VALIDATE_ACTION));
        ticketValidator.setService(getAppURL(httpRequest));
        ticketValidator.setServiceTicket(ticket);
        try {
            ticketValidator.validate();
        } catch (IOException e) {
            log.error("checkCasTicket failed with IOException:", e);
            return null;
        } catch (SAXException e) {
            log.error("checkCasTicket failed with SAXException:", e);
            return null;
        } catch (ParserConfigurationException e) {
            log.error("checkCasTicket failed with ParserConfigurationException:", e);
            return null;
        }
        log.debug("checkCasTicket : validation executed without error");
        String username = ticketValidator.getUser();
        log.debug("checkCasTicket: validation returned username = " + username);
        return username;
    }

    @Override
    public boolean onError(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            if (errorPage != null) {
                response.sendRedirect(errorPage);
            }
        } catch (Exception e) {
            log.error(e);
            return false;
        }
        return true;
    }

    @Override
    public boolean onSuccess(HttpServletRequest arg0, HttpServletResponse arg1) {
        log.debug(String.format("checkCasTicket %s", arg0.getRequestURL().toString()));
        return false;
    }

}