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