001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.portlet.ActionResult;
021    import com.liferay.portal.kernel.portlet.PortletContainer;
022    import com.liferay.portal.kernel.portlet.PortletContainerException;
023    import com.liferay.portal.kernel.portlet.PortletContainerUtil;
024    import com.liferay.portal.kernel.resiliency.spi.SPIUtil;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.servlet.HttpHeaders;
027    import com.liferay.portal.kernel.servlet.TempAttributesServletRequest;
028    import com.liferay.portal.kernel.struts.LastPath;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.GetterUtil;
031    import com.liferay.portal.kernel.util.WebKeys;
032    import com.liferay.portal.model.Layout;
033    import com.liferay.portal.model.LayoutType;
034    import com.liferay.portal.model.LayoutTypeAccessPolicy;
035    import com.liferay.portal.model.LayoutTypePortlet;
036    import com.liferay.portal.model.Portlet;
037    import com.liferay.portal.security.auth.AuthTokenUtil;
038    import com.liferay.portal.security.auth.AuthTokenWhitelistUtil;
039    import com.liferay.portal.security.auth.PrincipalException;
040    import com.liferay.portal.theme.ThemeDisplay;
041    import com.liferay.portal.util.PortalUtil;
042    import com.liferay.portal.util.PropsValues;
043    
044    import java.util.List;
045    import java.util.Map;
046    
047    import javax.portlet.Event;
048    
049    import javax.servlet.RequestDispatcher;
050    import javax.servlet.http.HttpServletRequest;
051    import javax.servlet.http.HttpServletResponse;
052    
053    /**
054     * @author Tomas Polesovsky
055     * @author Raymond Aug??
056     */
057    @DoPrivileged
058    public class SecurityPortletContainerWrapper implements PortletContainer {
059    
060            public static PortletContainer createSecurityPortletContainerWrapper(
061                    PortletContainer portletContainer) {
062    
063                    if (!SPIUtil.isSPI()) {
064                            portletContainer = new SecurityPortletContainerWrapper(
065                                    portletContainer);
066                    }
067    
068                    return portletContainer;
069            }
070    
071            public SecurityPortletContainerWrapper(PortletContainer portletContainer) {
072                    _portletContainer = portletContainer;
073            }
074    
075            @Override
076            public void preparePortlet(HttpServletRequest request, Portlet portlet)
077                    throws PortletContainerException {
078    
079                    _portletContainer.preparePortlet(request, portlet);
080            }
081    
082            @Override
083            public ActionResult processAction(
084                            HttpServletRequest request, HttpServletResponse response,
085                            Portlet portlet)
086                    throws PortletContainerException {
087    
088                    try {
089                            HttpServletRequest ownerLayoutRequest =
090                                    getOwnerLayoutRequestWrapper(request, portlet);
091    
092                            checkAction(ownerLayoutRequest, portlet);
093    
094                            return _portletContainer.processAction(request, response, portlet);
095                    }
096                    catch (PrincipalException pe) {
097                            return processActionException(request, response, portlet, pe);
098                    }
099                    catch (PortletContainerException pce) {
100                            throw pce;
101                    }
102                    catch (Exception e) {
103                            throw new PortletContainerException(e);
104                    }
105            }
106    
107            @Override
108            public List<Event> processEvent(
109                            HttpServletRequest request, HttpServletResponse response,
110                            Portlet portlet, Layout layout, Event event)
111                    throws PortletContainerException {
112    
113                    return _portletContainer.processEvent(
114                            request, response, portlet, layout, event);
115            }
116    
117            @Override
118            public void render(
119                            HttpServletRequest request, HttpServletResponse response,
120                            Portlet portlet)
121                    throws PortletContainerException {
122    
123                    try {
124                            checkRender(request, portlet);
125    
126                            _portletContainer.render(request, response, portlet);
127                    }
128                    catch (PrincipalException pe) {
129                            processRenderException(request, response, portlet);
130                    }
131                    catch (PortletContainerException pce) {
132                            throw pce;
133                    }
134                    catch (Exception e) {
135                            throw new PortletContainerException(e);
136                    }
137            }
138    
139            @Override
140            public void serveResource(
141                            HttpServletRequest request, HttpServletResponse response,
142                            Portlet portlet)
143                    throws PortletContainerException {
144    
145                    try {
146                            HttpServletRequest ownerLayoutRequest =
147                                    getOwnerLayoutRequestWrapper(request, portlet);
148    
149                            checkResource(ownerLayoutRequest, portlet);
150    
151                            _portletContainer.serveResource(request, response, portlet);
152                    }
153                    catch (PrincipalException pe) {
154                            processServeResourceException(request, response, portlet, pe);
155                    }
156                    catch (PortletContainerException pce) {
157                            throw pce;
158                    }
159                    catch (Exception e) {
160                            throw new PortletContainerException(e);
161                    }
162            }
163    
164            protected void check(HttpServletRequest request, Portlet portlet)
165                    throws Exception {
166    
167                    if (portlet == null) {
168                            return;
169                    }
170    
171                    if (!isValidPortletId(portlet.getPortletId())) {
172                            if (_log.isWarnEnabled()) {
173                                    _log.warn("Invalid portlet ID " + portlet.getPortletId());
174                            }
175    
176                            throw new PrincipalException(
177                                    "Invalid portlet ID " + portlet.getPortletId());
178                    }
179    
180                    if (portlet.isUndeployedPortlet()) {
181                            return;
182                    }
183    
184                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
185    
186                    LayoutType layoutType = layout.getLayoutType();
187    
188                    LayoutTypeAccessPolicy layoutTypeAccessPolicy =
189                            layoutType.getLayoutTypeAccessPolicy();
190    
191                    layoutTypeAccessPolicy.checkAccessAllowedToPortlet(
192                            request, layout, portlet);
193            }
194    
195            protected void checkAction(HttpServletRequest request, Portlet portlet)
196                    throws Exception {
197    
198                    checkCSRFProtection(request, portlet);
199    
200                    check(request, portlet);
201            }
202    
203            protected void checkCSRFProtection(
204                            HttpServletRequest request, Portlet portlet)
205                    throws PortalException {
206    
207                    Map<String, String> initParams = portlet.getInitParams();
208    
209                    boolean checkAuthToken = GetterUtil.getBoolean(
210                            initParams.get("check-auth-token"), true);
211    
212                    if (AuthTokenWhitelistUtil.isPortletCSRFWhitelisted(request, portlet)) {
213                            checkAuthToken = false;
214                    }
215    
216                    if (checkAuthToken) {
217                            AuthTokenUtil.checkCSRFToken(
218                                    request, SecurityPortletContainerWrapper.class.getName());
219                    }
220            }
221    
222            protected void checkRender(HttpServletRequest request, Portlet portlet)
223                    throws Exception {
224    
225                    check(request, portlet);
226            }
227    
228            protected void checkResource(HttpServletRequest request, Portlet portlet)
229                    throws Exception {
230    
231                    check(request, portlet);
232            }
233    
234            protected String getOriginalURL(HttpServletRequest request) {
235                    LastPath lastPath = (LastPath)request.getAttribute(WebKeys.LAST_PATH);
236    
237                    if (lastPath == null) {
238                            return String.valueOf(request.getRequestURI());
239                    }
240    
241                    String portalURL = PortalUtil.getPortalURL(request);
242    
243                    return portalURL.concat(
244                            lastPath.getContextPath()).concat(lastPath.getPath());
245            }
246    
247            protected HttpServletRequest getOwnerLayoutRequestWrapper(
248                            HttpServletRequest request, Portlet portlet)
249                    throws Exception {
250    
251                    if (!PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET ||
252                            PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals("render")) {
253    
254                            return request;
255                    }
256    
257                    Layout ownerLayout = null;
258                    LayoutTypePortlet ownerLayoutTypePortlet = null;
259    
260                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
261                            WebKeys.THEME_DISPLAY);
262    
263                    Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
264    
265                    List<LayoutTypePortlet> layoutTypePortlets =
266                            PortletContainerUtil.getLayoutTypePortlets(requestLayout);
267    
268                    for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
269                            if (layoutTypePortlet.hasPortletId(portlet.getPortletId())) {
270                                    ownerLayoutTypePortlet = layoutTypePortlet;
271    
272                                    ownerLayout = layoutTypePortlet.getLayout();
273    
274                                    break;
275                            }
276                    }
277    
278                    if (ownerLayout == null) {
279                            return request;
280                    }
281    
282                    Layout currentLayout = themeDisplay.getLayout();
283    
284                    if (currentLayout.equals(ownerLayout)) {
285                            return request;
286                    }
287    
288                    ThemeDisplay themeDisplayClone = (ThemeDisplay)themeDisplay.clone();
289    
290                    themeDisplayClone.setLayout(ownerLayout);
291                    themeDisplayClone.setLayoutTypePortlet(ownerLayoutTypePortlet);
292    
293                    TempAttributesServletRequest tempAttributesServletRequest =
294                            new TempAttributesServletRequest(request);
295    
296                    tempAttributesServletRequest.setTempAttribute(
297                            WebKeys.LAYOUT, ownerLayout);
298                    tempAttributesServletRequest.setTempAttribute(
299                            WebKeys.THEME_DISPLAY, themeDisplayClone);
300    
301                    return tempAttributesServletRequest;
302            }
303    
304            protected boolean isValidPortletId(String portletId) {
305                    for (int i = 0; i < portletId.length(); i++) {
306                            char c = portletId.charAt(i);
307    
308                            if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) {
309                                    continue;
310                            }
311    
312                            if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) {
313                                    continue;
314                            }
315    
316                            if ((c >= CharPool.NUMBER_0) && (c <= CharPool.NUMBER_9)) {
317                                    continue;
318                            }
319    
320                            if ((c == CharPool.POUND) || (c == CharPool.UNDERLINE)) {
321                                    continue;
322                            }
323    
324                            return false;
325                    }
326    
327                    return true;
328            }
329    
330            protected ActionResult processActionException(
331                    HttpServletRequest request, HttpServletResponse response,
332                    Portlet portlet, PrincipalException pe) {
333    
334                    if (_log.isDebugEnabled()) {
335                            _log.debug(pe);
336                    }
337    
338                    String url = getOriginalURL(request);
339    
340                    if (_log.isWarnEnabled()) {
341                            _log.warn(
342                                    String.format(
343                                            "User %s is not allowed to access URL %s and portlet %s",
344                                            PortalUtil.getUserId(request), url,
345                                            portlet.getPortletId()));
346                    }
347    
348                    return ActionResult.EMPTY_ACTION_RESULT;
349            }
350    
351            protected void processRenderException(
352                            HttpServletRequest request, HttpServletResponse response,
353                            Portlet portlet)
354                    throws PortletContainerException {
355    
356                    String portletContent = null;
357    
358                    if (portlet.isShowPortletAccessDenied()) {
359                            portletContent = "/html/portal/portlet_access_denied.jsp";
360                    }
361    
362                    try {
363                            if (portletContent != null) {
364                                    RequestDispatcher requestDispatcher =
365                                            request.getRequestDispatcher(portletContent);
366    
367                                    requestDispatcher.include(request, response);
368                            }
369                    }
370                    catch (Exception e) {
371                            throw new PortletContainerException(e);
372                    }
373            }
374    
375            protected void processServeResourceException(
376                    HttpServletRequest request, HttpServletResponse response,
377                    Portlet portlet, PrincipalException pe) {
378    
379                    if (_log.isDebugEnabled()) {
380                            _log.debug(pe);
381                    }
382    
383                    String url = getOriginalURL(request);
384    
385                    response.setHeader(
386                            HttpHeaders.CACHE_CONTROL,
387                            HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
388    
389                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
390    
391                    if (_log.isWarnEnabled()) {
392                            _log.warn(
393                                    "Reject serveResource for " + url + " on " +
394                                            portlet.getPortletId());
395                    }
396            }
397    
398            private static final Log _log = LogFactoryUtil.getLog(
399                    SecurityPortletContainerWrapper.class);
400    
401            private final PortletContainer _portletContainer;
402    
403    }