/** * Copyright (C) 2014 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.bonitasoft.console.common.server.page; import java.io.File; import java.io.IOException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; public class CustomPageServlet extends HttpServlet { /** * Logger */ private static Logger LOGGER = Logger.getLogger(CustomPageServlet.class.getName()); /** * uuid */ private static final long serialVersionUID = -5410859017103815654L; public static final String APP_TOKEN_PARAM = "appToken"; protected ResourceRenderer resourceRenderer = new ResourceRenderer(); protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer); protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { /* * Check if requested URL is missing last slash, like "custom-page/page-name". * If missing, redirect to "custom-page/page-name/" */ if (isPageUrlWithoutFinalSlash(request)) { customPageRequestModifier.redirectToValidPageUrl(request, response); return; } final String appToken = request.getParameter(APP_TOKEN_PARAM); final HttpSession session = request.getSession(); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); final List<String> pathSegments = resourceRenderer.getPathSegments(request.getPathInfo()); if (pathSegments.isEmpty()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "The name of the page is required."); return; } final String pageName = pathSegments.get(0); try { if (isPageRequest(pathSegments)) { if (!isAuthorized(apiSession, appToken, pageName)) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "User not Authorized"); return; } pageRenderer.displayCustomPage(request, response, apiSession, pageName); } else { final File resourceFile = getResourceFile(request.getPathInfo(), pageName, apiSession); pageRenderer.ensurePageFolderIsPresent(apiSession, pageRenderer.getPageResourceProvider(pageName, apiSession.getTenantId())); resourceRenderer.renderFile(request, response, resourceFile, apiSession); } } catch (final Exception e) { handleException(pageName, e); } } private boolean isPageRequest(final List<String> pathSegments) { if (pathSegments.size() == 1) { return true; } else if (pathSegments.size() == 2) { return isAnIndexSegment(pathSegments.get(1)); } return false; } private boolean isAnIndexSegment(final String segment) { return segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_FILENAME) || segment.equalsIgnoreCase(CustomPageService.PAGE_CONTROLLER_FILENAME) || segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_NAME); } private boolean isPageUrlWithoutFinalSlash(final HttpServletRequest request) { return request.getPathInfo().matches("/[^/]+"); } private File getResourceFile(final String resourcePath, final String pageName, final APISession apiSession) throws IOException, BonitaException { final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName, apiSession.getTenantId()); final File resourceFile = new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY + File.separator + getResourcePathWithoutPageName(resourcePath, pageName)); if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) { throw new BonitaException("Unauthorized access to the file " + resourcePath); } return resourceFile; } private String getResourcePathWithoutPageName(final String resourcePath, final String pageName) { //resource path match "/pagename/resourcefolder/filename" return resourcePath.substring(pageName.length() + 2); } private boolean isAuthorized(final APISession apiSession, final String appToken, final String pageName) throws BonitaException { return getCustomPageAuthorizationsHelper(apiSession).isPageAuthorized(appToken, pageName); } private void handleException(final String pageName, final Exception e) throws ServletException { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "Error while trying to render the custom page " + pageName, e); } throw new ServletException(e.getMessage()); } protected CustomPageAuthorizationsHelper getCustomPageAuthorizationsHelper(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return new CustomPageAuthorizationsHelper(new GetUserRightsHelper(apiSession), TenantAPIAccessor.getLivingApplicationAPI(apiSession), TenantAPIAccessor.getCustomPageAPI(apiSession)); } }