1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.struts;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  import com.liferay.portal.kernel.portlet.LiferayPortletURL;
20  import com.liferay.portal.kernel.util.JavaConstants;
21  import com.liferay.portal.kernel.util.StringPool;
22  import com.liferay.portal.kernel.util.Validator;
23  import com.liferay.portal.model.Layout;
24  import com.liferay.portal.model.Portlet;
25  import com.liferay.portal.security.auth.PrincipalException;
26  import com.liferay.portal.security.permission.ActionKeys;
27  import com.liferay.portal.security.permission.PermissionChecker;
28  import com.liferay.portal.service.PortletLocalServiceUtil;
29  import com.liferay.portal.service.permission.PortletPermissionUtil;
30  import com.liferay.portal.theme.ThemeDisplay;
31  import com.liferay.portal.util.PortalUtil;
32  import com.liferay.portal.util.PropsValues;
33  import com.liferay.portal.util.WebKeys;
34  import com.liferay.portlet.ActionResponseImpl;
35  import com.liferay.portlet.PortletConfigImpl;
36  import com.liferay.portlet.PortletRequestDispatcherImpl;
37  
38  import java.io.IOException;
39  
40  import java.lang.reflect.Constructor;
41  
42  import javax.portlet.ActionRequest;
43  import javax.portlet.ActionResponse;
44  import javax.portlet.PortletContext;
45  import javax.portlet.PortletException;
46  import javax.portlet.PortletRequest;
47  import javax.portlet.PortletResponse;
48  import javax.portlet.RenderRequest;
49  import javax.portlet.RenderResponse;
50  import javax.portlet.ResourceRequest;
51  import javax.portlet.ResourceResponse;
52  
53  import javax.servlet.ServletException;
54  import javax.servlet.http.HttpServletRequest;
55  import javax.servlet.http.HttpServletResponse;
56  
57  import org.apache.struts.Globals;
58  import org.apache.struts.action.Action;
59  import org.apache.struts.action.ActionErrors;
60  import org.apache.struts.action.ActionForm;
61  import org.apache.struts.action.ActionForward;
62  import org.apache.struts.action.ActionMapping;
63  import org.apache.struts.action.ActionServlet;
64  import org.apache.struts.config.ForwardConfig;
65  import org.apache.struts.config.ModuleConfig;
66  import org.apache.struts.tiles.TilesRequestProcessor;
67  
68  /**
69   * <a href="PortletRequestProcessor.java.html"><b><i>View Source</i></b></a>
70   *
71   * @author Brian Wing Shun Chan
72   */
73  public class PortletRequestProcessor extends TilesRequestProcessor {
74  
75      public static PortletRequestProcessor getInstance(
76              ActionServlet servlet, ModuleConfig moduleConfig)
77          throws ServletException {
78  
79          try {
80              String className = PropsValues.STRUTS_PORTLET_REQUEST_PROCESSOR;
81  
82              Class<?> clazz = Class.forName(className);
83  
84              Constructor<?> constructor = clazz.getConstructor(
85                  new Class[] {
86                      ActionServlet.class, ModuleConfig.class
87                  }
88              );
89  
90              PortletRequestProcessor portletReqProcessor =
91                  (PortletRequestProcessor)constructor.newInstance(
92                      new Object[] {
93                          servlet, moduleConfig
94                      }
95                  );
96  
97              return portletReqProcessor;
98          }
99          catch (Exception e) {
100             _log.error(e);
101 
102             return new PortletRequestProcessor(servlet, moduleConfig);
103         }
104     }
105 
106     public PortletRequestProcessor(
107             ActionServlet actionServlet, ModuleConfig moduleConfig)
108         throws ServletException {
109 
110         init(actionServlet, moduleConfig);
111     }
112 
113     public void process(
114             ActionRequest actionRequest, ActionResponse actionResponse,
115             String path)
116         throws IOException, ServletException {
117 
118         ActionResponseImpl actionResponseImpl =
119             (ActionResponseImpl)actionResponse;
120 
121         HttpServletRequest request = PortalUtil.getHttpServletRequest(
122             actionRequest);
123         HttpServletResponse response = PortalUtil.getHttpServletResponse(
124             actionResponse);
125 
126         ActionMapping mapping = processMapping(request, response, path);
127 
128         if (mapping == null) {
129             return;
130         }
131 
132         if (!processRoles(request, response, mapping, true)) {
133             return;
134         }
135 
136         ActionForm form = processActionForm(request, response, mapping);
137 
138         processPopulate(request, response, form, mapping);
139 
140         if (!processValidateAction(request, response, form, mapping)) {
141             return;
142         }
143 
144         PortletAction action =
145             (PortletAction)processActionCreate(request, response, mapping);
146 
147         if (action == null) {
148             return;
149         }
150 
151         PortletConfigImpl portletConfigImpl =
152             (PortletConfigImpl)actionRequest.getAttribute(
153                 JavaConstants.JAVAX_PORTLET_CONFIG);
154 
155         try {
156             if (action.isCheckMethodOnProcessAction()) {
157                 if (!PortalUtil.isMethodPost(actionRequest)) {
158                     String currentURL = PortalUtil.getCurrentURL(actionRequest);
159 
160                     if (_log.isWarnEnabled()) {
161                         _log.warn(
162                             "This URL can only be invoked using POST: " +
163                                 currentURL);
164                     }
165 
166                     throw new PrincipalException(currentURL);
167                 }
168             }
169 
170             action.processAction(
171                 mapping, form, portletConfigImpl, actionRequest,
172                 actionResponse);
173         }
174         catch (Exception e) {
175             String exceptionId =
176                 WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
177                     portletConfigImpl.getPortletId();
178 
179             actionRequest.setAttribute(exceptionId, e);
180         }
181 
182         String forward = (String)actionRequest.getAttribute(
183             PortletAction.getForwardKey(actionRequest));
184 
185         if (forward != null) {
186             String queryString = StringPool.BLANK;
187 
188             int pos = forward.indexOf(StringPool.QUESTION);
189 
190             if (pos != -1) {
191                 queryString = forward.substring(pos + 1, forward.length());
192                 forward = forward.substring(0, pos);
193             }
194 
195             ActionForward actionForward = mapping.findForward(forward);
196 
197             if ((actionForward != null) && (actionForward.getRedirect())) {
198                 String forwardPath = actionForward.getPath();
199 
200                 if (forwardPath.startsWith(StringPool.SLASH)) {
201                     LiferayPortletURL forwardURL =
202                         (LiferayPortletURL)actionResponseImpl.createRenderURL();
203 
204                     forwardURL.setParameter("struts_action", forwardPath);
205 
206                     StrutsURLEncoder.setParameters(forwardURL, queryString);
207 
208                     forwardPath = forwardURL.toString();
209                 }
210 
211                 actionResponse.sendRedirect(forwardPath);
212             }
213         }
214     }
215 
216     public void process(
217             RenderRequest renderRequest, RenderResponse renderResponse)
218         throws IOException, ServletException {
219 
220         HttpServletRequest request = PortalUtil.getHttpServletRequest(
221             renderRequest);
222         HttpServletResponse response = PortalUtil.getHttpServletResponse(
223             renderResponse);
224 
225         process(request, response);
226     }
227 
228     public void process(
229             ResourceRequest resourceRequest, ResourceResponse resourceResponse)
230         throws IOException, ServletException {
231 
232         HttpServletRequest request = PortalUtil.getHttpServletRequest(
233             resourceRequest);
234         HttpServletResponse response = PortalUtil.getHttpServletResponse(
235             resourceResponse);
236 
237         process(request, response);
238     }
239 
240     protected void doForward(
241             String uri, HttpServletRequest request,
242             HttpServletResponse response)
243         throws IOException, ServletException {
244 
245         doInclude(uri, request, response);
246     }
247 
248     protected void doInclude(
249             String uri, HttpServletRequest request,
250             HttpServletResponse response)
251         throws IOException, ServletException {
252 
253         PortletConfigImpl portletConfig =
254             (PortletConfigImpl)request.getAttribute(
255                 JavaConstants.JAVAX_PORTLET_CONFIG);
256 
257         PortletContext portletContext = portletConfig.getPortletContext();
258 
259         PortletRequest portletRequest = (PortletRequest)request.getAttribute(
260             JavaConstants.JAVAX_PORTLET_REQUEST);
261 
262         PortletResponse portletResponse = (PortletResponse)request.getAttribute(
263             JavaConstants.JAVAX_PORTLET_RESPONSE);
264 
265         PortletRequestDispatcherImpl portletRequestDispatcher =
266             (PortletRequestDispatcherImpl)portletContext.getRequestDispatcher(
267                 StrutsUtil.TEXT_HTML_DIR + uri);
268 
269         try {
270             if (portletRequestDispatcher == null) {
271                 _log.error(uri + " is not a valid include");
272             }
273             else {
274                 portletRequestDispatcher.include(
275                     portletRequest, portletResponse, true);
276             }
277         }
278         catch (PortletException pe) {
279             Throwable cause = pe.getCause();
280 
281             if (cause instanceof ServletException) {
282                 throw (ServletException)cause;
283             }
284             else {
285                 _log.error(cause, cause);
286             }
287         }
288     }
289 
290     protected ActionForm processActionForm(
291         HttpServletRequest request, HttpServletResponse response,
292         ActionMapping mapping) {
293 
294         ActionForm form = super.processActionForm(request, response, mapping);
295 
296         if (form instanceof InitializableActionForm) {
297             InitializableActionForm initForm = (InitializableActionForm)form;
298 
299             initForm.init(request, response, mapping);
300         }
301 
302         return form;
303     }
304 
305     protected ActionForward processActionPerform(
306             HttpServletRequest request, HttpServletResponse response,
307             Action action, ActionForm form, ActionMapping mapping)
308         throws IOException, ServletException {
309 
310         PortletConfigImpl portletConfig =
311             (PortletConfigImpl)request.getAttribute(
312                 JavaConstants.JAVAX_PORTLET_CONFIG);
313 
314         String exceptionId =
315             WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
316                 portletConfig.getPortletId();
317 
318         Exception e = (Exception)request.getAttribute(exceptionId);
319 
320         if (e != null) {
321             return processException(request, response, e, form, mapping);
322         }
323         else {
324             return super.processActionPerform(
325                 request, response, action, form, mapping);
326         }
327     }
328 
329     protected void processForwardConfig(
330             HttpServletRequest request, HttpServletResponse response,
331             ForwardConfig forward)
332         throws IOException, ServletException {
333 
334         if (forward == null) {
335             _log.error("Forward does not exist");
336         }
337         else {
338 
339             // Don't render a null path. This is useful if you're sending a file
340             // in an exclusive window state.
341 
342             if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
343                 return;
344             }
345         }
346 
347         super.processForwardConfig(request, response, forward);
348     }
349 
350     public ActionMapping processMapping(
351             HttpServletRequest request, HttpServletResponse response,
352             String path)
353         throws IOException {
354 
355         if (path == null) {
356             return null;
357         }
358 
359         ActionMapping mapping = super.processMapping(request, response, path);
360 
361         if (mapping == null) {
362             String msg = getInternal().getMessage("processInvalid");
363 
364             _log.error("User ID " + request.getRemoteUser());
365             _log.error("Current URL " + PortalUtil.getCurrentURL(request));
366             _log.error("Referer " + request.getHeader("Referer"));
367             _log.error("Remote address " + request.getRemoteAddr());
368 
369             _log.error(msg + " " + path);
370         }
371 
372         return mapping;
373     }
374 
375     protected HttpServletRequest processMultipart(HttpServletRequest request) {
376 
377         // Disable Struts from automatically wrapping a multipart request
378 
379         return request;
380     }
381 
382     protected String processPath(
383         HttpServletRequest request, HttpServletResponse response) {
384 
385         String path = request.getParameter("struts_action");
386 
387         if (_log.isDebugEnabled()) {
388             _log.debug("Getting request parameter path " + path);
389         }
390 
391         if (Validator.isNull(path)) {
392             if (_log.isDebugEnabled()) {
393                 _log.debug("Getting request attribute path " + path);
394             }
395 
396             path = (String)request.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
397         }
398 
399         if (path == null) {
400             PortletConfigImpl portletConfig =
401                 (PortletConfigImpl)request.getAttribute(
402                     JavaConstants.JAVAX_PORTLET_CONFIG);
403 
404             _log.error(
405                 portletConfig.getPortletName() +
406                     " does not have any paths specified");
407         }
408         else {
409             if (_log.isDebugEnabled()) {
410                 _log.debug("Processing path " + path);
411             }
412         }
413 
414         return path;
415     }
416 
417     protected boolean processRoles(
418             HttpServletRequest request, HttpServletResponse response,
419             ActionMapping mapping)
420         throws IOException, ServletException {
421 
422         return processRoles(request, response, mapping, false);
423     }
424 
425     protected boolean processRoles(
426             HttpServletRequest request, HttpServletResponse response,
427             ActionMapping mapping, boolean action)
428         throws IOException, ServletException {
429 
430         long companyId = PortalUtil.getCompanyId(request);
431 
432         String path = mapping.getPath();
433 
434         try {
435             PortletConfigImpl portletConfig =
436                 (PortletConfigImpl)request.getAttribute(
437                     JavaConstants.JAVAX_PORTLET_CONFIG);
438 
439             Portlet portlet = PortletLocalServiceUtil.getPortletById(
440                 companyId, portletConfig.getPortletId());
441 
442             if (portlet == null) {
443                 return false;
444             }
445 
446             String strutsPath = path.substring(
447                 1, path.lastIndexOf(StringPool.SLASH));
448 
449             if (!strutsPath.equals(portlet.getStrutsPath())) {
450                 if (_log.isWarnEnabled()) {
451                     _log.warn(
452                         "The struts path " + strutsPath + " does not belong " +
453                             "to portlet " + portlet.getPortletId() + ". " +
454                                 "Check the definition in liferay-portlet.xml");
455                 }
456 
457                 throw new PrincipalException();
458             }
459             else if (portlet.isActive()) {
460                 if (PortalUtil.isAllowAddPortletDefaultResource(
461                         request, portlet)) {
462 
463                     PortalUtil.addPortletDefaultResource(request, portlet);
464                 }
465 
466                 ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
467                     WebKeys.THEME_DISPLAY);
468 
469                 Layout layout = themeDisplay.getLayout();
470                 PermissionChecker permissionChecker =
471                     themeDisplay.getPermissionChecker();
472 
473                 if (!PortletPermissionUtil.contains(
474                         permissionChecker, layout.getPlid(), portlet,
475                         ActionKeys.VIEW)) {
476 
477                     throw new PrincipalException();
478                 }
479             }
480             else if (!portlet.isActive()) {
481                 ForwardConfig forwardConfig =
482                     mapping.findForward(_PATH_PORTAL_PORTLET_INACTIVE);
483 
484                 if (!action) {
485                     processForwardConfig(request, response, forwardConfig);
486                 }
487 
488                 return false;
489             }
490         }
491         catch (Exception e) {
492             if (_log.isWarnEnabled()) {
493                 _log.warn(e.getMessage());
494             }
495 
496             ForwardConfig forwardConfig =
497                 mapping.findForward(_PATH_PORTAL_PORTLET_ACCESS_DENIED);
498 
499             if (!action) {
500                 processForwardConfig(request, response, forwardConfig);
501             }
502 
503             return false;
504         }
505 
506         return true;
507     }
508 
509     protected boolean processValidateAction(
510         HttpServletRequest request, HttpServletResponse response,
511         ActionForm form, ActionMapping mapping) {
512 
513         if (form == null) {
514             return true;
515         }
516 
517         if (request.getAttribute(Globals.CANCEL_KEY) != null) {
518             return true;
519         }
520 
521         if (!mapping.getValidate()) {
522             return true;
523         }
524 
525         ActionErrors errors = form.validate(mapping, request);
526 
527         if ((errors == null) || errors.isEmpty()) {
528             return true;
529         }
530 
531         if (form.getMultipartRequestHandler() != null) {
532             form.getMultipartRequestHandler().rollback();
533         }
534 
535         String input = mapping.getInput();
536 
537         if (input == null) {
538             _log.error("Validation failed but no input form is available");
539 
540             return false;
541         }
542 
543         request.setAttribute(Globals.ERROR_KEY, errors);
544 
545         // Struts normally calls internalModuleRelativeForward which breaks
546         // if called inside processAction
547 
548         request.setAttribute(PortletAction.getForwardKey(request), input);
549 
550         return false;
551     }
552 
553     private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
554         "/portal/portlet_access_denied";
555 
556     private static final String _PATH_PORTAL_PORTLET_INACTIVE =
557         "/portal/portlet_inactive";
558 
559     private static Log _log = LogFactoryUtil.getLog(
560         PortletRequestProcessor.class);
561 
562 }