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