001    /**
002     * Copyright (c) 2000-2013 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.exception.SystemException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.portlet.ActionResult;
022    import com.liferay.portal.kernel.portlet.PortletContainer;
023    import com.liferay.portal.kernel.portlet.PortletContainerException;
024    import com.liferay.portal.kernel.portlet.PortletContainerUtil;
025    import com.liferay.portal.kernel.portlet.PortletModeFactory;
026    import com.liferay.portal.kernel.resiliency.spi.SPIUtil;
027    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
028    import com.liferay.portal.kernel.servlet.HttpHeaders;
029    import com.liferay.portal.kernel.servlet.TempAttributesServletRequest;
030    import com.liferay.portal.kernel.struts.LastPath;
031    import com.liferay.portal.kernel.util.ArrayUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.ParamUtil;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.model.Group;
037    import com.liferay.portal.model.Layout;
038    import com.liferay.portal.model.LayoutTypePortlet;
039    import com.liferay.portal.model.Portlet;
040    import com.liferay.portal.security.auth.AuthTokenUtil;
041    import com.liferay.portal.security.auth.PrincipalException;
042    import com.liferay.portal.security.permission.ActionKeys;
043    import com.liferay.portal.security.permission.PermissionChecker;
044    import com.liferay.portal.security.permission.PermissionThreadLocal;
045    import com.liferay.portal.service.permission.GroupPermissionUtil;
046    import com.liferay.portal.service.permission.LayoutPermissionUtil;
047    import com.liferay.portal.service.permission.LayoutPrototypePermissionUtil;
048    import com.liferay.portal.service.permission.LayoutSetPrototypePermissionUtil;
049    import com.liferay.portal.service.permission.OrganizationPermissionUtil;
050    import com.liferay.portal.service.permission.PortletPermissionUtil;
051    import com.liferay.portal.theme.ThemeDisplay;
052    import com.liferay.portal.util.PortalUtil;
053    import com.liferay.portal.util.PortletKeys;
054    import com.liferay.portal.util.PropsValues;
055    import com.liferay.portal.util.WebKeys;
056    
057    import java.util.List;
058    import java.util.Map;
059    
060    import javax.portlet.Event;
061    import javax.portlet.PortletMode;
062    
063    import javax.servlet.RequestDispatcher;
064    import javax.servlet.http.HttpServletRequest;
065    import javax.servlet.http.HttpServletResponse;
066    
067    /**
068     * @author Tomas Polesovsky
069     * @author Raymond Aug??
070     */
071    @DoPrivileged
072    public class SecurityPortletContainerWrapper implements PortletContainer {
073    
074            public static PortletContainer createSecurityPortletContainerWrapper(
075                    PortletContainer portletContainer) {
076    
077                    if (!SPIUtil.isSPI()) {
078                            portletContainer = new SecurityPortletContainerWrapper(
079                                    portletContainer);
080                    }
081    
082                    return portletContainer;
083            }
084    
085            public SecurityPortletContainerWrapper(PortletContainer portletContainer) {
086                    _portletContainer = portletContainer;
087            }
088    
089            @Override
090            public void preparePortlet(HttpServletRequest request, Portlet portlet)
091                    throws PortletContainerException {
092    
093                    _portletContainer.preparePortlet(request, portlet);
094            }
095    
096            @Override
097            public ActionResult processAction(
098                            HttpServletRequest request, HttpServletResponse response,
099                            Portlet portlet)
100                    throws PortletContainerException {
101    
102                    try {
103                            HttpServletRequest ownerLayoutRequest =
104                                    getOwnerLayoutRequestWrapper(request, portlet);
105    
106                            checkAction(ownerLayoutRequest, portlet);
107    
108                            return _portletContainer.processAction(request, response, portlet);
109                    }
110                    catch (PrincipalException pe) {
111                            return processActionException(request, response, portlet, pe);
112                    }
113                    catch (PortletContainerException pce) {
114                            throw pce;
115                    }
116                    catch (Exception e) {
117                            throw new PortletContainerException(e);
118                    }
119            }
120    
121            @Override
122            public List<Event> processEvent(
123                            HttpServletRequest request, HttpServletResponse response,
124                            Portlet portlet, Layout layout, Event event)
125                    throws PortletContainerException {
126    
127                    return _portletContainer.processEvent(
128                            request, response, portlet, layout, event);
129            }
130    
131            @Override
132            public void render(
133                            HttpServletRequest request, HttpServletResponse response,
134                            Portlet portlet)
135                    throws PortletContainerException {
136    
137                    try {
138                            checkRender(request, portlet);
139    
140                            _portletContainer.render(request, response, portlet);
141                    }
142                    catch (PrincipalException e) {
143                            processRenderException(request, response, portlet);
144                    }
145                    catch (PortletContainerException e) {
146                            throw e;
147                    }
148                    catch (Exception e) {
149                            throw new PortletContainerException(e);
150                    }
151            }
152    
153            @Override
154            public void serveResource(
155                            HttpServletRequest request, HttpServletResponse response,
156                            Portlet portlet)
157                    throws PortletContainerException {
158    
159                    try {
160                            HttpServletRequest ownerLayoutRequest =
161                                    getOwnerLayoutRequestWrapper(request, portlet);
162    
163                            checkResource(ownerLayoutRequest, portlet);
164    
165                            _portletContainer.serveResource(request, response, portlet);
166                    }
167                    catch (PrincipalException pe) {
168                            processServeResourceException(request, response, portlet, pe);
169                    }
170                    catch (PortletContainerException pce) {
171                            throw pce;
172                    }
173                    catch (Exception e) {
174                            throw new PortletContainerException(e);
175                    }
176            }
177    
178            protected void check(HttpServletRequest request, Portlet portlet)
179                    throws Exception {
180    
181                    if (portlet == null) {
182                            return;
183                    }
184    
185                    if (portlet.isUndeployedPortlet()) {
186                            return;
187                    }
188    
189                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
190    
191                    if (layout.isTypeControlPanel()) {
192                            isAccessAllowedToControlPanelPortlet(request, portlet);
193    
194                            return;
195                    }
196    
197                    if (isAccessAllowedToLayoutPortlet(request, portlet)) {
198                            PortalUtil.addPortletDefaultResource(request, portlet);
199    
200                            if (hasAccessPermission(request, portlet)) {
201                                    return;
202                            }
203                    }
204    
205                    throw new PrincipalException();
206            }
207    
208            protected void checkAction(HttpServletRequest request, Portlet portlet)
209                    throws Exception {
210    
211                    checkCSRFProtection(request, portlet);
212    
213                    check(request, portlet);
214            }
215    
216            protected void checkCSRFProtection(
217                            HttpServletRequest request, Portlet portlet)
218                    throws PortalException {
219    
220                    if (!PropsValues.AUTH_TOKEN_CHECK_ENABLED) {
221                            return;
222                    }
223    
224                    Map<String, String> initParams = portlet.getInitParams();
225    
226                    boolean checkAuthToken = GetterUtil.getBoolean(
227                            initParams.get("check-auth-token"), true);
228    
229                    if (checkAuthToken) {
230                            AuthTokenUtil.check(request);
231                    }
232            }
233    
234            protected void checkRender(HttpServletRequest request, Portlet portlet)
235                    throws Exception {
236    
237                    check(request, portlet);
238            }
239    
240            protected void checkResource(HttpServletRequest request, Portlet portlet)
241                    throws Exception {
242    
243                    check(request, portlet);
244            }
245    
246            protected String getOriginalURL(HttpServletRequest request) {
247                    LastPath lastPath = (LastPath)request.getAttribute(WebKeys.LAST_PATH);
248    
249                    if (lastPath == null) {
250                            return String.valueOf(request.getRequestURI());
251                    }
252    
253                    String portalURL = PortalUtil.getPortalURL(request);
254    
255                    return portalURL.concat(
256                            lastPath.getContextPath()).concat(lastPath.getPath());
257            }
258    
259            protected HttpServletRequest getOwnerLayoutRequestWrapper(
260                            HttpServletRequest request, Portlet portlet)
261                    throws Exception {
262    
263                    if (!PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET ||
264                            PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals("render")) {
265    
266                            return request;
267                    }
268    
269                    Layout ownerLayout = null;
270                    LayoutTypePortlet ownerLayoutTypePortlet = null;
271    
272                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
273                            WebKeys.THEME_DISPLAY);
274    
275                    Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
276    
277                    List<LayoutTypePortlet> layoutTypePortlets =
278                            PortletContainerUtil.getLayoutTypePortlets(requestLayout);
279    
280                    for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
281                            if (layoutTypePortlet.hasPortletId(portlet.getPortletId())) {
282                                    ownerLayoutTypePortlet = layoutTypePortlet;
283    
284                                    ownerLayout = layoutTypePortlet.getLayout();
285    
286                                    break;
287                            }
288                    }
289    
290                    if (ownerLayout == null) {
291                            return request;
292                    }
293    
294                    Layout currentLayout = themeDisplay.getLayout();
295    
296                    if (currentLayout.equals(ownerLayout)) {
297                            return request;
298                    }
299    
300                    ThemeDisplay themeDisplayClone = (ThemeDisplay)themeDisplay.clone();
301    
302                    themeDisplayClone.setLayout(ownerLayout);
303                    themeDisplayClone.setLayoutTypePortlet(ownerLayoutTypePortlet);
304    
305                    TempAttributesServletRequest tempAttributesServletRequest =
306                            new TempAttributesServletRequest(request);
307    
308                    tempAttributesServletRequest.setTempAttribute(
309                            WebKeys.LAYOUT, ownerLayout);
310                    tempAttributesServletRequest.setTempAttribute(
311                            WebKeys.THEME_DISPLAY, themeDisplayClone);
312    
313                    return tempAttributesServletRequest;
314            }
315    
316            protected boolean hasAccessPermission(
317                            HttpServletRequest request, Portlet portlet)
318                    throws PortalException, SystemException {
319    
320                    PermissionChecker permissionChecker =
321                            PermissionThreadLocal.getPermissionChecker();
322    
323                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
324                            WebKeys.THEME_DISPLAY);
325    
326                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
327    
328                    PortletMode portletMode = PortletModeFactory.getPortletMode(
329                            ParamUtil.getString(request, "p_p_mode"));
330    
331                    return PortletPermissionUtil.hasAccessPermission(
332                            permissionChecker, themeDisplay.getScopeGroupId(), layout, portlet,
333                            portletMode);
334            }
335    
336            protected void isAccessAllowedToControlPanelPortlet(
337                            HttpServletRequest request, Portlet portlet)
338                    throws PortalException, SystemException {
339    
340                    PermissionChecker permissionChecker =
341                            PermissionThreadLocal.getPermissionChecker();
342    
343                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
344                            WebKeys.THEME_DISPLAY);
345    
346                    if (PortletPermissionUtil.hasControlPanelAccessPermission(
347                                    permissionChecker, themeDisplay.getScopeGroupId(), portlet)) {
348    
349                            return;
350                    }
351    
352                    if (isAccessGrantedByRuntimePortlet(request, portlet)) {
353                            return;
354                    }
355    
356                    if (isAccessGrantedByPortletAuthenticationToken(request, portlet)) {
357                            return;
358                    }
359    
360                    throw new PrincipalException();
361            }
362    
363            protected boolean isAccessAllowedToLayoutPortlet(
364                            HttpServletRequest request, Portlet portlet)
365                    throws PortalException, SystemException {
366    
367                    if (isAccessGrantedByRuntimePortlet(request, portlet)) {
368                            return true;
369                    }
370    
371                    if (isAccessGrantedByPortletOnPage(request, portlet)) {
372                            return true;
373                    }
374    
375                    if (isLayoutConfigurationAllowed(request, portlet)) {
376                            return true;
377                    }
378    
379                    if (isAccessGrantedByPortletAuthenticationToken(request, portlet)) {
380                            return true;
381                    }
382    
383                    return false;
384            }
385    
386            protected boolean isAccessGrantedByPortletAuthenticationToken(
387                    HttpServletRequest request, Portlet portlet) {
388    
389                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
390                            WebKeys.THEME_DISPLAY);
391    
392                    String portletId = portlet.getPortletId();
393    
394                    if (!portlet.isAddDefaultResource()) {
395                            return false;
396                    }
397    
398                    if (!PropsValues.PORTLET_ADD_DEFAULT_RESOURCE_CHECK_ENABLED) {
399                            return true;
400                    }
401    
402                    String namespace = PortalUtil.getPortletNamespace(portletId);
403    
404                    String strutsAction = ParamUtil.getString(
405                            request, namespace + "struts_action");
406    
407                    if (Validator.isNull(strutsAction)) {
408                            strutsAction = ParamUtil.getString(request, "struts_action");
409                    }
410    
411                    String requestPortletAuthenticationToken = ParamUtil.getString(
412                            request, "p_p_auth");
413    
414                    if (Validator.isNull(requestPortletAuthenticationToken)) {
415                            HttpServletRequest originalRequest =
416                                    PortalUtil.getOriginalServletRequest(request);
417    
418                            requestPortletAuthenticationToken = ParamUtil.getString(
419                                    originalRequest, "p_p_auth");
420                    }
421    
422                    if (AuthTokenUtil.isValidPortletInvocationToken(
423                                    request, themeDisplay.getPlid(), portletId, strutsAction,
424                            requestPortletAuthenticationToken)) {
425    
426                            return true;
427                    }
428    
429                    return false;
430            }
431    
432            protected boolean isAccessGrantedByPortletOnPage(
433                            HttpServletRequest request, Portlet portlet)
434                    throws PortalException, SystemException {
435    
436                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
437                            WebKeys.THEME_DISPLAY);
438    
439                    Layout layout = themeDisplay.getLayout();
440    
441                    String portletId = portlet.getPortletId();
442    
443                    if (layout.isTypePanel() &&
444                            isPanelSelectedPortlet(themeDisplay, portletId)) {
445    
446                            return true;
447                    }
448    
449                    LayoutTypePortlet layoutTypePortlet =
450                            themeDisplay.getLayoutTypePortlet();
451    
452                    if ((layoutTypePortlet != null) &&
453                            layoutTypePortlet.hasPortletId(portletId)) {
454    
455                            return true;
456                    }
457    
458                    return false;
459            }
460    
461            protected boolean isAccessGrantedByRuntimePortlet(
462                    HttpServletRequest request, Portlet portlet) {
463    
464                    Boolean renderPortletResource = (Boolean)request.getAttribute(
465                            WebKeys.RENDER_PORTLET_RESOURCE);
466    
467                    if (renderPortletResource != null) {
468                            boolean runtimePortlet = renderPortletResource.booleanValue();
469    
470                            if (runtimePortlet) {
471                                    return true;
472                            }
473                    }
474    
475                    return false;
476            }
477    
478            protected boolean isLayoutConfigurationAllowed(
479                            HttpServletRequest request, Portlet portlet)
480                    throws PortalException, SystemException {
481    
482                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
483                            WebKeys.THEME_DISPLAY);
484    
485                    if (!themeDisplay.isSignedIn()) {
486                            return false;
487                    }
488    
489                    String portletId = portlet.getPortletId();
490    
491                    if (!portletId.equals(PortletKeys.LAYOUTS_ADMIN)) {
492                            return false;
493                    }
494    
495                    PermissionChecker permissionChecker =
496                            themeDisplay.getPermissionChecker();
497    
498                    Layout layout = themeDisplay.getLayout();
499    
500                    Group group = layout.getGroup();
501    
502                    if (group.isSite()) {
503                            if (LayoutPermissionUtil.contains(
504                                            permissionChecker, layout, ActionKeys.CUSTOMIZE) ||
505                                    LayoutPermissionUtil.contains(
506                                            permissionChecker, layout, ActionKeys.UPDATE)) {
507    
508                                    return true;
509                            }
510                    }
511    
512                    if (group.isCompany()) {
513                            if (permissionChecker.isCompanyAdmin()) {
514                                    return true;
515                            }
516                    }
517                    else if (group.isLayoutPrototype()) {
518                            long layoutPrototypeId = group.getClassPK();
519    
520                            if (LayoutPrototypePermissionUtil.contains(
521                                            permissionChecker, layoutPrototypeId,
522                                    ActionKeys.UPDATE)) {
523    
524                                    return true;
525                            }
526                    }
527                    else if (group.isLayoutSetPrototype()) {
528                            long layoutSetPrototypeId = group.getClassPK();
529    
530                            if (LayoutSetPrototypePermissionUtil.contains(
531                                            permissionChecker, layoutSetPrototypeId,
532                                    ActionKeys.UPDATE)) {
533    
534                                    return true;
535                            }
536                    }
537                    else if (group.isOrganization()) {
538                            long organizationId = group.getOrganizationId();
539    
540                            if (OrganizationPermissionUtil.contains(
541                                            permissionChecker, organizationId, ActionKeys.UPDATE)) {
542    
543                                    return true;
544                            }
545                    }
546                    else if (group.isUserGroup()) {
547                            long scopeGroupId = themeDisplay.getScopeGroupId();
548    
549                            if (GroupPermissionUtil.contains(
550                                            permissionChecker, scopeGroupId, ActionKeys.UPDATE)) {
551    
552                                    return true;
553                            }
554                    }
555                    else if (group.isUser()) {
556                            return true;
557                    }
558    
559                    return false;
560            }
561    
562            protected boolean isPanelSelectedPortlet(
563                    ThemeDisplay themeDisplay, String portletId) {
564    
565                    Layout layout = themeDisplay.getLayout();
566    
567                    String panelSelectedPortlets = layout.getTypeSettingsProperty(
568                            "panelSelectedPortlets");
569    
570                    if (Validator.isNotNull(panelSelectedPortlets)) {
571                            String[] panelSelectedPortletsArray = StringUtil.split(
572                                    panelSelectedPortlets);
573    
574                            return ArrayUtil.contains(panelSelectedPortletsArray, portletId);
575                    }
576    
577                    return false;
578            }
579    
580            protected ActionResult processActionException(
581                    HttpServletRequest request, HttpServletResponse response,
582                    Portlet portlet, PrincipalException e) {
583    
584                    if (_log.isDebugEnabled()) {
585                            _log.debug(e);
586                    }
587    
588                    String url = getOriginalURL(request);
589    
590                    _log.warn(
591                            "Reject process action for " + url + " on " +
592                                    portlet.getPortletId());
593    
594                    return ActionResult.EMPTY_ACTION_RESULT;
595            }
596    
597            protected void processRenderException(
598                            HttpServletRequest request, HttpServletResponse response,
599                            Portlet portlet)
600                    throws PortletContainerException {
601    
602                    String portletContent = null;
603    
604                    if (portlet.isShowPortletAccessDenied()) {
605                            portletContent = "/html/portal/portlet_access_denied.jsp";
606                    }
607    
608                    try {
609                            if (portletContent != null) {
610                                    RequestDispatcher requestDispatcher =
611                                            request.getRequestDispatcher(portletContent);
612    
613                                    requestDispatcher.include(request, response);
614                            }
615                    }
616                    catch (Exception ex) {
617                            throw new PortletContainerException(ex);
618                    }
619            }
620    
621            protected void processServeResourceException(
622                    HttpServletRequest request, HttpServletResponse response,
623                    Portlet portlet, PrincipalException e) {
624    
625                    if (_log.isDebugEnabled()) {
626                            _log.debug(e);
627                    }
628    
629                    String url = getOriginalURL(request);
630    
631                    response.setHeader(
632                            HttpHeaders.CACHE_CONTROL,
633                            HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
634    
635                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
636    
637                    _log.warn(
638                            "Reject serveResource for " + url + " on " +
639                                    portlet.getPortletId());
640            }
641    
642            private static Log _log = LogFactoryUtil.getLog(
643                    SecurityPortletContainerWrapper.class);
644    
645            private PortletContainer _portletContainer;
646    
647    }