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