001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     ldoguin
018 */
019package org.nuxeo.ecm.platform.web.common.exceptionhandling;
020
021import java.io.IOException;
022import java.security.Principal;
023import java.util.HashMap;
024import java.util.Map;
025
026import javax.faces.context.FacesContext;
027import javax.servlet.ServletException;
028import javax.servlet.http.HttpServletRequest;
029import javax.servlet.http.HttpServletResponse;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.nuxeo.common.utils.URIUtils;
034import org.nuxeo.ecm.core.api.NuxeoPrincipal;
035import org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter;
036import org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService;
037import org.nuxeo.runtime.api.Framework;
038
039import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.*;
040
041/**
042 * This exception handler adds security error flag in the URL parameters to ensure the anonymous user will get
043 * appropriate error message when being redirected to login page.
044 * <p>
045 * If it isn't a security exception, or if the user is not anonymous, this handler ends up using
046 * DefaultNuxeoExceptionHandler.
047 *
048 * @author ldoguin
049 */
050public class NuxeoSecurityExceptionHandler extends DefaultNuxeoExceptionHandler {
051
052    private static final Log log = LogFactory.getLog(NuxeoSecurityExceptionHandler.class);
053
054    protected PluggableAuthenticationService service;
055
056    @Override
057    public void handleException(HttpServletRequest request, HttpServletResponse response, Throwable t)
058            throws IOException, ServletException {
059
060        Throwable unwrappedException = unwrapException(t);
061        if (!ExceptionHelper.isSecurityError(unwrappedException)) {
062            super.handleException(request, response, t);
063            return;
064        }
065
066        Principal principal = request.getUserPrincipal();
067        if (principal instanceof NuxeoPrincipal) {
068            NuxeoPrincipal nuxeoPrincipal = (NuxeoPrincipal) principal;
069            if (nuxeoPrincipal.isAnonymous()) {
070                // redirect to login than to requested page
071                if (handleAnonymousException(request, response)) {
072                    return;
073                }
074            }
075        }
076        // go back to default handler
077        super.handleException(request, response, t);
078    }
079
080    /**
081     * Handles the Security Error when the user is anonymous.
082     *
083     * @return {@code true} if the Security Error is handled so that the calling method won't fallback on the default
084     *         handler, {@code false} otherwise.
085     */
086    protected boolean handleAnonymousException(HttpServletRequest request, HttpServletResponse response)
087            throws IOException, ServletException {
088        getAuthenticationService().invalidateSession(request);
089        Map<String, String> urlParameters = new HashMap<String, String>();
090        urlParameters.put(SECURITY_ERROR, "true");
091        urlParameters.put(FORCE_ANONYMOUS_LOGIN, "true");
092        if (request.getAttribute(REQUESTED_URL) != null) {
093            urlParameters.put(REQUESTED_URL, (String) request.getAttribute(REQUESTED_URL));
094        } else {
095            urlParameters.put(REQUESTED_URL, NuxeoAuthenticationFilter.getRequestedUrl(request));
096        }
097        // Redirect to login with urlParameters
098        if (!response.isCommitted()) {
099            String baseURL = getAuthenticationService().getBaseURL(request) + LOGOUT_PAGE;
100            request.setAttribute(DISABLE_REDIRECT_REQUEST_KEY, true);
101            baseURL = URIUtils.addParametersToURIQuery(baseURL, urlParameters);
102            response.sendRedirect(baseURL);
103            FacesContext fContext = FacesContext.getCurrentInstance();
104            if (fContext != null) {
105                fContext.responseComplete();
106            } else {
107                log.error("Cannot set response complete: faces context is null");
108            }
109        } else {
110            log.error("Cannot redirect to login page: response is already committed");
111        }
112        return true;
113    }
114
115    protected PluggableAuthenticationService getAuthenticationService() throws ServletException {
116        if (service != null) {
117            return service;
118        }
119        service = (PluggableAuthenticationService) Framework.getRuntime().getComponent(
120                PluggableAuthenticationService.NAME);
121        if (service == null) {
122            throw new ServletException("Can't initialize Nuxeo Pluggable Authentication Service: "
123                    + PluggableAuthenticationService.NAME);
124        }
125        return service;
126    }
127
128}