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