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