001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.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                    PortletConfigImpl portletConfigImpl =
151                            (PortletConfigImpl)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, portletConfigImpl, actionRequest,
171                                    actionResponse);
172                    }
173                    catch (Exception e) {
174                            String exceptionId =
175                                    WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
176                                            portletConfigImpl.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                            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    
215            public void process(
216                            RenderRequest renderRequest, RenderResponse renderResponse)
217                    throws IOException, ServletException {
218    
219                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
220                            renderRequest);
221                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
222                            renderResponse);
223    
224                    process(request, response);
225            }
226    
227            public void process(
228                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
229                    throws IOException, ServletException {
230    
231                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
232                            resourceRequest);
233                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
234                            resourceResponse);
235    
236                    process(request, response);
237            }
238    
239            @Override
240            public ActionMapping processMapping(
241                    HttpServletRequest request, HttpServletResponse response, String path) {
242    
243                    if (path == null) {
244                            return null;
245                    }
246    
247                    ActionMapping actionMapping = null;
248    
249                    long companyId = PortalUtil.getCompanyId(request);
250    
251                    PortletConfigImpl portletConfigImpl =
252                            (PortletConfigImpl)request.getAttribute(
253                                    JavaConstants.JAVAX_PORTLET_CONFIG);
254    
255                    try {
256                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
257                                    companyId, portletConfigImpl.getPortletId());
258    
259                            if (StrutsActionRegistryUtil.getAction(path) != null) {
260                                    actionMapping = (ActionMapping)moduleConfig.findActionConfig(
261                                            path);
262    
263                                    if (actionMapping == null) {
264                                            actionMapping = new ActionMapping();
265    
266                                            actionMapping.setModuleConfig(moduleConfig);
267                                            actionMapping.setPath(path);
268    
269                                            request.setAttribute(Globals.MAPPING_KEY, actionMapping);
270                                    }
271                            }
272                            else if (moduleConfig.findActionConfig(path) != null) {
273                                    actionMapping = super.processMapping(request, response, path);
274                            }
275                            else if (Validator.isNotNull(portlet.getParentStrutsPath())) {
276                                    int pos = path.indexOf(StringPool.SLASH, 1);
277    
278                                    String parentPath =
279                                            StringPool.SLASH + portlet.getParentStrutsPath() +
280                                                    path.substring(pos);
281    
282                                    if (StrutsActionRegistryUtil.getAction(parentPath) != null) {
283                                            actionMapping =
284                                                    (ActionMapping)moduleConfig.findActionConfig(path);
285    
286                                            if (actionMapping == null) {
287                                                    actionMapping = new ActionMapping();
288    
289                                                    actionMapping.setModuleConfig(moduleConfig);
290                                                    actionMapping.setPath(parentPath);
291    
292                                                    request.setAttribute(
293                                                            Globals.MAPPING_KEY, actionMapping);
294                                            }
295                                    }
296                                    else if (moduleConfig.findActionConfig(parentPath) != null) {
297                                            actionMapping = super.processMapping(
298                                                    request, response, parentPath);
299                                    }
300                            }
301                    }
302                    catch (Exception e) {
303                    }
304    
305                    if (actionMapping == null) {
306                            MessageResources messageResources = getInternal();
307    
308                            String msg = messageResources.getMessage("processInvalid");
309    
310                            _log.error("User ID " + request.getRemoteUser());
311                            _log.error("Current URL " + PortalUtil.getCurrentURL(request));
312                            _log.error("Referer " + request.getHeader("Referer"));
313                            _log.error("Remote address " + request.getRemoteAddr());
314    
315                            _log.error(msg + " " + path);
316                    }
317    
318                    return actionMapping;
319            }
320    
321            @Override
322            protected void doForward(
323                            String uri, HttpServletRequest request,
324                            HttpServletResponse response)
325                    throws IOException, ServletException {
326    
327                    doInclude(uri, request, response);
328            }
329    
330            @Override
331            protected void doInclude(
332                            String uri, HttpServletRequest request,
333                            HttpServletResponse response)
334                    throws IOException, ServletException {
335    
336                    PortletConfigImpl portletConfigImpl =
337                            (PortletConfigImpl)request.getAttribute(
338                                    JavaConstants.JAVAX_PORTLET_CONFIG);
339    
340                    PortletContext portletContext = portletConfigImpl.getPortletContext();
341    
342                    PortletRequest portletRequest = (PortletRequest)request.getAttribute(
343                            JavaConstants.JAVAX_PORTLET_REQUEST);
344    
345                    PortletResponse portletResponse = (PortletResponse)request.getAttribute(
346                            JavaConstants.JAVAX_PORTLET_RESPONSE);
347    
348                    PortletRequestDispatcherImpl portletRequestDispatcher =
349                            (PortletRequestDispatcherImpl)portletContext.getRequestDispatcher(
350                                    StrutsUtil.TEXT_HTML_DIR + uri);
351    
352                    try {
353                            if (portletRequestDispatcher == null) {
354                                    _log.error(uri + " is not a valid include");
355                            }
356                            else {
357                                    portletRequestDispatcher.include(
358                                            portletRequest, portletResponse, true);
359                            }
360                    }
361                    catch (PortletException pe) {
362                            Throwable cause = pe.getCause();
363    
364                            if (cause instanceof ServletException) {
365                                    throw (ServletException)cause;
366                            }
367                            else {
368                                    _log.error(cause, cause);
369                            }
370                    }
371            }
372    
373            @Override
374            protected Action processActionCreate(
375                            HttpServletRequest request, HttpServletResponse response,
376                            ActionMapping actionMapping)
377                    throws IOException {
378    
379                    PortletActionAdapter portletActionAdapter =
380                            (PortletActionAdapter)StrutsActionRegistryUtil.getAction(
381                                    actionMapping.getPath());
382    
383                    if (portletActionAdapter != null) {
384                            ActionConfig actionConfig = moduleConfig.findActionConfig(
385                                    actionMapping.getPath());
386    
387                            if (actionConfig != null) {
388                                    PortletAction originalPortletAction =
389                                            (PortletAction)super.processActionCreate(
390                                                    request, response, actionMapping);
391    
392                                    portletActionAdapter.setOriginalPortletAction(
393                                            originalPortletAction);
394                            }
395    
396                            return portletActionAdapter;
397                    }
398    
399                    return super.processActionCreate(request, response, actionMapping);
400            }
401    
402            @Override
403            protected ActionForm processActionForm(
404                    HttpServletRequest request, HttpServletResponse response,
405                    ActionMapping actionMapping) {
406    
407                    ActionForm actionForm = super.processActionForm(
408                            request, response, actionMapping);
409    
410                    if (actionForm instanceof InitializableActionForm) {
411                            InitializableActionForm initializableActionForm =
412                                    (InitializableActionForm)actionForm;
413    
414                            initializableActionForm.init(request, response, actionMapping);
415                    }
416    
417                    return actionForm;
418            }
419    
420            @Override
421            protected ActionForward processActionPerform(
422                            HttpServletRequest request, HttpServletResponse response,
423                            Action action, ActionForm actionForm, ActionMapping actionMapping)
424                    throws IOException, ServletException {
425    
426                    PortletConfigImpl portletConfigImpl =
427                            (PortletConfigImpl)request.getAttribute(
428                                    JavaConstants.JAVAX_PORTLET_CONFIG);
429    
430                    String exceptionId =
431                            WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
432                                    portletConfigImpl.getPortletId();
433    
434                    Exception e = (Exception)request.getAttribute(exceptionId);
435    
436                    if (e != null) {
437                            return processException(
438                                    request, response, e, actionForm, actionMapping);
439                    }
440                    else {
441                            return super.processActionPerform(
442                                    request, response, action, actionForm, actionMapping);
443                    }
444            }
445    
446            @Override
447            protected void processForwardConfig(
448                            HttpServletRequest request, HttpServletResponse response,
449                            ForwardConfig forward)
450                    throws IOException, ServletException {
451    
452                    if (forward == null) {
453                            _log.error("Forward does not exist");
454                    }
455                    else {
456    
457                            // Don't render a null path. This is useful if you're sending a file
458                            // in an exclusive window state.
459    
460                            if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
461                                    return;
462                            }
463                    }
464    
465                    super.processForwardConfig(request, response, forward);
466            }
467    
468            @Override
469            protected HttpServletRequest processMultipart(HttpServletRequest request) {
470    
471                    // Disable Struts from automatically wrapping a multipart request
472    
473                    return request;
474            }
475    
476            @Override
477            protected String processPath(
478                    HttpServletRequest request, HttpServletResponse response) {
479    
480                    String path = request.getParameter("struts_action");
481    
482                    if (_log.isDebugEnabled()) {
483                            _log.debug("Getting request parameter path " + path);
484                    }
485    
486                    if (Validator.isNull(path)) {
487                            if (_log.isDebugEnabled()) {
488                                    _log.debug("Getting request attribute path " + path);
489                            }
490    
491                            path = (String)request.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
492                    }
493    
494                    if (path == null) {
495                            PortletConfigImpl portletConfigImpl =
496                                    (PortletConfigImpl)request.getAttribute(
497                                            JavaConstants.JAVAX_PORTLET_CONFIG);
498    
499                            _log.error(
500                                    portletConfigImpl.getPortletName() +
501                                            " does not have any paths specified");
502                    }
503                    else {
504                            if (_log.isDebugEnabled()) {
505                                    _log.debug("Processing path " + path);
506                            }
507                    }
508    
509                    return path;
510            }
511    
512            @Override
513            protected boolean processRoles(
514                            HttpServletRequest request, HttpServletResponse response,
515                            ActionMapping actionMapping)
516                    throws IOException, ServletException {
517    
518                    return processRoles(request, response, actionMapping, false);
519            }
520    
521            protected boolean processRoles(
522                            HttpServletRequest request, HttpServletResponse response,
523                            ActionMapping actionMapping, boolean action)
524                    throws IOException, ServletException {
525    
526                    long companyId = PortalUtil.getCompanyId(request);
527    
528                    String path = actionMapping.getPath();
529    
530                    try {
531                            PortletConfigImpl portletConfigImpl =
532                                    (PortletConfigImpl)request.getAttribute(
533                                            JavaConstants.JAVAX_PORTLET_CONFIG);
534    
535                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
536                                    companyId, portletConfigImpl.getPortletId());
537    
538                            if (portlet == null) {
539                                    return false;
540                            }
541    
542                            String strutsPath = path.substring(
543                                    1, path.lastIndexOf(CharPool.SLASH));
544    
545                            if (!strutsPath.equals(portlet.getStrutsPath()) &&
546                                    !strutsPath.equals(portlet.getParentStrutsPath())) {
547                                    if (_log.isWarnEnabled()) {
548                                            _log.warn(
549                                                    "The struts path " + strutsPath + " does not belong " +
550                                                            "to portlet " + portlet.getPortletId() + ". " +
551                                                                    "Check the definition in liferay-portlet.xml");
552                                    }
553    
554                                    throw new PrincipalException();
555                            }
556                            else if (portlet.isActive()) {
557                                    if (PortalUtil.isAllowAddPortletDefaultResource(
558                                                    request, portlet)) {
559    
560                                            PortalUtil.addPortletDefaultResource(request, portlet);
561                                    }
562    
563                                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
564                                            WebKeys.THEME_DISPLAY);
565    
566                                    Layout layout = themeDisplay.getLayout();
567                                    PermissionChecker permissionChecker =
568                                            themeDisplay.getPermissionChecker();
569    
570                                    if (!PortletPermissionUtil.contains(
571                                                    permissionChecker, layout, portlet, ActionKeys.VIEW)) {
572    
573                                            throw new PrincipalException();
574                                    }
575                            }
576                            else if (!portlet.isActive()) {
577                                    ForwardConfig forwardConfig = actionMapping.findForward(
578                                            _PATH_PORTAL_PORTLET_INACTIVE);
579    
580                                    if (!action) {
581                                            processForwardConfig(request, response, forwardConfig);
582                                    }
583    
584                                    return false;
585                            }
586                    }
587                    catch (Exception e) {
588                            if (_log.isWarnEnabled()) {
589                                    _log.warn(e.getMessage());
590                            }
591    
592                            ForwardConfig forwardConfig = actionMapping.findForward(
593                                    _PATH_PORTAL_PORTLET_ACCESS_DENIED);
594    
595                            if (!action) {
596                                    processForwardConfig(request, response, forwardConfig);
597                            }
598    
599                            return false;
600                    }
601    
602                    return true;
603            }
604    
605            protected boolean processValidateAction(
606                    HttpServletRequest request, HttpServletResponse response,
607                    ActionForm actionForm, ActionMapping actionMapping) {
608    
609                    if (actionForm == null) {
610                            return true;
611                    }
612    
613                    if (request.getAttribute(Globals.CANCEL_KEY) != null) {
614                            return true;
615                    }
616    
617                    if (!actionMapping.getValidate()) {
618                            return true;
619                    }
620    
621                    ActionErrors errors = actionForm.validate(actionMapping, request);
622    
623                    if ((errors == null) || errors.isEmpty()) {
624                            return true;
625                    }
626    
627                    if (actionForm.getMultipartRequestHandler() != null) {
628                            actionForm.getMultipartRequestHandler().rollback();
629                    }
630    
631                    String input = actionMapping.getInput();
632    
633                    if (input == null) {
634                            _log.error("Validation failed but no input form is available");
635    
636                            return false;
637                    }
638    
639                    request.setAttribute(Globals.ERROR_KEY, errors);
640    
641                    // Struts normally calls internalModuleRelativeForward which breaks
642                    // if called inside processAction
643    
644                    request.setAttribute(PortletAction.getForwardKey(request), input);
645    
646                    return false;
647            }
648    
649            private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
650                    "/portal/portlet_access_denied";
651    
652            private static final String _PATH_PORTAL_PORTLET_INACTIVE =
653                    "/portal/portlet_inactive";
654    
655            private static Log _log = LogFactoryUtil.getLog(
656                    PortletRequestProcessor.class);
657    
658    }