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