/**
* 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.portal.security.auth;
import com.liferay.portal.kernel.concurrent.ConcurrentHashSet;
import com.liferay.portal.kernel.model.Portlet;
import com.liferay.portal.kernel.model.PortletConstants;
import com.liferay.portal.kernel.portlet.LiferayPortletURL;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCActionCommand;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCRenderCommand;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCResourceCommand;
import com.liferay.portal.kernel.security.auth.BaseAuthTokenWhitelist;
import com.liferay.portal.kernel.security.pacl.DoPrivileged;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import com.liferay.registry.ServiceReference;
import com.liferay.registry.ServiceTracker;
import com.liferay.registry.ServiceTrackerCustomizer;
import com.liferay.registry.util.StringPlus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.portlet.ActionRequest;
import javax.portlet.PortletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* @author Tomas Polesovsky
*/
@DoPrivileged
public class MVCPortletAuthTokenWhitelist extends BaseAuthTokenWhitelist {
public MVCPortletAuthTokenWhitelist() {
trackWhitelistServices(
"auth.token.ignore.mvc.action", MVCActionCommand.class,
_portletCSRFWhitelist);
trackWhitelistServices(
"portlet.add.default.resource.check.whitelist.mvc.action",
MVCActionCommand.class, _portletInvocationWhitelistAction);
trackWhitelistServices(
"portlet.add.default.resource.check.whitelist.mvc.action",
MVCRenderCommand.class, _portletInvocationWhitelistRender);
trackWhitelistServices(
"portlet.add.default.resource.check.whitelist.mvc.action",
MVCResourceCommand.class, _portletInvocationWhitelistResource);
}
@Override
public boolean isPortletCSRFWhitelisted(
HttpServletRequest request, Portlet portlet) {
String portletId = portlet.getPortletId();
String[] mvcActionCommandNames = getMVCActionCommandNames(
request, portletId);
return _containsAll(
portletId, _portletCSRFWhitelist, mvcActionCommandNames);
}
@Override
public boolean isPortletInvocationWhitelisted(
HttpServletRequest request, Portlet portlet) {
String portletId = portlet.getPortletId();
ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
WebKeys.THEME_DISPLAY);
if (themeDisplay.isLifecycleAction()) {
String[] mvcActionCommandNames = getMVCActionCommandNames(
request, portletId);
return _containsAll(
portletId, _portletInvocationWhitelistAction,
mvcActionCommandNames);
}
else if (themeDisplay.isLifecycleRender()) {
String namespace = PortalUtil.getPortletNamespace(portletId);
String mvcRenderCommandName = request.getParameter(
namespace.concat("mvcRenderCommandName"));
return _contains(
portletId, _portletInvocationWhitelistRender,
mvcRenderCommandName);
}
else if (themeDisplay.isLifecycleResource()) {
String ppid = request.getParameter("p_p_id");
if (!portletId.equals(ppid)) {
return false;
}
String mvcResourceCommandName = request.getParameter(
"p_p_resource_id");
return _contains(
portletId, _portletInvocationWhitelistResource,
mvcResourceCommandName);
}
return false;
}
@Override
public boolean isPortletURLCSRFWhitelisted(
LiferayPortletURL liferayPortletURL) {
String[] mvcActionCommandNames = getMVCActionCommandNames(
liferayPortletURL);
return _containsAll(
liferayPortletURL.getPortletId(), _portletCSRFWhitelist,
mvcActionCommandNames);
}
@Override
public boolean isPortletURLPortletInvocationWhitelisted(
LiferayPortletURL liferayPortletURL) {
String portletId = liferayPortletURL.getPortletId();
String lifecycle = liferayPortletURL.getLifecycle();
if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
String[] mvcActionCommandNames = getMVCActionCommandNames(
liferayPortletURL);
return _containsAll(
portletId, _portletInvocationWhitelistAction,
mvcActionCommandNames);
}
else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
String mvcRenderCommandName = liferayPortletURL.getParameter(
"mvcRenderCommandName");
return _contains(
portletId, _portletInvocationWhitelistRender,
mvcRenderCommandName);
}
else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
String mvcResourceCommandName = liferayPortletURL.getResourceID();
return _contains(
portletId, _portletInvocationWhitelistResource,
mvcResourceCommandName);
}
return false;
}
protected String[] getMVCActionCommandNames(
HttpServletRequest request, String portletId) {
String namespace = PortalUtil.getPortletNamespace(portletId);
String[] actionNames = request.getParameterValues(
namespace.concat(ActionRequest.ACTION_NAME));
String actions = StringUtil.merge(actionNames);
return StringUtil.split(actions);
}
protected String[] getMVCActionCommandNames(
LiferayPortletURL liferayPortletURL) {
Map<String, String[]> parameterMap =
liferayPortletURL.getParameterMap();
String[] actionNames = parameterMap.get(ActionRequest.ACTION_NAME);
String actions = StringUtil.merge(actionNames);
return StringUtil.split(actions);
}
protected String getWhitelistValue(
String portletName, String whitelistAction) {
return portletName.concat(StringPool.POUND).concat(whitelistAction);
}
protected void trackWhitelistServices(
String whitelistName, Class<?> serviceClass, Set<String> whiteList) {
Registry registry = RegistryUtil.getRegistry();
ServiceTracker<Object, Object> serviceTracker = registry.trackServices(
registry.getFilter(
"(&(&(" + whitelistName + "=*)(javax.portlet.name=*))" +
"(objectClass=" + serviceClass.getName() + "))"),
new TokenWhitelistTrackerCustomizer(whiteList));
serviceTracker.open();
serviceTrackers.add(serviceTracker);
}
private boolean _contains(
String portletId, Set<String> whitelist, String item) {
if (Validator.isBlank(item)) {
return false;
}
String rootPortletId = PortletConstants.getRootPortletId(portletId);
return whitelist.contains(getWhitelistValue(rootPortletId, item));
}
private boolean _containsAll(
String portletId, Set<String> whitelist, String[] items) {
if (items.length == 0) {
return false;
}
String rootPortletId = PortletConstants.getRootPortletId(portletId);
for (String action : items) {
if (!whitelist.contains(getWhitelistValue(rootPortletId, action))) {
return false;
}
}
return true;
}
private final Set<String> _portletCSRFWhitelist = new ConcurrentHashSet<>();
private final Set<String> _portletInvocationWhitelistAction =
new ConcurrentHashSet<>();
private final Set<String> _portletInvocationWhitelistRender =
new ConcurrentHashSet<>();
private final Set<String> _portletInvocationWhitelistResource =
new ConcurrentHashSet<>();
private class TokenWhitelistTrackerCustomizer
implements ServiceTrackerCustomizer<Object, Object> {
public TokenWhitelistTrackerCustomizer(Set<String> whitelist) {
_whitelist = whitelist;
}
@Override
public Object addingService(ServiceReference<Object> serviceReference) {
Collection<String> whitelistValues = new ArrayList<>();
List<String> whitelistActions = StringPlus.asList(
serviceReference.getProperty("mvc.command.name"));
List<String> portletNames = StringPlus.asList(
serviceReference.getProperty("javax.portlet.name"));
for (String portletName : portletNames) {
for (String whitelistAction : whitelistActions) {
whitelistValues.add(
getWhitelistValue(portletName, whitelistAction));
}
}
_whitelist.addAll(whitelistValues);
return whitelistValues;
}
@Override
public void modifiedService(
ServiceReference<Object> serviceReference, Object object) {
removedService(serviceReference, object);
addingService(serviceReference);
}
@Override
public void removedService(
ServiceReference<Object> serviceReference, Object object) {
Collection<String> whitelistValues = (Collection<String>)object;
_whitelist.removeAll(whitelistValues);
}
private final Set<String> _whitelist;
}
}