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