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.portal.struts;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.portlet.LiferayPortletConfig;
020    import com.liferay.portal.kernel.portlet.LiferayPortletRequestDispatcher;
021    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
022    import com.liferay.portal.kernel.util.CharPool;
023    import com.liferay.portal.kernel.util.JavaConstants;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.model.Layout;
027    import com.liferay.portal.model.Portlet;
028    import com.liferay.portal.security.auth.PrincipalException;
029    import com.liferay.portal.security.permission.ActionKeys;
030    import com.liferay.portal.security.permission.PermissionChecker;
031    import com.liferay.portal.service.PortletLocalServiceUtil;
032    import com.liferay.portal.service.permission.PortletPermissionUtil;
033    import com.liferay.portal.theme.ThemeDisplay;
034    import com.liferay.portal.util.PortalUtil;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.portal.util.WebKeys;
037    import com.liferay.portlet.ActionResponseImpl;
038    
039    import java.io.IOException;
040    
041    import java.lang.reflect.Constructor;
042    
043    import javax.portlet.ActionRequest;
044    import javax.portlet.ActionResponse;
045    import javax.portlet.EventRequest;
046    import javax.portlet.EventResponse;
047    import javax.portlet.PortletContext;
048    import javax.portlet.PortletException;
049    import javax.portlet.PortletRequest;
050    import javax.portlet.PortletResponse;
051    import javax.portlet.RenderRequest;
052    import javax.portlet.RenderResponse;
053    import javax.portlet.ResourceRequest;
054    import javax.portlet.ResourceResponse;
055    
056    import javax.servlet.ServletException;
057    import javax.servlet.http.HttpServletRequest;
058    import javax.servlet.http.HttpServletResponse;
059    
060    import org.apache.struts.Globals;
061    import org.apache.struts.action.Action;
062    import org.apache.struts.action.ActionErrors;
063    import org.apache.struts.action.ActionForm;
064    import org.apache.struts.action.ActionForward;
065    import org.apache.struts.action.ActionMapping;
066    import org.apache.struts.action.ActionServlet;
067    import org.apache.struts.config.ActionConfig;
068    import org.apache.struts.config.ForwardConfig;
069    import org.apache.struts.config.ModuleConfig;
070    import org.apache.struts.tiles.TilesRequestProcessor;
071    import org.apache.struts.util.MessageResources;
072    
073    /**
074     * @author Brian Wing Shun Chan
075     * @author Raymond Augé
076     */
077    public class PortletRequestProcessor extends TilesRequestProcessor {
078    
079            public static PortletRequestProcessor getInstance(
080                            ActionServlet servlet, ModuleConfig moduleConfig)
081                    throws ServletException {
082    
083                    try {
084                            String className = PropsValues.STRUTS_PORTLET_REQUEST_PROCESSOR;
085    
086                            Class<?> clazz = Class.forName(className);
087    
088                            Constructor<?> constructor = clazz.getConstructor(
089                                    ActionServlet.class, ModuleConfig.class);
090    
091                            PortletRequestProcessor portletReqProcessor =
092                                    (PortletRequestProcessor)constructor.newInstance(
093                                            servlet, moduleConfig);
094    
095                            return portletReqProcessor;
096                    }
097                    catch (Exception e) {
098                            _log.error(e);
099    
100                            return new PortletRequestProcessor(servlet, moduleConfig);
101                    }
102            }
103    
104            public PortletRequestProcessor(
105                            ActionServlet actionServlet, ModuleConfig moduleConfig)
106                    throws ServletException {
107    
108                    init(actionServlet, moduleConfig);
109            }
110    
111            public void process(
112                            ActionRequest actionRequest, ActionResponse actionResponse,
113                            String path)
114                    throws IOException, ServletException {
115    
116                    ActionResponseImpl actionResponseImpl =
117                            (ActionResponseImpl)actionResponse;
118    
119                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
120                            actionRequest);
121                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
122                            actionResponse);
123    
124                    ActionMapping actionMapping = processMapping(request, response, path);
125    
126                    if (actionMapping == null) {
127                            return;
128                    }
129    
130                    if (!processRoles(request, response, actionMapping, true)) {
131                            return;
132                    }
133    
134                    ActionForm actionForm = processActionForm(
135                            request, response, actionMapping);
136    
137                    processPopulate(request, response, actionForm, actionMapping);
138    
139                    if (!processValidateAction(
140                                    request, response, actionForm, actionMapping)) {
141    
142                            return;
143                    }
144    
145                    PortletAction portletAction = (PortletAction)processActionCreate(
146                            request, response, actionMapping);
147    
148                    if (portletAction == null) {
149                            return;
150                    }
151    
152                    LiferayPortletConfig liferayPortletConfig =
153                            (LiferayPortletConfig)actionRequest.getAttribute(
154                                    JavaConstants.JAVAX_PORTLET_CONFIG);
155    
156                    try {
157                            if (portletAction.isCheckMethodOnProcessAction()) {
158                                    if (!PortalUtil.isMethodPost(actionRequest)) {
159                                            String currentURL = PortalUtil.getCurrentURL(actionRequest);
160    
161                                            if (_log.isWarnEnabled()) {
162                                                    _log.warn(
163                                                            "This URL can only be invoked using POST: " +
164                                                                    currentURL);
165                                            }
166    
167                                            throw new PrincipalException(currentURL);
168                                    }
169                            }
170    
171                            portletAction.processAction(
172                                    actionMapping, actionForm, liferayPortletConfig, actionRequest,
173                                    actionResponse);
174                    }
175                    catch (Exception e) {
176                            String exceptionId =
177                                    WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
178                                            liferayPortletConfig.getPortletId();
179    
180                            actionRequest.setAttribute(exceptionId, e);
181                    }
182    
183                    String forward = (String)actionRequest.getAttribute(
184                            PortletAction.getForwardKey(actionRequest));
185    
186                    if (forward != null) {
187                            String queryString = StringPool.BLANK;
188    
189                            int pos = forward.indexOf(CharPool.QUESTION);
190    
191                            if (pos != -1) {
192                                    queryString = forward.substring(pos + 1);
193                                    forward = forward.substring(0, pos);
194                            }
195    
196                            ActionForward actionForward = actionMapping.findForward(forward);
197    
198                            if ((actionForward != null) && actionForward.getRedirect()) {
199                                    String forwardPath = actionForward.getPath();
200    
201                                    if (forwardPath.startsWith(StringPool.SLASH)) {
202                                            LiferayPortletURL forwardURL =
203                                                    (LiferayPortletURL)actionResponseImpl.createRenderURL();
204    
205                                            forwardURL.setParameter("struts_action", forwardPath);
206    
207                                            StrutsURLEncoder.setParameters(forwardURL, queryString);
208    
209                                            forwardPath = forwardURL.toString();
210                                    }
211    
212                                    actionResponse.sendRedirect(forwardPath);
213                            }
214                    }
215            }
216    
217            public void process(EventRequest eventRequest, EventResponse eventResponse)
218                    throws IOException, ServletException {
219    
220                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
221                            eventRequest);
222                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
223                            eventResponse);
224    
225                    process(request, response);
226            }
227    
228            public void process(
229                            RenderRequest renderRequest, RenderResponse renderResponse)
230                    throws IOException, ServletException {
231    
232                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
233                            renderRequest);
234                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
235                            renderResponse);
236    
237                    process(request, response);
238            }
239    
240            public void process(
241                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
242                    throws IOException, ServletException {
243    
244                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
245                            resourceRequest);
246                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
247                            resourceResponse);
248    
249                    process(request, response);
250            }
251    
252            @Override
253            public ActionMapping processMapping(
254                    HttpServletRequest request, HttpServletResponse response, String path) {
255    
256                    if (path == null) {
257                            return null;
258                    }
259    
260                    ActionMapping actionMapping = null;
261    
262                    long companyId = PortalUtil.getCompanyId(request);
263    
264                    LiferayPortletConfig liferayPortletConfig =
265                            (LiferayPortletConfig)request.getAttribute(
266                                    JavaConstants.JAVAX_PORTLET_CONFIG);
267    
268                    try {
269                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
270                                    companyId, liferayPortletConfig.getPortletId());
271    
272                            if (StrutsActionRegistryUtil.getAction(path) != null) {
273                                    actionMapping = (ActionMapping)moduleConfig.findActionConfig(
274                                            path);
275    
276                                    if (actionMapping == null) {
277                                            actionMapping = new ActionMapping();
278    
279                                            actionMapping.setModuleConfig(moduleConfig);
280                                            actionMapping.setPath(path);
281    
282                                            request.setAttribute(Globals.MAPPING_KEY, actionMapping);
283                                    }
284                            }
285                            else if (moduleConfig.findActionConfig(path) != null) {
286                                    actionMapping = super.processMapping(request, response, path);
287                            }
288                            else if (Validator.isNotNull(portlet.getParentStrutsPath())) {
289                                    int pos = path.indexOf(StringPool.SLASH, 1);
290    
291                                    String parentPath =
292                                            StringPool.SLASH + portlet.getParentStrutsPath() +
293                                                    path.substring(pos);
294    
295                                    if (StrutsActionRegistryUtil.getAction(parentPath) != null) {
296                                            actionMapping =
297                                                    (ActionMapping)moduleConfig.findActionConfig(
298                                                            parentPath);
299    
300                                            if (actionMapping == null) {
301                                                    actionMapping = new ActionMapping();
302    
303                                                    actionMapping.setModuleConfig(moduleConfig);
304                                                    actionMapping.setPath(parentPath);
305    
306                                                    request.setAttribute(
307                                                            Globals.MAPPING_KEY, actionMapping);
308                                            }
309                                    }
310                                    else if (moduleConfig.findActionConfig(parentPath) != null) {
311                                            actionMapping = super.processMapping(
312                                                    request, response, parentPath);
313                                    }
314                            }
315                    }
316                    catch (Exception e) {
317                    }
318    
319                    if (actionMapping == null) {
320                            MessageResources messageResources = getInternal();
321    
322                            String msg = messageResources.getMessage("processInvalid");
323    
324                            _log.error("User ID " + request.getRemoteUser());
325                            _log.error("Current URL " + PortalUtil.getCurrentURL(request));
326                            _log.error("Referer " + request.getHeader("Referer"));
327                            _log.error("Remote address " + request.getRemoteAddr());
328    
329                            _log.error(msg + " " + path);
330                    }
331    
332                    return actionMapping;
333            }
334    
335            @Override
336            protected void doForward(
337                            String uri, HttpServletRequest request,
338                            HttpServletResponse response)
339                    throws IOException, ServletException {
340    
341                    doInclude(uri, request, response);
342            }
343    
344            @Override
345            protected void doInclude(
346                            String uri, HttpServletRequest request,
347                            HttpServletResponse response)
348                    throws IOException, ServletException {
349    
350                    LiferayPortletConfig liferayPortletConfig =
351                            (LiferayPortletConfig)request.getAttribute(
352                                    JavaConstants.JAVAX_PORTLET_CONFIG);
353    
354                    PortletContext portletContext =
355                            liferayPortletConfig.getPortletContext();
356    
357                    PortletRequest portletRequest = (PortletRequest)request.getAttribute(
358                            JavaConstants.JAVAX_PORTLET_REQUEST);
359    
360                    PortletResponse portletResponse = (PortletResponse)request.getAttribute(
361                            JavaConstants.JAVAX_PORTLET_RESPONSE);
362    
363                    LiferayPortletRequestDispatcher liferayPortletRequestDispatcher =
364                            (LiferayPortletRequestDispatcher)
365                                    portletContext.getRequestDispatcher(
366                                            StrutsUtil.TEXT_HTML_DIR + uri);
367    
368                    try {
369                            if (liferayPortletRequestDispatcher == null) {
370                                    _log.error(uri + " is not a valid include");
371                            }
372                            else {
373                                    liferayPortletRequestDispatcher.include(
374                                            portletRequest, portletResponse, true);
375                            }
376                    }
377                    catch (PortletException pe) {
378                            Throwable cause = pe.getCause();
379    
380                            if (cause instanceof ServletException) {
381                                    throw (ServletException)cause;
382                            }
383                            else {
384                                    _log.error(cause, cause);
385                            }
386                    }
387            }
388    
389            @Override
390            protected Action processActionCreate(
391                            HttpServletRequest request, HttpServletResponse response,
392                            ActionMapping actionMapping)
393                    throws IOException {
394    
395                    PortletActionAdapter portletActionAdapter =
396                            (PortletActionAdapter)StrutsActionRegistryUtil.getAction(
397                                    actionMapping.getPath());
398    
399                    if (portletActionAdapter != null) {
400                            ActionConfig actionConfig = moduleConfig.findActionConfig(
401                                    actionMapping.getPath());
402    
403                            if (actionConfig != null) {
404                                    PortletAction originalPortletAction =
405                                            (PortletAction)super.processActionCreate(
406                                                    request, response, actionMapping);
407    
408                                    portletActionAdapter.setOriginalPortletAction(
409                                            originalPortletAction);
410                            }
411    
412                            return portletActionAdapter;
413                    }
414    
415                    return super.processActionCreate(request, response, actionMapping);
416            }
417    
418            @Override
419            protected ActionForm processActionForm(
420                    HttpServletRequest request, HttpServletResponse response,
421                    ActionMapping actionMapping) {
422    
423                    ActionForm actionForm = super.processActionForm(
424                            request, response, actionMapping);
425    
426                    if (actionForm instanceof InitializableActionForm) {
427                            InitializableActionForm initializableActionForm =
428                                    (InitializableActionForm)actionForm;
429    
430                            initializableActionForm.init(request, response, actionMapping);
431                    }
432    
433                    return actionForm;
434            }
435    
436            @Override
437            protected ActionForward processActionPerform(
438                            HttpServletRequest request, HttpServletResponse response,
439                            Action action, ActionForm actionForm, ActionMapping actionMapping)
440                    throws IOException, ServletException {
441    
442                    LiferayPortletConfig liferayPortletConfig =
443                            (LiferayPortletConfig)request.getAttribute(
444                                    JavaConstants.JAVAX_PORTLET_CONFIG);
445    
446                    String exceptionId =
447                            WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
448                                    liferayPortletConfig.getPortletId();
449    
450                    Exception e = (Exception)request.getAttribute(exceptionId);
451    
452                    if (e != null) {
453                            return processException(
454                                    request, response, e, actionForm, actionMapping);
455                    }
456                    else {
457                            return super.processActionPerform(
458                                    request, response, action, actionForm, actionMapping);
459                    }
460            }
461    
462            @Override
463            protected void processForwardConfig(
464                            HttpServletRequest request, HttpServletResponse response,
465                            ForwardConfig forward)
466                    throws IOException, ServletException {
467    
468                    if (forward == null) {
469                            _log.error("Forward does not exist");
470                    }
471                    else {
472    
473                            // Don't render a null path. This is useful if you're sending a file
474                            // in an exclusive window state.
475    
476                            if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
477                                    return;
478                            }
479                    }
480    
481                    super.processForwardConfig(request, response, forward);
482            }
483    
484            @Override
485            protected HttpServletRequest processMultipart(HttpServletRequest request) {
486    
487                    // Disable Struts from automatically wrapping a multipart request
488    
489                    return request;
490            }
491    
492            @Override
493            protected String processPath(
494                    HttpServletRequest request, HttpServletResponse response) {
495    
496                    String path = request.getParameter("struts_action");
497    
498                    if (_log.isDebugEnabled()) {
499                            _log.debug("Getting request parameter path " + path);
500                    }
501    
502                    if (Validator.isNull(path)) {
503                            if (_log.isDebugEnabled()) {
504                                    _log.debug("Getting request attribute path " + path);
505                            }
506    
507                            path = (String)request.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
508                    }
509    
510                    if (path == null) {
511                            LiferayPortletConfig liferayPortletConfig =
512                                    (LiferayPortletConfig)request.getAttribute(
513                                            JavaConstants.JAVAX_PORTLET_CONFIG);
514    
515                            _log.error(
516                                    liferayPortletConfig.getPortletName() +
517                                            " does not have any paths specified");
518                    }
519                    else {
520                            if (_log.isDebugEnabled()) {
521                                    _log.debug("Processing path " + path);
522                            }
523    
524                            request.removeAttribute(WebKeys.PORTLET_STRUTS_ACTION);
525                    }
526    
527                    return path;
528            }
529    
530            @Override
531            protected boolean processRoles(
532                            HttpServletRequest request, HttpServletResponse response,
533                            ActionMapping actionMapping)
534                    throws IOException, ServletException {
535    
536                    return processRoles(request, response, actionMapping, false);
537            }
538    
539            protected boolean processRoles(
540                            HttpServletRequest request, HttpServletResponse response,
541                            ActionMapping actionMapping, boolean action)
542                    throws IOException, ServletException {
543    
544                    long companyId = PortalUtil.getCompanyId(request);
545    
546                    String path = actionMapping.getPath();
547    
548                    try {
549                            LiferayPortletConfig liferayPortletConfig =
550                                    (LiferayPortletConfig)request.getAttribute(
551                                            JavaConstants.JAVAX_PORTLET_CONFIG);
552    
553                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
554                                    companyId, liferayPortletConfig.getPortletId());
555    
556                            if (portlet == null) {
557                                    return false;
558                            }
559    
560                            String strutsPath = path.substring(
561                                    1, path.lastIndexOf(CharPool.SLASH));
562    
563                            if (!strutsPath.equals(portlet.getStrutsPath()) &&
564                                    !strutsPath.equals(portlet.getParentStrutsPath())) {
565    
566                                    if (_log.isWarnEnabled()) {
567                                            _log.warn(
568                                                    "The struts path " + strutsPath + " does not belong " +
569                                                            "to portlet " + portlet.getPortletId() + ". " +
570                                                                    "Check the definition in liferay-portlet.xml");
571                                    }
572    
573                                    throw new PrincipalException();
574                            }
575                            else if (portlet.isActive()) {
576                                    if (PortalUtil.isAllowAddPortletDefaultResource(
577                                                    request, portlet)) {
578    
579                                            PortalUtil.addPortletDefaultResource(request, portlet);
580                                    }
581    
582                                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
583                                            WebKeys.THEME_DISPLAY);
584    
585                                    Layout layout = themeDisplay.getLayout();
586                                    PermissionChecker permissionChecker =
587                                            themeDisplay.getPermissionChecker();
588    
589                                    if (!PortletPermissionUtil.contains(
590                                                    permissionChecker, layout, portlet, ActionKeys.VIEW)) {
591    
592                                            throw new PrincipalException();
593                                    }
594                            }
595                            else if (!portlet.isActive()) {
596                                    ForwardConfig forwardConfig = actionMapping.findForward(
597                                            _PATH_PORTAL_PORTLET_INACTIVE);
598    
599                                    if (!action) {
600                                            processForwardConfig(request, response, forwardConfig);
601                                    }
602    
603                                    return false;
604                            }
605                    }
606                    catch (Exception e) {
607                            if (_log.isWarnEnabled()) {
608                                    _log.warn(e.getMessage());
609                            }
610    
611                            ForwardConfig forwardConfig = actionMapping.findForward(
612                                    _PATH_PORTAL_PORTLET_ACCESS_DENIED);
613    
614                            if (!action) {
615                                    processForwardConfig(request, response, forwardConfig);
616                            }
617    
618                            return false;
619                    }
620    
621                    return true;
622            }
623    
624            protected boolean processValidateAction(
625                    HttpServletRequest request, HttpServletResponse response,
626                    ActionForm actionForm, ActionMapping actionMapping) {
627    
628                    if (actionForm == null) {
629                            return true;
630                    }
631    
632                    if (request.getAttribute(Globals.CANCEL_KEY) != null) {
633                            return true;
634                    }
635    
636                    if (!actionMapping.getValidate()) {
637                            return true;
638                    }
639    
640                    ActionErrors errors = actionForm.validate(actionMapping, request);
641    
642                    if ((errors == null) || errors.isEmpty()) {
643                            return true;
644                    }
645    
646                    if (actionForm.getMultipartRequestHandler() != null) {
647                            actionForm.getMultipartRequestHandler().rollback();
648                    }
649    
650                    String input = actionMapping.getInput();
651    
652                    if (input == null) {
653                            _log.error("Validation failed but no input form is available");
654    
655                            return false;
656                    }
657    
658                    request.setAttribute(Globals.ERROR_KEY, errors);
659    
660                    // Struts normally calls internalModuleRelativeForward which breaks if
661                    // called inside processAction
662    
663                    request.setAttribute(PortletAction.getForwardKey(request), input);
664    
665                    return false;
666            }
667    
668            private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
669                    "/portal/portlet_access_denied";
670    
671            private static final String _PATH_PORTAL_PORTLET_INACTIVE =
672                    "/portal/portlet_inactive";
673    
674            private static Log _log = LogFactoryUtil.getLog(
675                    PortletRequestProcessor.class);
676    
677    }