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