/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* 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.
*/
package com.liferay.portlet;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Layout;
import com.liferay.portal.kernel.model.LayoutTypeAccessPolicy;
import com.liferay.portal.kernel.model.LayoutTypePortlet;
import com.liferay.portal.kernel.model.Portlet;
import com.liferay.portal.kernel.portlet.ActionResult;
import com.liferay.portal.kernel.portlet.PortletContainer;
import com.liferay.portal.kernel.portlet.PortletContainerException;
import com.liferay.portal.kernel.portlet.PortletContainerUtil;
import com.liferay.portal.kernel.resiliency.spi.SPIUtil;
import com.liferay.portal.kernel.security.auth.AuthTokenUtil;
import com.liferay.portal.kernel.security.auth.AuthTokenWhitelistUtil;
import com.liferay.portal.kernel.security.auth.PrincipalException;
import com.liferay.portal.kernel.security.pacl.DoPrivileged;
import com.liferay.portal.kernel.servlet.HttpHeaders;
import com.liferay.portal.kernel.servlet.TempAttributesServletRequest;
import com.liferay.portal.kernel.struts.LastPath;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.util.LayoutTypeAccessPolicyTracker;
import com.liferay.portal.util.PropsValues;
import java.util.List;
import java.util.Map;
import javax.portlet.Event;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Tomas Polesovsky
* @author Raymond Augé
*/
@DoPrivileged
public class SecurityPortletContainerWrapper implements PortletContainer {
public static PortletContainer createSecurityPortletContainerWrapper(
PortletContainer portletContainer) {
if (!SPIUtil.isSPI()) {
portletContainer = new SecurityPortletContainerWrapper(
portletContainer);
}
return portletContainer;
}
public SecurityPortletContainerWrapper(PortletContainer portletContainer) {
_portletContainer = portletContainer;
}
@Override
public void preparePortlet(HttpServletRequest request, Portlet portlet)
throws PortletContainerException {
_portletContainer.preparePortlet(request, portlet);
}
@Override
public ActionResult processAction(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet)
throws PortletContainerException {
try {
HttpServletRequest ownerLayoutRequest =
getOwnerLayoutRequestWrapper(request, portlet);
checkAction(ownerLayoutRequest, portlet);
return _portletContainer.processAction(request, response, portlet);
}
catch (PrincipalException pe) {
return processActionException(request, response, portlet, pe);
}
catch (PortletContainerException pce) {
throw pce;
}
catch (Exception e) {
throw new PortletContainerException(e);
}
}
@Override
public List<Event> processEvent(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet, Layout layout, Event event)
throws PortletContainerException {
return _portletContainer.processEvent(
request, response, portlet, layout, event);
}
@Override
public void render(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet)
throws PortletContainerException {
try {
checkRender(request, portlet);
_portletContainer.render(request, response, portlet);
}
catch (PrincipalException pe) {
// LPS-52675
if (_log.isDebugEnabled()) {
_log.debug(pe, pe);
}
processRenderException(request, response, portlet);
}
catch (PortletContainerException pce) {
throw pce;
}
catch (Exception e) {
throw new PortletContainerException(e);
}
}
@Override
public void serveResource(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet)
throws PortletContainerException {
try {
HttpServletRequest ownerLayoutRequest =
getOwnerLayoutRequestWrapper(request, portlet);
checkResource(ownerLayoutRequest, portlet);
_portletContainer.serveResource(request, response, portlet);
}
catch (PrincipalException pe) {
processServeResourceException(request, response, portlet, pe);
}
catch (PortletContainerException pce) {
throw pce;
}
catch (Exception e) {
throw new PortletContainerException(e);
}
}
protected void check(HttpServletRequest request, Portlet portlet)
throws Exception {
if (portlet == null) {
return;
}
if (portlet.isUndeployedPortlet()) {
return;
}
Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
LayoutTypeAccessPolicy layoutTypeAccessPolicy =
LayoutTypeAccessPolicyTracker.getLayoutTypeAccessPolicy(layout);
layoutTypeAccessPolicy.checkAccessAllowedToPortlet(
request, layout, portlet);
}
protected void checkAction(HttpServletRequest request, Portlet portlet)
throws Exception {
checkCSRFProtection(request, portlet);
check(request, portlet);
}
protected void checkCSRFProtection(
HttpServletRequest request, Portlet portlet)
throws PortalException {
Map<String, String> initParams = portlet.getInitParams();
boolean checkAuthToken = GetterUtil.getBoolean(
initParams.get("check-auth-token"), true);
if (AuthTokenWhitelistUtil.isPortletCSRFWhitelisted(request, portlet)) {
checkAuthToken = false;
}
if (checkAuthToken) {
AuthTokenUtil.checkCSRFToken(
request, SecurityPortletContainerWrapper.class.getName());
}
}
protected void checkRender(HttpServletRequest request, Portlet portlet)
throws Exception {
check(request, portlet);
}
protected void checkResource(HttpServletRequest request, Portlet portlet)
throws Exception {
check(request, portlet);
}
protected String getOriginalURL(HttpServletRequest request) {
LastPath lastPath = (LastPath)request.getAttribute(WebKeys.LAST_PATH);
if (lastPath == null) {
return String.valueOf(request.getRequestURI());
}
String portalURL = PortalUtil.getPortalURL(request);
return portalURL.concat(
lastPath.getContextPath()).concat(lastPath.getPath());
}
protected HttpServletRequest getOwnerLayoutRequestWrapper(
HttpServletRequest request, Portlet portlet)
throws Exception {
if (!PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET ||
PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals("render")) {
return request;
}
Layout ownerLayout = null;
LayoutTypePortlet ownerLayoutTypePortlet = null;
ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
WebKeys.THEME_DISPLAY);
Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
List<LayoutTypePortlet> layoutTypePortlets =
PortletContainerUtil.getLayoutTypePortlets(requestLayout);
for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
if (layoutTypePortlet.hasPortletId(portlet.getPortletId())) {
ownerLayoutTypePortlet = layoutTypePortlet;
ownerLayout = layoutTypePortlet.getLayout();
break;
}
}
if (ownerLayout == null) {
return request;
}
Layout currentLayout = themeDisplay.getLayout();
if (currentLayout.equals(ownerLayout)) {
return request;
}
ThemeDisplay themeDisplayClone = (ThemeDisplay)themeDisplay.clone();
themeDisplayClone.setLayout(ownerLayout);
themeDisplayClone.setLayoutTypePortlet(ownerLayoutTypePortlet);
TempAttributesServletRequest tempAttributesServletRequest =
new TempAttributesServletRequest(request);
tempAttributesServletRequest.setTempAttribute(
WebKeys.LAYOUT, ownerLayout);
tempAttributesServletRequest.setTempAttribute(
WebKeys.THEME_DISPLAY, themeDisplayClone);
return tempAttributesServletRequest;
}
/**
* @deprecated As of 7.0.0, with no direct replacement
*/
@Deprecated
protected boolean isValidPortletId(String portletId) {
for (int i = 0; i < portletId.length(); i++) {
char c = portletId.charAt(i);
if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) {
continue;
}
if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) {
continue;
}
if ((c >= CharPool.NUMBER_0) && (c <= CharPool.NUMBER_9)) {
continue;
}
if ((c == CharPool.POUND) || (c == CharPool.UNDERLINE)) {
continue;
}
return false;
}
return true;
}
protected ActionResult processActionException(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet, PrincipalException pe) {
if (_log.isDebugEnabled()) {
_log.debug(pe);
}
String url = getOriginalURL(request);
if (_log.isWarnEnabled()) {
_log.warn(
String.format(
"User %s is not allowed to access URL %s and portlet %s",
PortalUtil.getUserId(request), url,
portlet.getPortletId()));
}
return ActionResult.EMPTY_ACTION_RESULT;
}
protected void processRenderException(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet)
throws PortletContainerException {
String portletContent = null;
if (portlet.isShowPortletAccessDenied()) {
portletContent = "/html/portal/portlet_access_denied.jsp";
}
try {
if (portletContent != null) {
RequestDispatcher requestDispatcher =
request.getRequestDispatcher(portletContent);
requestDispatcher.include(request, response);
}
}
catch (Exception e) {
throw new PortletContainerException(e);
}
}
protected void processServeResourceException(
HttpServletRequest request, HttpServletResponse response,
Portlet portlet, PrincipalException pe) {
if (_log.isDebugEnabled()) {
_log.debug(pe);
}
String url = getOriginalURL(request);
response.setHeader(
HttpHeaders.CACHE_CONTROL,
HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
if (_log.isWarnEnabled()) {
_log.warn(
"Reject serveResource for " + url + " on " +
portlet.getPortletId());
}
}
private static final Log _log = LogFactoryUtil.getLog(
SecurityPortletContainerWrapper.class);
private final PortletContainer _portletContainer;
}