001/*
002 * (C) Copyright 2012 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 *     bjalon
018 */
019package org.nuxeo.ecm.mobile.filter;
020
021import java.io.IOException;
022import java.util.HashMap;
023import java.util.Map;
024
025import javax.servlet.Filter;
026import javax.servlet.FilterChain;
027import javax.servlet.FilterConfig;
028import javax.servlet.ServletException;
029import javax.servlet.ServletRequest;
030import javax.servlet.ServletResponse;
031import javax.servlet.http.HttpServletRequest;
032import javax.servlet.http.HttpServletResponse;
033
034import org.apache.commons.lang.StringUtils;
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.nuxeo.common.utils.URIUtils;
038import org.nuxeo.ecm.mobile.ApplicationDefinitionService;
039import org.nuxeo.ecm.platform.ui.web.auth.CachableUserIdentificationInfo;
040import org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter;
041import org.nuxeo.runtime.api.Framework;
042
043/**
044 * Filter that redirects to the chosen application URL or application dedicated for the context request.
045 * To understand the application selection engine look {@code ApplicationDefinitionService#getTargetApplication(HttpServletRequest).
046 * If no application match, no redirection will be executed.
047 *
048 * @author <a href="mailto:bjalon@nuxeo.com">Benjamin JALON</a>
049 * @since 5.5
050 *
051 */
052public class ApplicationRedirectionFilter implements Filter {
053
054    protected static final Log log = LogFactory.getLog(ApplicationRedirectionFilter.class);
055
056    public static final String INITIAL_TARGET_URL_PARAM_NAME = "targetURL";
057
058    private ApplicationDefinitionService service;
059
060    protected ApplicationDefinitionService getService() {
061        if (service == null) {
062            service = Framework.getLocalService(ApplicationDefinitionService.class);
063        }
064        return service;
065    }
066
067    @Override
068    public void init(FilterConfig filterConfig) throws ServletException {
069        // Nothing to do
070    }
071
072    @Override
073    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
074            ServletException {
075        if (!(request instanceof HttpServletRequest)) {
076            log.debug("Not an Http request, no redirection");
077            doNoRedirect(request, response, chain);
078            return;
079        }
080
081        HttpServletRequest req = (HttpServletRequest) request;
082        log.debug("do filter - URL :" + req.getRequestURL() + "?" + req.getQueryString());
083
084        if (!isAuthenticated(req)) {
085            log.debug("User not authenticated so no application redirection");
086            doNoRedirect(request, response, chain);
087            return;
088        }
089
090        String targetApplicationBaseURL = getService().getApplicationBaseURI(req);
091
092        if (targetApplicationBaseURL == null) {
093            log.debug("No application match this request context " + "=> no redirect: final URL: "
094                    + req.getRequestURI());
095            doNoRedirect(request, response, chain);
096            return;
097        }
098
099        if (isRequestIntoApplication(req, targetApplicationBaseURL)) {
100            log.debug("Request URI is a child of target application so no redirect:" + " final URL: "
101                    + req.getRequestURI());
102            doNoRedirect(request, response, chain);
103            return;
104        }
105
106        if (getService().isResourceURL(req)) {
107            log.debug("Request URI is a resource of the target application so no redirect:" + " final URL: "
108                    + req.getRequestURI());
109            doNoRedirect(request, response, chain);
110            return;
111        }
112
113        RequestAdapter requestAdapter = new RequestAdapter(req);
114        if (requestAdapter.isOpenURL()) {
115            log.debug("Request URI is opened so no redirect:" + " final URL: " + req.getRequestURI());
116            doNoRedirect(request, response, chain);
117            return;
118        }
119
120        doApplicationRedirection((HttpServletRequest) request, (HttpServletResponse) response, chain);
121    }
122
123    /**
124     * Return principal stored into session
125     */
126    private boolean isAuthenticated(HttpServletRequest req) {
127        if (req.getSession(false) == null) {
128            return false;
129        }
130
131        CachableUserIdentificationInfo idInfo = (CachableUserIdentificationInfo) req.getSession().getAttribute(
132                "org.nuxeo.ecm.login.identity");
133
134        if (idInfo == null || idInfo.getPrincipal() == null) {
135            return false;
136        }
137
138        return true;
139    }
140
141    /**
142     * Redirect the browser to the target application with the initial URL as parameter into the
143     * {@value WebMobileConstants#TARGET_URL_PARAMETER} url parameter.
144     */
145    private void doApplicationRedirection(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
146            throws IOException, ServletException {
147        Map<String, String> parameters = new HashMap<String, String>();
148
149        String requestedPage = NuxeoAuthenticationFilter.getRequestedPage(request);
150        if (!StringUtils.isBlank(requestedPage)) {
151            parameters.put(INITIAL_TARGET_URL_PARAM_NAME, requestedPage);
152        }
153
154        String redirectURI = URIUtils.addParametersToURIQuery(getService().getApplicationBaseURL(request), parameters);
155        log.debug("Handler match/Non target application URI " + "=> Application redirected: target URL: " + redirectURI);
156
157        response.sendRedirect(redirectURI);
158    }
159
160    /**
161     * Do no redirection and let filters application to be done.
162     */
163    private void doNoRedirect(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
164            ServletException {
165        chain.doFilter(request, response);
166    }
167
168    private boolean isRequestIntoApplication(HttpServletRequest req, String targetApplicationBaseURL) {
169        String uri = req.getRequestURI();
170
171        log.debug("Request url: " + uri + " and targetApplicationURI: ");
172        if (!uri.startsWith(targetApplicationBaseURL)) {
173            log.debug("Request uri is not a child of application base url");
174            return false;
175        }
176        if (uri.equals(targetApplicationBaseURL)) {
177            log.debug("Request uri is the root of the application");
178            return true;
179        }
180        char character = uri.charAt(targetApplicationBaseURL.length());
181        if (character != '/' && character != '?' && character != '#' && character != '@') {
182            log.debug("Request uri is not a child of application base url");
183            return false;
184        }
185        log.debug("Request uri is a child of application base url");
186        return true;
187    }
188
189    @Override
190    public void destroy() {
191        log.debug("Filter detroyed");
192    }
193
194}