org.opencms.main.CmsProtectedStaticFileHandler Maven / Gradle / Ivy
Show all versions of opencms-core Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.main;
import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.configuration.I_CmsConfigurationParameterHandler;
import org.opencms.configuration.I_CmsNeedsAdminCmsObject;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.types.I_CmsResourceType;
import org.opencms.relations.CmsLink;
import org.opencms.relations.I_CmsCustomLinkRenderer;
import org.opencms.security.CmsPermissionViolationException;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.xml.xml2json.I_CmsApiAuthorizationHandler;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
/**
* Resource init handler that provides an alternative way of serving static files like images or binary files, using the API authorization mechanism
* instead of the normal authorization handler.
*
* Resources are accessed by appending their VFS root path to the /staticresource handler path. When resources are requested this way, they are still
* loaded with the normal OpenCms loader mechanism. This works for the intended use case (binary files, images) but may not work for other types.
*
*
The resources accessible through this handler can be restricted by setting regex configuration parameters for path and type which the requested resources
* have to match.
*
*
This can be used in combination with the CmsJsonResourceHandler class. When configured correctly (using the linkrewrite.id parameter on this handler,
* and a matching linkrewrite.refid on the CmsJsonResourceHandler), links to resources this handler is responsible for will be rewritten to point to the URL
* for the resource using this handler.
*/
public class CmsProtectedStaticFileHandler
implements I_CmsResourceInit, I_CmsConfigurationParameterHandler, I_CmsNeedsAdminCmsObject, I_CmsCustomLinkRenderer {
/** Parameter for defining the id under which the link renderer should be registered. */
public static final String PARAM_LINKREWRITE_ID = "linkrewrite.id";
/** Configuration parameter that determines which authorization method to use. */
public static final String PARAM_AUTHORIZATION = "authorization";
/** Configuration parameter for the path filter regex. */
public static final String PARAM_PATHFILTER = "pathfilter";
/** Configuration parameter for the type filter regex. */
public static final String PARAM_TYPEFILTER = "typefilter";
/** URL prefix. */
public static final String PREFIX = "/staticresource";
/** Logger instance for this class. */
private static final Log LOG = CmsLog.getLog(CmsProtectedStaticFileHandler.class);
public static final String PARAM_LINKREWRITE_PREFIX = "linkrewrite.prefix";
/** The Admin CMS context. */
private CmsObject m_adminCms;
/** Configuration from config file. */
private CmsParameterConfiguration m_config = new CmsParameterConfiguration();
/** Regex for matching paths. */
private Pattern m_pathFilter;
/** Regex for matching types. */
private Pattern m_typeFilter;
/** The link rewrite prefix. */
private String m_linkRewritePrefix;
/**
* Merges a link prefix with additional link components.
*
* @param prefix the prefix
* @param path the path
* @param query the query
*
* @return the combined link
*/
public static String mergeLinkPrefix(String prefix, String path, String query) {
try {
URI baseUri = new URI(prefix);
// we can't give an URIBuilder an already escaped query string, so we parse a dummy URL with the query string
// and use its parameter list for constructing the final URI
URI queryStringUri = new URI("http://test.invalid" + (query != null ? ("?" + query) : ""));
List params = new URIBuilder(queryStringUri).getQueryParams();
String result = new URIBuilder(baseUri).setPath(
CmsStringUtil.joinPaths(baseUri.getPath(), PREFIX, path)).setParameters(params).build().toASCIIString();
return result;
} catch (URISyntaxException e) {
LOG.error(e.getLocalizedMessage(), e);
return null;
}
}
/**
* Helper method for authorizing requests based on a comma-separated list of API authorization handler names.
*
* This will evaluate each authorization handler from authChain and return the first non-null CmsObject returned.
* A special case is authChain contains the word 'default', this is not u
*
*
Returns null if the authorization failed.
*
* @param adminCms the Admin CmsObject
* @param defaultCms the current CmsObject with the default user data from the request
* @param request the current request
* @param authChain a comma-separated list of API authorization handler names
*
* @return the initialized CmsObject
*/
private static CmsObject authorize(
CmsObject adminCms,
CmsObject defaultCms,
HttpServletRequest request,
String authChain) {
if (authChain == null) {
return defaultCms;
}
for (String token : authChain.split(",")) {
token = token.trim();
if ("default".equals(token)) {
LOG.info("Using default CmsObject");
return defaultCms;
} else if ("guest".equals(token)) {
try {
return OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
return null;
}
} else {
I_CmsApiAuthorizationHandler handler = OpenCms.getApiAuthorization(token);
if (handler == null) {
LOG.error("Could not find API authorization handler " + token);
return null;
} else {
try {
CmsObject cms = handler.initCmsObject(adminCms, request);
if (cms != null) {
LOG.info("Succeeded with authorization handler: " + token);
return cms;
}
} catch (CmsException e) {
LOG.error("Error evaluating authorization handler " + token);
return null;
}
}
}
}
LOG.info("Authentication unsusccessful");
return null;
}
/**
* @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String)
*/
public void addConfigurationParameter(String paramName, String paramValue) {
m_config.add(paramName, paramValue);
}
/**
* @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration()
*/
public CmsParameterConfiguration getConfiguration() {
return m_config;
}
/**
* @see org.opencms.relations.I_CmsCustomLinkRenderer#getLink(org.opencms.file.CmsObject, org.opencms.relations.CmsLink)
*/
public String getLink(CmsObject cms, CmsLink link) {
try {
CmsObject adminCms = OpenCms.initCmsObject(m_adminCms);
adminCms.getRequestContext().setCurrentProject(cms.getRequestContext().getCurrentProject());
link.checkConsistency(adminCms);
if (checkResourceAccessible(link.getResource())) {
return mergeLinkPrefix(m_linkRewritePrefix, link.getResource().getRootPath(), link.getQuery());
}
return null;
} catch (CmsException e) {
LOG.warn(e.getLocalizedMessage(), e);
return null;
}
}
/**
* @see org.opencms.relations.I_CmsCustomLinkRenderer#getLink(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
*/
public String getLink(CmsObject cms, CmsResource resource) {
if (checkResourceAccessible(resource)) {
return mergeLinkPrefix(m_linkRewritePrefix, resource.getRootPath(), null);
}
return null;
}
/**
* @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
*/
public void initConfiguration() {
m_config = CmsParameterConfiguration.unmodifiableVersion(m_config);
m_pathFilter = Pattern.compile(m_config.getString(PARAM_PATHFILTER, ".*"));
m_typeFilter = Pattern.compile(m_config.getString(PARAM_TYPEFILTER, "image|text|binary"));
String linkRewriteId = m_config.getString(PARAM_LINKREWRITE_ID, null);
if (linkRewriteId != null) {
OpenCms.setRuntimeProperty(linkRewriteId, this);
}
m_linkRewritePrefix = m_config.getString(PARAM_LINKREWRITE_PREFIX, null);
}
/**
* @see org.opencms.main.I_CmsResourceInit#initResource(org.opencms.file.CmsResource, org.opencms.file.CmsObject, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public CmsResource initResource(CmsResource origRes, CmsObject cms, HttpServletRequest req, HttpServletResponse res)
throws CmsResourceInitException {
String uri = cms.getRequestContext().getUri();
if (origRes != null) {
return origRes;
}
if (res == null) {
// called from locale handler
return origRes;
}
if (!CmsStringUtil.isPrefixPath(PREFIX, uri)) {
return null;
}
String path = uri.substring(PREFIX.length());
if (path.isEmpty()) {
path = "/";
} else if (path.length() > 1) {
path = CmsFileUtil.removeTrailingSeparator(path);
}
String authorizationParam = m_config.get(PARAM_AUTHORIZATION);
CmsObject origCms = cms;
cms = authorize(m_adminCms, origCms, req, authorizationParam);
if ((cms != null) && (cms != origCms)) {
origCms.getRequestContext().setAttribute(I_CmsResourceInit.ATTR_ALTERNATIVE_CMS_OBJECT, cms);
cms.getRequestContext().setSiteRoot(origCms.getRequestContext().getSiteRoot());
cms.getRequestContext().setUri(origCms.getRequestContext().getUri());
}
int status = 200;
try {
CmsObject rootCms = OpenCms.initCmsObject(cms);
rootCms.getRequestContext().setSiteRoot("");
if (m_pathFilter.matcher(path).matches()) {
CmsResource resource = rootCms.readResource(path, CmsResourceFilter.IGNORE_EXPIRATION);
if (!checkResourceAccessible(resource)) {
status = HttpServletResponse.SC_FORBIDDEN;
} else {
return resource;
}
}
status = HttpServletResponse.SC_NOT_FOUND;
} catch (CmsPermissionViolationException e) {
if (OpenCms.getDefaultUsers().isUserGuest(cms.getRequestContext().getCurrentUser().getName())) {
status = HttpServletResponse.SC_UNAUTHORIZED;
} else {
status = HttpServletResponse.SC_FORBIDDEN;
}
} catch (CmsVfsResourceNotFoundException e) {
status = HttpServletResponse.SC_NOT_FOUND;
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
try {
res.sendError(status);
} catch (IOException e) {
LOG.error(e.getLocalizedMessage(), e);
}
CmsResourceInitException ex = new CmsResourceInitException(CmsProtectedStaticFileHandler.class);
ex.setClearErrors(true);
throw ex;
}
/**
* @see org.opencms.configuration.I_CmsNeedsAdminCmsObject#setAdminCmsObject(org.opencms.file.CmsObject)
*/
public void setAdminCmsObject(CmsObject adminCms) {
m_adminCms = adminCms;
}
/**
* Checks if the resource is not hidden according to the filters configured in the resource handler parameters.
*
* @param res the resource to check
* @return true if the resource is accessible
*/
private boolean checkResourceAccessible(CmsResource res) {
return (res != null) && m_pathFilter.matcher(res.getRootPath()).matches() && checkType(res.getTypeId());
}
/**
* Checks that the type matches the configured type filter
*
* @param typeId a type id
* @return true if the type matches the configured type filter
*/
private boolean checkType(int typeId) {
try {
I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(typeId);
return m_typeFilter.matcher(type.getTypeName()).matches();
} catch (Exception e) {
LOG.error("Missing type with id: " + typeId);
return false;
}
}
}