1   /**
2    * Copyright (c) 2000-2007 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.struts;
24  
25  import com.liferay.portal.kernel.portlet.LiferayPortletURL;
26  import com.liferay.portal.kernel.security.permission.ActionKeys;
27  import com.liferay.portal.kernel.security.permission.PermissionChecker;
28  import com.liferay.portal.kernel.util.JavaConstants;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.Validator;
31  import com.liferay.portal.model.Layout;
32  import com.liferay.portal.model.Portlet;
33  import com.liferay.portal.model.User;
34  import com.liferay.portal.security.auth.PrincipalException;
35  import com.liferay.portal.service.PortletLocalServiceUtil;
36  import com.liferay.portal.service.permission.PortletPermissionUtil;
37  import com.liferay.portal.theme.ThemeDisplay;
38  import com.liferay.portal.util.PortalUtil;
39  import com.liferay.portal.util.PropsUtil;
40  import com.liferay.portal.util.WebKeys;
41  import com.liferay.portlet.ActionResponseImpl;
42  import com.liferay.portlet.PortletConfigImpl;
43  import com.liferay.portlet.PortletRequestDispatcherImpl;
44  
45  import java.io.IOException;
46  
47  import java.lang.reflect.Constructor;
48  
49  import javax.portlet.ActionRequest;
50  import javax.portlet.ActionResponse;
51  import javax.portlet.PortletException;
52  import javax.portlet.RenderRequest;
53  import javax.portlet.RenderResponse;
54  
55  import javax.servlet.ServletException;
56  import javax.servlet.http.HttpServletRequest;
57  import javax.servlet.http.HttpServletResponse;
58  
59  import org.apache.commons.logging.Log;
60  import org.apache.commons.logging.LogFactory;
61  import org.apache.struts.Globals;
62  import org.apache.struts.action.Action;
63  import org.apache.struts.action.ActionErrors;
64  import org.apache.struts.action.ActionForm;
65  import org.apache.struts.action.ActionForward;
66  import org.apache.struts.action.ActionMapping;
67  import org.apache.struts.action.ActionServlet;
68  import org.apache.struts.config.ForwardConfig;
69  import org.apache.struts.config.ModuleConfig;
70  import org.apache.struts.tiles.TilesRequestProcessor;
71  
72  /**
73   * <a href="PortletRequestProcessor.java.html"><b><i>View Source</i></b></a>
74   *
75   * @author Brian Wing Shun Chan
76   *
77   */
78  public class PortletRequestProcessor extends TilesRequestProcessor {
79  
80      public static PortletRequestProcessor getInstance(
81              ActionServlet servlet, ModuleConfig config)
82          throws ServletException {
83  
84          try {
85              String className = PropsUtil.get(
86                  PropsUtil.STRUTS_PORTLET_REQUEST_PROCESSOR);
87  
88              Class clazz = Class.forName(className);
89  
90              Constructor constructor = clazz.getConstructor(
91                  new Class[] {
92                      ActionServlet.class, ModuleConfig.class
93                  }
94              );
95  
96              PortletRequestProcessor portletReqProcessor =
97                  (PortletRequestProcessor)constructor.newInstance(
98                      new Object[] {
99                          servlet, config
100                     }
101                 );
102 
103             return portletReqProcessor;
104         }
105         catch (Exception e) {
106             _log.error(e);
107 
108             return new PortletRequestProcessor(servlet, config);
109         }
110     }
111 
112     public PortletRequestProcessor(ActionServlet servlet, ModuleConfig config)
113         throws ServletException {
114 
115         init(servlet, config);
116     }
117 
118     public void process(RenderRequest req, RenderResponse res)
119         throws IOException, ServletException {
120 
121         HttpServletRequest httpReq = PortalUtil.getHttpServletRequest(req);
122         HttpServletResponse httpRes = PortalUtil.getHttpServletResponse(res);
123 
124         process(httpReq, httpRes);
125     }
126 
127     public void process(ActionRequest req, ActionResponse res, String path)
128         throws IOException, ServletException {
129 
130         ActionResponseImpl resImpl = (ActionResponseImpl)res;
131 
132         HttpServletRequest httpReq = PortalUtil.getHttpServletRequest(req);
133         HttpServletResponse httpRes = PortalUtil.getHttpServletResponse(res);
134 
135         ActionMapping mapping = processMapping(httpReq, httpRes, path);
136 
137         if (mapping == null) {
138             return;
139         }
140 
141         if (!processRoles(httpReq, httpRes, mapping, true)) {
142             return;
143         }
144 
145         ActionForm form = processActionForm(httpReq, httpRes, mapping);
146 
147         processPopulate(httpReq, httpRes, form, mapping);
148 
149         if (!processValidateAction(httpReq, httpRes, form, mapping)) {
150             return;
151         }
152 
153         PortletAction action =
154             (PortletAction)processActionCreate(httpReq, httpRes, mapping);
155 
156         if (action == null) {
157             return;
158         }
159 
160         PortletConfigImpl portletConfig = (PortletConfigImpl)req.getAttribute(
161             JavaConstants.JAVAX_PORTLET_CONFIG);
162 
163         try {
164             action.processAction(mapping, form, portletConfig, req, res);
165         }
166         catch (Exception e) {
167             String exceptionId =
168                 WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
169                     portletConfig.getPortletId();
170 
171             req.setAttribute(exceptionId, e);
172         }
173 
174         String forward =
175             (String)req.getAttribute(PortletAction.getForwardKey(req));
176 
177         if (forward != null) {
178             String queryString = StringPool.BLANK;
179 
180             int pos = forward.indexOf("?");
181 
182             if (pos != -1) {
183                 queryString = forward.substring(pos + 1, forward.length());
184                 forward = forward.substring(0, pos);
185             }
186 
187             ActionForward actionForward = mapping.findForward(forward);
188 
189             if ((actionForward != null) && (actionForward.getRedirect())) {
190                 String forwardPath = actionForward.getPath();
191 
192                 if (forwardPath.startsWith("/")) {
193                     LiferayPortletURL forwardURL =
194                         (LiferayPortletURL)resImpl.createRenderURL();
195 
196                     forwardURL.setParameter("struts_action", forwardPath);
197 
198                     StrutsURLEncoder.setParameters(forwardURL, queryString);
199 
200                     forwardPath = forwardURL.toString();
201                 }
202 
203                 res.sendRedirect(forwardPath);
204             }
205         }
206     }
207 
208     protected ActionForm processActionForm(
209         HttpServletRequest req, HttpServletResponse res,
210         ActionMapping mapping) {
211 
212         ActionForm form = super.processActionForm(req, res, mapping);
213 
214         if (form instanceof InitializableActionForm) {
215             InitializableActionForm initForm = (InitializableActionForm)form;
216 
217             initForm.init(req, res, mapping);
218         }
219 
220         return form;
221     }
222 
223     protected ActionForward processActionPerform(
224             HttpServletRequest req, HttpServletResponse res, Action action,
225             ActionForm form, ActionMapping mapping)
226         throws IOException, ServletException {
227 
228         PortletConfigImpl portletConfig = (PortletConfigImpl)req.getAttribute(
229             JavaConstants.JAVAX_PORTLET_CONFIG);
230 
231         String exceptionId =
232             WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
233                 portletConfig.getPortletId();
234 
235         Exception e = (Exception)req.getAttribute(exceptionId);
236 
237         if (e != null) {
238             return processException(req, res, e, form, mapping);
239         }
240         else {
241             return super.processActionPerform(req, res, action, form, mapping);
242         }
243     }
244 
245     protected void processForwardConfig(
246             HttpServletRequest req, HttpServletResponse res,
247             ForwardConfig forward)
248         throws IOException, ServletException {
249 
250         if (forward == null) {
251             _log.error("Forward does not exist");
252         }
253         else {
254 
255             // Don't render a null path. This is useful if you're sending a file
256             // in an exclusive window state.
257 
258             if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
259                 return;
260             }
261         }
262 
263         super.processForwardConfig(req, res, forward);
264     }
265 
266     public ActionMapping processMapping(
267             HttpServletRequest req, HttpServletResponse res, String path)
268         throws IOException {
269 
270         if (path == null) {
271             return null;
272         }
273 
274         return super.processMapping(req, res, path);
275     }
276 
277     protected boolean processRoles(
278             HttpServletRequest req, HttpServletResponse res,
279             ActionMapping mapping)
280         throws IOException, ServletException {
281 
282         return processRoles(req, res, mapping, false);
283     }
284 
285     protected boolean processRoles(
286             HttpServletRequest req, HttpServletResponse res,
287             ActionMapping mapping, boolean action)
288         throws IOException, ServletException {
289 
290         User user = null;
291 
292         try {
293             user = PortalUtil.getUser(req);
294         }
295         catch (Exception e) {
296         }
297 
298         if (user == null) {
299             return true;
300         }
301 
302         String path = mapping.getPath();
303 
304         try {
305             PortletConfigImpl portletConfig =
306                 (PortletConfigImpl)req.getAttribute(
307                     JavaConstants.JAVAX_PORTLET_CONFIG);
308 
309             Portlet portlet = PortletLocalServiceUtil.getPortletById(
310                 user.getCompanyId(), portletConfig.getPortletId());
311 
312             if (portlet == null) {
313                 return false;
314             }
315 
316             String strutsPath = path.substring(
317                 1, path.lastIndexOf(StringPool.SLASH));
318 
319             if (!strutsPath.equals(portlet.getStrutsPath())) {
320                 if (_log.isWarnEnabled()) {
321                     _log.warn(
322                         "The struts path " + strutsPath + " does not belong " +
323                             "to portlet " + portlet.getPortletId() + ". " +
324                                 "Check the definition in liferay-portlet.xml");
325                 }
326 
327                 throw new PrincipalException();
328             }
329             else if (portlet.isActive()) {
330                 ThemeDisplay themeDisplay =
331                     (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
332 
333                 Layout layout = themeDisplay.getLayout();
334                 PermissionChecker permissionChecker =
335                     themeDisplay.getPermissionChecker();
336 
337                 if (!PortletPermissionUtil.contains(
338                         permissionChecker, layout.getPlid(), portlet,
339                         ActionKeys.VIEW)) {
340 
341                     throw new PrincipalException();
342                 }
343             }
344             else if (!portlet.isActive()) {
345                 ForwardConfig forwardConfig =
346                     mapping.findForward(_PATH_PORTAL_PORTLET_INACTIVE);
347 
348                 if (!action) {
349                     processForwardConfig(req, res, forwardConfig);
350                 }
351 
352                 return false;
353             }
354         }
355         catch (Exception e) {
356             if (_log.isWarnEnabled()) {
357                 _log.warn(e.getMessage());
358             }
359 
360             ForwardConfig forwardConfig =
361                 mapping.findForward(_PATH_PORTAL_PORTLET_ACCESS_DENIED);
362 
363             if (!action) {
364                 processForwardConfig(req, res, forwardConfig);
365             }
366 
367             return false;
368         }
369 
370         return true;
371     }
372 
373     protected boolean processValidateAction(
374             HttpServletRequest req, HttpServletResponse res, ActionForm form,
375             ActionMapping mapping)
376         throws IOException, ServletException {
377 
378         if (form == null) {
379             return true;
380         }
381 
382         if (req.getAttribute(Globals.CANCEL_KEY) != null) {
383             return true;
384         }
385 
386         if (!mapping.getValidate()) {
387             return true;
388         }
389 
390         ActionErrors errors = form.validate(mapping, req);
391 
392         if ((errors == null) || errors.isEmpty()) {
393             return true;
394         }
395 
396         if (form.getMultipartRequestHandler() != null) {
397             form.getMultipartRequestHandler().rollback();
398         }
399 
400         String input = mapping.getInput();
401 
402         if (input == null) {
403             _log.error("Validation failed but no input form is available");
404 
405             return false;
406         }
407 
408         req.setAttribute(Globals.ERROR_KEY, errors);
409 
410         // Struts normally calls internalModuleRelativeForward which breaks
411         // if called inside processAction
412 
413         req.setAttribute(PortletAction.getForwardKey(req), input);
414 
415         return false;
416     }
417 
418     protected void doForward(
419             String uri, HttpServletRequest req, HttpServletResponse res)
420         throws IOException, ServletException {
421 
422         doInclude(uri, req, res);
423     }
424 
425     protected void doInclude(
426             String uri, HttpServletRequest req, HttpServletResponse res)
427         throws IOException, ServletException {
428 
429         PortletConfigImpl portletConfig = (PortletConfigImpl)req.getAttribute(
430             JavaConstants.JAVAX_PORTLET_CONFIG);
431 
432         RenderRequest renderRequest = (RenderRequest)req.getAttribute(
433             JavaConstants.JAVAX_PORTLET_REQUEST);
434 
435         RenderResponse renderResponse = (RenderResponse)req.getAttribute(
436             JavaConstants.JAVAX_PORTLET_RESPONSE);
437 
438         PortletRequestDispatcherImpl prd = (PortletRequestDispatcherImpl)
439             portletConfig.getPortletContext().getRequestDispatcher(
440                 StrutsUtil.TEXT_HTML_DIR + uri);
441 
442         try {
443             if (prd == null) {
444                 _log.error(uri + " is not a valid include");
445             }
446             else {
447                 prd.include(renderRequest, renderResponse, true);
448             }
449         }
450         catch (PortletException pe) {
451             Throwable cause = pe.getCause();
452 
453             if (cause instanceof ServletException) {
454                 throw (ServletException)cause;
455             }
456             else {
457                 _log.error(cause, cause);
458             }
459         }
460     }
461 
462     protected HttpServletRequest processMultipart(HttpServletRequest req) {
463 
464         // Disable Struts from automatically wrapping a multipart request
465 
466         return req;
467     }
468 
469     protected String processPath(
470             HttpServletRequest req, HttpServletResponse res)
471         throws IOException {
472 
473         String path = req.getParameter("struts_action");
474 
475         if (Validator.isNull(path)) {
476             path = (String)req.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
477         }
478 
479         if (path == null) {
480             PortletConfigImpl portletConfig =
481                 (PortletConfigImpl)req.getAttribute(
482                     JavaConstants.JAVAX_PORTLET_CONFIG);
483 
484             _log.error(
485                 portletConfig.getPortletName() +
486                     " does not have any paths specified");
487         }
488         else {
489             if (_log.isDebugEnabled()) {
490                 _log.debug("Processing path " + path);
491             }
492         }
493 
494         return path;
495     }
496 
497     private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
498         "/portal/portlet_access_denied";
499 
500     private static final String _PATH_PORTAL_PORTLET_INACTIVE =
501         "/portal/portlet_inactive";
502 
503     private static Log _log = LogFactory.getLog(PortletRequestProcessor.class);
504 
505 }