From 40fa8afce47870f80bc79efd7501a699d05a57fa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Oct 2010 00:03:09 +0000 Subject: [PATCH] DispatcherPortlet's default resource serving explicitly prevents access to WEB-INF and META-INF (SPR-7540) --- .../handler/SimplePortletHandlerAdapter.java | 13 ++---- .../mvc/SimpleControllerHandlerAdapter.java | 11 ++--- .../web/portlet/util/PortletUtils.java | 44 ++++++++++++++++++- .../Portlet20AnnotationControllerTests.java | 33 ++++++++++++++ 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java index a4ea17611da..b8e85ab7450 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import javax.portlet.EventRequest; import javax.portlet.EventResponse; import javax.portlet.Portlet; import javax.portlet.PortletContext; -import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; @@ -33,6 +32,7 @@ import javax.portlet.ResourceServingPortlet; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.context.PortletContextAware; +import org.springframework.web.portlet.util.PortletUtils; /** * Adapter to use the Portlet interface with the generic DispatcherPortlet. @@ -92,13 +92,8 @@ public class SimplePortletHandlerAdapter implements HandlerAdapter, PortletConte ((ResourceServingPortlet) handler).serveResource(request, response); } else { - // equivalent to Portlet 2.0 GenericPortlet - if (request.getResourceID() != null) { - PortletRequestDispatcher rd = this.portletContext.getRequestDispatcher(request.getResourceID()); - if (rd != null) { - rd.forward(request, response); - } - } + // roughly equivalent to Portlet 2.0 GenericPortlet + PortletUtils.serveResource(request, response, this.portletContext); } return null; } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java index d0a3ba7edbf..918d3ee1fcc 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import javax.portlet.ActionResponse; import javax.portlet.EventRequest; import javax.portlet.EventResponse; import javax.portlet.PortletContext; -import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; @@ -30,6 +29,7 @@ import javax.portlet.ResourceResponse; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.context.PortletContextAware; +import org.springframework.web.portlet.util.PortletUtils; /** * Adapter to use the Controller workflow interface with the generic DispatcherPortlet. @@ -78,12 +78,7 @@ public class SimpleControllerHandlerAdapter implements HandlerAdapter, PortletCo } else { // equivalent to Portlet 2.0 GenericPortlet - if (request.getResourceID() != null) { - PortletRequestDispatcher rd = this.portletContext.getRequestDispatcher(request.getResourceID()); - if (rd != null) { - rd.forward(request, response); - } - } + PortletUtils.serveResource(request, response, this.portletContext); return null; } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletUtils.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletUtils.java index 5ed7ec5a89b..cd9040b1b54 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletUtils.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletUtils.java @@ -18,6 +18,7 @@ package org.springframework.web.portlet.util; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -25,11 +26,16 @@ import java.util.TreeMap; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletContext; +import javax.portlet.PortletException; import javax.portlet.PortletRequest; +import javax.portlet.PortletRequestDispatcher; import javax.portlet.PortletSession; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import javax.servlet.http.Cookie; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import org.springframework.web.util.WebUtils; /** @@ -442,11 +448,47 @@ public abstract class PortletUtils { */ public static void clearAllRenderParameters(ActionResponse response) { try { - response.setRenderParameters(new HashMap()); + response.setRenderParameters(new HashMap(0)); } catch (IllegalStateException ex) { // Ignore in case sendRedirect was already set. } } + /** + * Serve the resource as specified in the given request to the given response, + * using the PortletContext's request dispatcher. + *

This is roughly equivalent to Portlet 2.0 GenericPortlet. + * @param request the current resource request + * @param response the current resource response + * @param context the current Portlet's PortletContext + * @throws PortletException propagated from Portlet API's forward method + * @throws IOException propagated from Portlet API's forward method + */ + public static void serveResource(ResourceRequest request, ResourceResponse response, PortletContext context) + throws PortletException, IOException { + + String id = request.getResourceID(); + if (id != null) { + if (!PortletUtils.isProtectedResource(id)) { + PortletRequestDispatcher rd = context.getRequestDispatcher(id); + if (rd != null) { + rd.forward(request, response); + return; + } + } + response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "404"); + } + } + + /** + * Check whether the specified path indicates a resource in the protected + * WEB-INF or META-INF directories. + * @param path the path to check + */ + private static boolean isProtectedResource(String path) { + return (StringUtils.startsWithIgnoreCase(path, "/WEB-INF") || + StringUtils.startsWithIgnoreCase(path, "/META-INF")); + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java index 3e9dc25b510..c1e8fd5c705 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java @@ -33,6 +33,7 @@ import javax.portlet.PortletRequest; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceResponse; import javax.portlet.StateAwareResponse; import javax.portlet.WindowState; import javax.servlet.http.Cookie; @@ -117,6 +118,38 @@ public class Portlet20AnnotationControllerTests { assertEquals("test", response.getContentAsString()); } + @Test + public void standardHandleMethodWithResources() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + StaticPortletApplicationContext wac = new StaticPortletApplicationContext(); + wac.setPortletConfig(getPortletConfig()); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.class)); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockResourceRequest resourceRequest = new MockResourceRequest("/resource1"); + MockResourceResponse resourceResponse = new MockResourceResponse(); + portlet.serveResource(resourceRequest, resourceResponse); + assertEquals("/resource1", resourceResponse.getForwardedUrl()); + assertNull(resourceResponse.getProperty(ResourceResponse.HTTP_STATUS_CODE)); + + resourceRequest = new MockResourceRequest("/WEB-INF/resource2"); + resourceResponse = new MockResourceResponse(); + portlet.serveResource(resourceRequest, resourceResponse); + assertNull(resourceResponse.getForwardedUrl()); + assertEquals("404", resourceResponse.getProperty(ResourceResponse.HTTP_STATUS_CODE)); + + resourceRequest = new MockResourceRequest("/META-INF/resource3"); + resourceResponse = new MockResourceResponse(); + portlet.serveResource(resourceRequest, resourceResponse); + assertNull(resourceResponse.getForwardedUrl()); + assertEquals("404", resourceResponse.getProperty(ResourceResponse.HTTP_STATUS_CODE)); + } + @Test public void adaptedHandleMethods() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController.class);