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 (!isValidPortletId(portlet.getPortletId())) {
187                            if (_log.isWarnEnabled()) {
188                                    _log.warn("Invalid portlet id " + portlet.getPortletId());
189                            }
190    
191                            throw new PrincipalException();
192                    }
193    
194                    if (portlet.isUndeployedPortlet()) {
195                            return;
196                    }
197    
198                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
199    
200                    if (layout.isTypeControlPanel()) {
201                            isAccessAllowedToControlPanelPortlet(request, portlet);
202    
203                            return;
204                    }
205    
206                    if (isAccessAllowedToLayoutPortlet(request, portlet)) {
207                            PortalUtil.addPortletDefaultResource(request, portlet);
208    
209                            if (hasAccessPermission(request, portlet)) {
210                                    return;
211                            }
212                    }
213    
214                    throw new PrincipalException();
215            }
216    
217            protected void checkAction(HttpServletRequest request, Portlet portlet)
218                    throws Exception {
219    
220                    checkCSRFProtection(request, portlet);
221    
222                    check(request, portlet);
223            }
224    
225            protected void checkCSRFProtection(
226                            HttpServletRequest request, Portlet portlet)
227                    throws PortalException {
228    
229                    Map<String, String> initParams = portlet.getInitParams();
230    
231                    boolean checkAuthToken = GetterUtil.getBoolean(
232                            initParams.get("check-auth-token"), true);
233    
234                    if (checkAuthToken) {
235                            AuthTokenUtil.checkCSRFToken(
236                                    request, SecurityPortletContainerWrapper.class.getName());
237                    }
238            }
239    
240            protected void checkRender(HttpServletRequest request, Portlet portlet)
241                    throws Exception {
242    
243                    check(request, portlet);
244            }
245    
246            protected void checkResource(HttpServletRequest request, Portlet portlet)
247                    throws Exception {
248    
249                    check(request, portlet);
250            }
251    
252            protected String getOriginalURL(HttpServletRequest request) {
253                    LastPath lastPath = (LastPath)request.getAttribute(WebKeys.LAST_PATH);
254    
255                    if (lastPath == null) {
256                            return String.valueOf(request.getRequestURI());
257                    }
258    
259                    String portalURL = PortalUtil.getPortalURL(request);
260    
261                    return portalURL.concat(
262                            lastPath.getContextPath()).concat(lastPath.getPath());
263            }
264    
265            protected HttpServletRequest getOwnerLayoutRequestWrapper(
266                            HttpServletRequest request, Portlet portlet)
267                    throws Exception {
268    
269                    if (!PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET ||
270                            PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals("render")) {
271    
272                            return request;
273                    }
274    
275                    Layout ownerLayout = null;
276                    LayoutTypePortlet ownerLayoutTypePortlet = null;
277    
278                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
279                            WebKeys.THEME_DISPLAY);
280    
281                    Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
282    
283                    List<LayoutTypePortlet> layoutTypePortlets =
284                            PortletContainerUtil.getLayoutTypePortlets(requestLayout);
285    
286                    for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
287                            if (layoutTypePortlet.hasPortletId(portlet.getPortletId())) {
288                                    ownerLayoutTypePortlet = layoutTypePortlet;
289    
290                                    ownerLayout = layoutTypePortlet.getLayout();
291    
292                                    break;
293                            }
294                    }
295    
296                    if (ownerLayout == null) {
297                            return request;
298                    }
299    
300                    Layout currentLayout = themeDisplay.getLayout();
301    
302                    if (currentLayout.equals(ownerLayout)) {
303                            return request;
304                    }
305    
306                    ThemeDisplay themeDisplayClone = (ThemeDisplay)themeDisplay.clone();
307    
308                    themeDisplayClone.setLayout(ownerLayout);
309                    themeDisplayClone.setLayoutTypePortlet(ownerLayoutTypePortlet);
310    
311                    TempAttributesServletRequest tempAttributesServletRequest =
312                            new TempAttributesServletRequest(request);
313    
314                    tempAttributesServletRequest.setTempAttribute(
315                            WebKeys.LAYOUT, ownerLayout);
316                    tempAttributesServletRequest.setTempAttribute(
317                            WebKeys.THEME_DISPLAY, themeDisplayClone);
318    
319                    return tempAttributesServletRequest;
320            }
321    
322            protected boolean hasAccessPermission(
323                            HttpServletRequest request, Portlet portlet)
324                    throws PortalException, SystemException {
325    
326                    PermissionChecker permissionChecker =
327                            PermissionThreadLocal.getPermissionChecker();
328    
329                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
330                            WebKeys.THEME_DISPLAY);
331    
332                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
333    
334                    PortletMode portletMode = PortletMode.VIEW;
335    
336                    String portletId = portlet.getPortletId();
337                    String ppid = request.getParameter("p_p_id");
338                    String ppmode = request.getParameter("p_p_mode");
339    
340                    if (portletId.equals(ppid) && (ppmode != null)) {
341                            portletMode = PortletModeFactory.getPortletMode(ppmode);
342                    }
343    
344                    return PortletPermissionUtil.hasAccessPermission(
345                            permissionChecker, themeDisplay.getScopeGroupId(), layout, portlet,
346                            portletMode);
347            }
348    
349            protected void isAccessAllowedToControlPanelPortlet(
350                            HttpServletRequest request, Portlet portlet)
351                    throws PortalException, SystemException {
352    
353                    PermissionChecker permissionChecker =
354                            PermissionThreadLocal.getPermissionChecker();
355    
356                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
357                            WebKeys.THEME_DISPLAY);
358    
359                    if (PortletPermissionUtil.hasControlPanelAccessPermission(
360                                    permissionChecker, themeDisplay.getScopeGroupId(), portlet)) {
361    
362                            return;
363                    }
364    
365                    if (isAccessGrantedByRuntimePortlet(request, portlet)) {
366                            return;
367                    }
368    
369                    if (isAccessGrantedByPortletAuthenticationToken(request, portlet)) {
370                            return;
371                    }
372    
373                    throw new PrincipalException();
374            }
375    
376            protected boolean isAccessAllowedToLayoutPortlet(
377                            HttpServletRequest request, Portlet portlet)
378                    throws PortalException, SystemException {
379    
380                    if (isAccessGrantedByRuntimePortlet(request, portlet)) {
381                            return true;
382                    }
383    
384                    if (isAccessGrantedByPortletOnPage(request, portlet)) {
385                            return true;
386                    }
387    
388                    if (isLayoutConfigurationAllowed(request, portlet)) {
389                            return true;
390                    }
391    
392                    if (isAccessGrantedByPortletAuthenticationToken(request, portlet)) {
393                            return true;
394                    }
395    
396                    return false;
397            }
398    
399            protected boolean isAccessGrantedByPortletAuthenticationToken(
400                    HttpServletRequest request, Portlet portlet) {
401    
402                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
403                            WebKeys.THEME_DISPLAY);
404    
405                    String portletId = portlet.getPortletId();
406    
407                    if (!portlet.isAddDefaultResource()) {
408                            return false;
409                    }
410    
411                    if (!PropsValues.PORTLET_ADD_DEFAULT_RESOURCE_CHECK_ENABLED) {
412                            return true;
413                    }
414    
415                    String namespace = PortalUtil.getPortletNamespace(portletId);
416    
417                    String strutsAction = ParamUtil.getString(
418                            request, namespace + "struts_action");
419    
420                    if (Validator.isNull(strutsAction)) {
421                            strutsAction = ParamUtil.getString(request, "struts_action");
422                    }
423    
424                    String requestPortletAuthenticationToken = ParamUtil.getString(
425                            request, "p_p_auth");
426    
427                    if (Validator.isNull(requestPortletAuthenticationToken)) {
428                            HttpServletRequest originalRequest =
429                                    PortalUtil.getOriginalServletRequest(request);
430    
431                            requestPortletAuthenticationToken = ParamUtil.getString(
432                                    originalRequest, "p_p_auth");
433                    }
434    
435                    if (AuthTokenUtil.isValidPortletInvocationToken(
436                                    request, themeDisplay.getPlid(), portletId, strutsAction,
437                            requestPortletAuthenticationToken)) {
438    
439                            return true;
440                    }
441    
442                    return false;
443            }
444    
445            protected boolean isAccessGrantedByPortletOnPage(
446                            HttpServletRequest request, Portlet portlet)
447                    throws PortalException, SystemException {
448    
449                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
450                            WebKeys.THEME_DISPLAY);
451    
452                    Layout layout = themeDisplay.getLayout();
453    
454                    String portletId = portlet.getPortletId();
455    
456                    if (layout.isTypePanel() &&
457                            isPanelSelectedPortlet(themeDisplay, portletId)) {
458    
459                            return true;
460                    }
461    
462                    LayoutTypePortlet layoutTypePortlet =
463                            themeDisplay.getLayoutTypePortlet();
464    
465                    if ((layoutTypePortlet != null) &&
466                            layoutTypePortlet.hasPortletId(portletId)) {
467    
468                            return true;
469                    }
470    
471                    return false;
472            }
473    
474            protected boolean isAccessGrantedByRuntimePortlet(
475                    HttpServletRequest request, Portlet portlet) {
476    
477                    Boolean renderPortletResource = (Boolean)request.getAttribute(
478                            WebKeys.RENDER_PORTLET_RESOURCE);
479    
480                    if (renderPortletResource != null) {
481                            boolean runtimePortlet = renderPortletResource.booleanValue();
482    
483                            if (runtimePortlet) {
484                                    return true;
485                            }
486                    }
487    
488                    return false;
489            }
490    
491            protected boolean isLayoutConfigurationAllowed(
492                            HttpServletRequest request, Portlet portlet)
493                    throws PortalException, SystemException {
494    
495                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
496                            WebKeys.THEME_DISPLAY);
497    
498                    if (!themeDisplay.isSignedIn()) {
499                            return false;
500                    }
501    
502                    String portletId = portlet.getPortletId();
503    
504                    if (!portletId.equals(PortletKeys.LAYOUTS_ADMIN)) {
505                            return false;
506                    }
507    
508                    PermissionChecker permissionChecker =
509                            themeDisplay.getPermissionChecker();
510    
511                    Layout layout = themeDisplay.getLayout();
512    
513                    Group group = layout.getGroup();
514    
515                    if (group.isSite()) {
516                            if (LayoutPermissionUtil.contains(
517                                            permissionChecker, layout, ActionKeys.CUSTOMIZE) ||
518                                    LayoutPermissionUtil.contains(
519                                            permissionChecker, layout, ActionKeys.UPDATE)) {
520    
521                                    return true;
522                            }
523                    }
524    
525                    if (group.isCompany()) {
526                            if (permissionChecker.isCompanyAdmin()) {
527                                    return true;
528                            }
529                    }
530                    else if (group.isLayoutPrototype()) {
531                            long layoutPrototypeId = group.getClassPK();
532    
533                            if (LayoutPrototypePermissionUtil.contains(
534                                            permissionChecker, layoutPrototypeId,
535                                    ActionKeys.UPDATE)) {
536    
537                                    return true;
538                            }
539                    }
540                    else if (group.isLayoutSetPrototype()) {
541                            long layoutSetPrototypeId = group.getClassPK();
542    
543                            if (LayoutSetPrototypePermissionUtil.contains(
544                                            permissionChecker, layoutSetPrototypeId,
545                                    ActionKeys.UPDATE)) {
546    
547                                    return true;
548                            }
549                    }
550                    else if (group.isOrganization()) {
551                            long organizationId = group.getOrganizationId();
552    
553                            if (OrganizationPermissionUtil.contains(
554                                            permissionChecker, organizationId, ActionKeys.UPDATE)) {
555    
556                                    return true;
557                            }
558                    }
559                    else if (group.isUserGroup()) {
560                            long scopeGroupId = themeDisplay.getScopeGroupId();
561    
562                            if (GroupPermissionUtil.contains(
563                                            permissionChecker, scopeGroupId, ActionKeys.UPDATE)) {
564    
565                                    return true;
566                            }
567                    }
568                    else if (group.isUser()) {
569                            return true;
570                    }
571    
572                    return false;
573            }
574    
575            protected boolean isPanelSelectedPortlet(
576                    ThemeDisplay themeDisplay, String portletId) {
577    
578                    Layout layout = themeDisplay.getLayout();
579    
580                    String panelSelectedPortlets = layout.getTypeSettingsProperty(
581                            "panelSelectedPortlets");
582    
583                    if (Validator.isNotNull(panelSelectedPortlets)) {
584                            String[] panelSelectedPortletsArray = StringUtil.split(
585                                    panelSelectedPortlets);
586    
587                            return ArrayUtil.contains(panelSelectedPortletsArray, portletId);
588                    }
589    
590                    return false;
591            }
592    
593            protected boolean isValidPortletId(String portletId) {
594                    for (int i = 0; i < portletId.length(); i++) {
595                            char c = portletId.charAt(i);
596    
597                            if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) {
598                                    continue;
599                            }
600    
601                            if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) {
602                                    continue;
603                            }
604    
605                            if ((c >= CharPool.NUMBER_0) && (c <= CharPool.NUMBER_9)) {
606                                    continue;
607                            }
608    
609                            if (c == CharPool.UNDERLINE) {
610                                    continue;
611                            }
612    
613                            return false;
614                    }
615    
616                    return true;
617            }
618    
619            protected ActionResult processActionException(
620                    HttpServletRequest request, HttpServletResponse response,
621                    Portlet portlet, PrincipalException e) {
622    
623                    if (_log.isDebugEnabled()) {
624                            _log.debug(e);
625                    }
626    
627                    String url = getOriginalURL(request);
628    
629                    if (_log.isWarnEnabled()) {
630                            _log.warn(
631                                    "Reject process action for " + url + " on " +
632                                            portlet.getPortletId());
633                    }
634    
635                    return ActionResult.EMPTY_ACTION_RESULT;
636            }
637    
638            protected void processRenderException(
639                            HttpServletRequest request, HttpServletResponse response,
640                            Portlet portlet)
641                    throws PortletContainerException {
642    
643                    String portletContent = null;
644    
645                    if (portlet.isShowPortletAccessDenied()) {
646                            portletContent = "/html/portal/portlet_access_denied.jsp";
647                    }
648    
649                    try {
650                            if (portletContent != null) {
651                                    RequestDispatcher requestDispatcher =
652                                            request.getRequestDispatcher(portletContent);
653    
654                                    requestDispatcher.include(request, response);
655                            }
656                    }
657                    catch (Exception ex) {
658                            throw new PortletContainerException(ex);
659                    }
660            }
661    
662            protected void processServeResourceException(
663                    HttpServletRequest request, HttpServletResponse response,
664                    Portlet portlet, PrincipalException e) {
665    
666                    if (_log.isDebugEnabled()) {
667                            _log.debug(e);
668                    }
669    
670                    String url = getOriginalURL(request);
671    
672                    response.setHeader(
673                            HttpHeaders.CACHE_CONTROL,
674                            HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
675    
676                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
677    
678                    if (_log.isWarnEnabled()) {
679                            _log.warn(
680                                    "Reject serveResource for " + url + " on " +
681                                            portlet.getPortletId());
682                    }
683            }
684    
685            private static Log _log = LogFactoryUtil.getLog(
686                    SecurityPortletContainerWrapper.class);
687    
688            private PortletContainer _portletContainer;
689    
690    }