001    /**
002     * Copyright (c) 2000-2012 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.LiferayPortletURL;
020    import com.liferay.portal.kernel.util.CharPool;
021    import com.liferay.portal.kernel.util.JavaConstants;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.Validator;
024    import com.liferay.portal.model.Layout;
025    import com.liferay.portal.model.Portlet;
026    import com.liferay.portal.security.auth.PrincipalException;
027    import com.liferay.portal.security.permission.ActionKeys;
028    import com.liferay.portal.security.permission.PermissionChecker;
029    import com.liferay.portal.service.PortletLocalServiceUtil;
030    import com.liferay.portal.service.permission.PortletPermissionUtil;
031    import com.liferay.portal.theme.ThemeDisplay;
032    import com.liferay.portal.util.PortalUtil;
033    import com.liferay.portal.util.PropsValues;
034    import com.liferay.portal.util.WebKeys;
035    import com.liferay.portlet.ActionResponseImpl;
036    import com.liferay.portlet.PortletConfigImpl;
037    import com.liferay.portlet.PortletRequestDispatcherImpl;
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                    PortletConfigImpl portletConfigImpl =
153                            (PortletConfigImpl)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, portletConfigImpl, actionRequest,
173                                    actionResponse);
174                    }
175                    catch (Exception e) {
176                            String exceptionId =
177                                    WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
178                                            portletConfigImpl.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                    PortletConfigImpl portletConfigImpl =
265                            (PortletConfigImpl)request.getAttribute(
266                                    JavaConstants.JAVAX_PORTLET_CONFIG);
267    
268                    try {
269                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
270                                    companyId, portletConfigImpl.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                    PortletConfigImpl portletConfigImpl =
351                            (PortletConfigImpl)request.getAttribute(
352                                    JavaConstants.JAVAX_PORTLET_CONFIG);
353    
354                    PortletContext portletContext = portletConfigImpl.getPortletContext();
355    
356                    PortletRequest portletRequest = (PortletRequest)request.getAttribute(
357                            JavaConstants.JAVAX_PORTLET_REQUEST);
358    
359                    PortletResponse portletResponse = (PortletResponse)request.getAttribute(
360                            JavaConstants.JAVAX_PORTLET_RESPONSE);
361    
362                    PortletRequestDispatcherImpl portletRequestDispatcher =
363                            (PortletRequestDispatcherImpl)portletContext.getRequestDispatcher(
364                                    StrutsUtil.TEXT_HTML_DIR + uri);
365    
366                    try {
367                            if (portletRequestDispatcher == null) {
368                                    _log.error(uri + " is not a valid include");
369                            }
370                            else {
371                                    portletRequestDispatcher.include(
372                                            portletRequest, portletResponse, true);
373                            }
374                    }
375                    catch (PortletException pe) {
376                            Throwable cause = pe.getCause();
377    
378                            if (cause instanceof ServletException) {
379                                    throw (ServletException)cause;
380                            }
381                            else {
382                                    _log.error(cause, cause);
383                            }
384                    }
385            }
386    
387            @Override
388            protected Action processActionCreate(
389                            HttpServletRequest request, HttpServletResponse response,
390                            ActionMapping actionMapping)
391                    throws IOException {
392    
393                    PortletActionAdapter portletActionAdapter =
394                            (PortletActionAdapter)StrutsActionRegistryUtil.getAction(
395                                    actionMapping.getPath());
396    
397                    if (portletActionAdapter != null) {
398                            ActionConfig actionConfig = moduleConfig.findActionConfig(
399                                    actionMapping.getPath());
400    
401                            if (actionConfig != null) {
402                                    PortletAction originalPortletAction =
403                                            (PortletAction)super.processActionCreate(
404                                                    request, response, actionMapping);
405    
406                                    portletActionAdapter.setOriginalPortletAction(
407                                            originalPortletAction);
408                            }
409    
410                            return portletActionAdapter;
411                    }
412    
413                    return super.processActionCreate(request, response, actionMapping);
414            }
415    
416            @Override
417            protected ActionForm processActionForm(
418                    HttpServletRequest request, HttpServletResponse response,
419                    ActionMapping actionMapping) {
420    
421                    ActionForm actionForm = super.processActionForm(
422                            request, response, actionMapping);
423    
424                    if (actionForm instanceof InitializableActionForm) {
425                            InitializableActionForm initializableActionForm =
426                                    (InitializableActionForm)actionForm;
427    
428                            initializableActionForm.init(request, response, actionMapping);
429                    }
430    
431                    return actionForm;
432            }
433    
434            @Override
435            protected ActionForward processActionPerform(
436                            HttpServletRequest request, HttpServletResponse response,
437                            Action action, ActionForm actionForm, ActionMapping actionMapping)
438                    throws IOException, ServletException {
439    
440                    PortletConfigImpl portletConfigImpl =
441                            (PortletConfigImpl)request.getAttribute(
442                                    JavaConstants.JAVAX_PORTLET_CONFIG);
443    
444                    String exceptionId =
445                            WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
446                                    portletConfigImpl.getPortletId();
447    
448                    Exception e = (Exception)request.getAttribute(exceptionId);
449    
450                    if (e != null) {
451                            return processException(
452                                    request, response, e, actionForm, actionMapping);
453                    }
454                    else {
455                            return super.processActionPerform(
456                                    request, response, action, actionForm, actionMapping);
457                    }
458            }
459    
460            @Override
461            protected void processForwardConfig(
462                            HttpServletRequest request, HttpServletResponse response,
463                            ForwardConfig forward)
464                    throws IOException, ServletException {
465    
466                    if (forward == null) {
467                            _log.error("Forward does not exist");
468                    }
469                    else {
470    
471                            // Don't render a null path. This is useful if you're sending a file
472                            // in an exclusive window state.
473    
474                            if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
475                                    return;
476                            }
477                    }
478    
479                    super.processForwardConfig(request, response, forward);
480            }
481    
482            @Override
483            protected HttpServletRequest processMultipart(HttpServletRequest request) {
484    
485                    // Disable Struts from automatically wrapping a multipart request
486    
487                    return request;
488            }
489    
490            @Override
491            protected String processPath(
492                    HttpServletRequest request, HttpServletResponse response) {
493    
494                    String path = request.getParameter("struts_action");
495    
496                    if (_log.isDebugEnabled()) {
497                            _log.debug("Getting request parameter path " + path);
498                    }
499    
500                    if (Validator.isNull(path)) {
501                            if (_log.isDebugEnabled()) {
502                                    _log.debug("Getting request attribute path " + path);
503                            }
504    
505                            path = (String)request.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
506                    }
507    
508                    if (path == null) {
509                            PortletConfigImpl portletConfigImpl =
510                                    (PortletConfigImpl)request.getAttribute(
511                                            JavaConstants.JAVAX_PORTLET_CONFIG);
512    
513                            _log.error(
514                                    portletConfigImpl.getPortletName() +
515                                            " does not have any paths specified");
516                    }
517                    else {
518                            if (_log.isDebugEnabled()) {
519                                    _log.debug("Processing path " + path);
520                            }
521    
522                            request.removeAttribute(WebKeys.PORTLET_STRUTS_ACTION);
523                    }
524    
525                    return path;
526            }
527    
528            @Override
529            protected boolean processRoles(
530                            HttpServletRequest request, HttpServletResponse response,
531                            ActionMapping actionMapping)
532                    throws IOException, ServletException {
533    
534                    return processRoles(request, response, actionMapping, false);
535            }
536    
537            protected boolean processRoles(
538                            HttpServletRequest request, HttpServletResponse response,
539                            ActionMapping actionMapping, boolean action)
540                    throws IOException, ServletException {
541    
542                    long companyId = PortalUtil.getCompanyId(request);
543    
544                    String path = actionMapping.getPath();
545    
546                    try {
547                            PortletConfigImpl portletConfigImpl =
548                                    (PortletConfigImpl)request.getAttribute(
549                                            JavaConstants.JAVAX_PORTLET_CONFIG);
550    
551                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
552                                    companyId, portletConfigImpl.getPortletId());
553    
554                            if (portlet == null) {
555                                    return false;
556                            }
557    
558                            String strutsPath = path.substring(
559                                    1, path.lastIndexOf(CharPool.SLASH));
560    
561                            if (!strutsPath.equals(portlet.getStrutsPath()) &&
562                                    !strutsPath.equals(portlet.getParentStrutsPath())) {
563    
564                                    if (_log.isWarnEnabled()) {
565                                            _log.warn(
566                                                    "The struts path " + strutsPath + " does not belong " +
567                                                            "to portlet " + portlet.getPortletId() + ". " +
568                                                                    "Check the definition in liferay-portlet.xml");
569                                    }
570    
571                                    throw new PrincipalException();
572                            }
573                            else if (portlet.isActive()) {
574                                    if (PortalUtil.isAllowAddPortletDefaultResource(
575                                                    request, portlet)) {
576    
577                                            PortalUtil.addPortletDefaultResource(request, portlet);
578                                    }
579    
580                                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
581                                            WebKeys.THEME_DISPLAY);
582    
583                                    Layout layout = themeDisplay.getLayout();
584                                    PermissionChecker permissionChecker =
585                                            themeDisplay.getPermissionChecker();
586    
587                                    if (!PortletPermissionUtil.contains(
588                                                    permissionChecker, layout, portlet, ActionKeys.VIEW)) {
589    
590                                            throw new PrincipalException();
591                                    }
592                            }
593                            else if (!portlet.isActive()) {
594                                    ForwardConfig forwardConfig = actionMapping.findForward(
595                                            _PATH_PORTAL_PORTLET_INACTIVE);
596    
597                                    if (!action) {
598                                            processForwardConfig(request, response, forwardConfig);
599                                    }
600    
601                                    return false;
602                            }
603                    }
604                    catch (Exception e) {
605                            if (_log.isWarnEnabled()) {
606                                    _log.warn(e.getMessage());
607                            }
608    
609                            ForwardConfig forwardConfig = actionMapping.findForward(
610                                    _PATH_PORTAL_PORTLET_ACCESS_DENIED);
611    
612                            if (!action) {
613                                    processForwardConfig(request, response, forwardConfig);
614                            }
615    
616                            return false;
617                    }
618    
619                    return true;
620            }
621    
622            protected boolean processValidateAction(
623                    HttpServletRequest request, HttpServletResponse response,
624                    ActionForm actionForm, ActionMapping actionMapping) {
625    
626                    if (actionForm == null) {
627                            return true;
628                    }
629    
630                    if (request.getAttribute(Globals.CANCEL_KEY) != null) {
631                            return true;
632                    }
633    
634                    if (!actionMapping.getValidate()) {
635                            return true;
636                    }
637    
638                    ActionErrors errors = actionForm.validate(actionMapping, request);
639    
640                    if ((errors == null) || errors.isEmpty()) {
641                            return true;
642                    }
643    
644                    if (actionForm.getMultipartRequestHandler() != null) {
645                            actionForm.getMultipartRequestHandler().rollback();
646                    }
647    
648                    String input = actionMapping.getInput();
649    
650                    if (input == null) {
651                            _log.error("Validation failed but no input form is available");
652    
653                            return false;
654                    }
655    
656                    request.setAttribute(Globals.ERROR_KEY, errors);
657    
658                    // Struts normally calls internalModuleRelativeForward which breaks
659                    // if called inside processAction
660    
661                    request.setAttribute(PortletAction.getForwardKey(request), input);
662    
663                    return false;
664            }
665    
666            private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
667                    "/portal/portlet_access_denied";
668    
669            private static final String _PATH_PORTAL_PORTLET_INACTIVE =
670                    "/portal/portlet_inactive";
671    
672            private static Log _log = LogFactoryUtil.getLog(
673                    PortletRequestProcessor.class);
674    
675    }