Author: robertdzeigler Date: Mon Nov 9 17:23:10 2009 New Revision: 834151 URL: http://svn.apache.org/viewvc?rev=834151&view=rev Log: TAP5-815: Asset dispatcher allows any file inside the webapp visible and downloadable (5.2 branch)
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcher.java tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RegexAuthorizer.java tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/WhitelistAuthorizer.java tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/AssetPathAuthorizer.java tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcherTest.java tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RegexAuthorizerTest.java tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/WhitelistAuthorizerTest.java Modified: tapestry/tapestry5/trunk/src/site/apt/guide/assets.apt tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java Modified: tapestry/tapestry5/trunk/src/site/apt/guide/assets.apt URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/assets.apt?rev=834151&r1=834150&r2=834151&view=diff ============================================================================== --- tapestry/tapestry5/trunk/src/site/apt/guide/assets.apt (original) +++ tapestry/tapestry5/trunk/src/site/apt/guide/assets.apt Mon Nov 9 17:23:10 2009 @@ -138,6 +138,31 @@ In addition, context assets will use the URL prefix <<</assets/ctx/>>><app-version><<</>>>. +Securing Assets + + Securing assets is an important consideration for any web application. Many assets, such as hibernate configuration + files, sit in the classpath and are exposable via the Asset service, which is not desirable. To protect these and + other sensitive assets, Tapestry provides the AssetProtectionDispatcher. This dispatcher sits in front of the + AssetDispatcher, the service responsible for streaming assets to the client, and watches for Asset requests. + When an asset request comes in, the protection dispatcher checks for authorization to view the file against a + contributed list of AssetPathAuthorizer implementations. Determination of whether the client can view the requested + resource is then made based on whether any of the contributed AssetPathAuthorizer implementations explicitly allowed + or denied access to the resource. + + Tapestry provides two AssetPathAuthorizer implemenations "out of the box" to which users may contribute: RegexAuthorizer + and WhitelistAuthorizer. RegexAuthorizer uses regular expressions to determine assets which are viewable by the + client; any assets that match one of its (contributed) regular expressions are authorized. Anything not matched is + passed through to the WhitelistAuthorizer. WhitelistAuthorizer uses an exact-matching whitelist. Anything matching + exactly one its contributions is allowed; all other asset requests are denied. The default tapestry configuration + contributes nothing to WhitelistAuthorizer (access will be denied to all asset requests passed through to it), and + explicitly allows access to css, jpg, jpeg, js, png, and gif files associated with tapestry (tapestry.js, blackbird + files, date picker files, etc.). The default contribution also enables access to the css, jpg, jpeg, js, png, and gif + files provided by the popular chenille-kit 3rd party library. The default configuration denies access to all other + assets. To enable access to your application's assets, either contribute a custom AssetPathAnalyzer, or contribute + appropriate regular expression or exact path contributions to RegexAuthorizer or WhitelistAuthorizer, respectively. + See TapestryModule.contribteRegexAuthorizer for examples. + + Performance Notes Assets are expected to be entirely static (not changing while the application is deployed). When Tapestry generates a URL @@ -146,4 +171,4 @@ asset. In addition, Tapestry will {{{compress.html}GZIP compress}} the content of <all> assets (if the asset - is compressable, and the client supports it). \ No newline at end of file + is compressable, and the client supports it). Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcher.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcher.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcher.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcher.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,92 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.tapestry5.internal.services.RequestConstants; +import org.apache.tapestry5.services.*; +import org.slf4j.Logger; + +/** + * Dispatcher that handles whether to allow or deny access to particular + * assets. Actual work of authorizing a particular url is handled by + * implementations of AssetPathAuthorizer. Configuration is an ordered + * list of AssetPathAuthorizers. Each authorizer specifies an order of + * operations as a list (see AssetPathAuthorizer.Order). + * + */ +public class AssetProtectionDispatcher implements Dispatcher +{ + + private final Collection<AssetPathAuthorizer> authorizers; + private final ClasspathAssetAliasManager assetAliasManager; + private final Logger logger; + + public AssetProtectionDispatcher( + List<AssetPathAuthorizer> auths, + ClasspathAssetAliasManager manager, + Logger logger) + { + this.authorizers = Collections.unmodifiableList(auths); + this.assetAliasManager = manager; + this.logger = logger; + } + + public boolean dispatch(Request request, Response response) + throws IOException + { + String path = request.getPath(); + //we only protect assets, and don't examine any other url's. + if (!path.startsWith(RequestConstants.ASSET_PATH_PREFIX)) + { + return false; + } + String resourcePath = assetAliasManager.toResourcePath(path); + for(AssetPathAuthorizer auth : authorizers) + { + for(AssetPathAuthorizer.Order o : auth.order()) + { + if (o == AssetPathAuthorizer.Order.ALLOW) + { + if (auth.accessAllowed(resourcePath)) + { + logger.debug("Allowing access to " + resourcePath); + return false; + } + } + else + { + if (auth.accessDenied(resourcePath)) + { + logger.debug("Denying access to " + resourcePath); + response.sendError(HttpServletResponse.SC_FORBIDDEN,resourcePath); + return true; + } + } + } + } + //if we get here, no Authorizer had anything useful to say about the resourcePath. + //so let it fall through. + logger.debug("Fell through the list of authorizers. Allowing access to: " + resourcePath); + return false; + } + +} Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RegexAuthorizer.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RegexAuthorizer.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RegexAuthorizer.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RegexAuthorizer.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,76 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import org.apache.tapestry5.services.AssetPathAuthorizer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Provides a regex-based authorization scheme for asset-access authorization. + * Note that this implementation doesn't actually deny access to anything. + * But it's placement within the chain of command of authorizers is just before + * the whitelist authorizer, which has an explicit deny policy. + * Hence, as long as the whitelist authorizer is being used in conjunction with + * the regex authorizer, there is no need to worry about accessDenied in this authorizer. + * + */ +public class RegexAuthorizer implements AssetPathAuthorizer +{ + + private final Collection<Pattern> _regexes; + + public RegexAuthorizer(final Collection<String> regex) + { + //an alternate way to construct this would be to make sure that each pattern is grouped + //and then to regex or the various patterns together into a single pattern. + //that might be faster, but probably not enough to make a difference, and this is cleaner. + List<Pattern> tmp = new ArrayList<Pattern>(); + for(String exp : regex) + { + tmp.add(Pattern.compile(exp)); + } + _regexes = Collections.unmodifiableCollection(tmp); + + } + + public boolean accessAllowed(String resourcePath) + { + for(Pattern regex : _regexes) + { + if (regex.matcher(resourcePath).matches()) + { + return true; + } + } + return false; + } + + public boolean accessDenied(String resourcePath) + { + return false; + } + + public List<Order> order() + { + return Arrays.asList(Order.ALLOW); + } + +} Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/WhitelistAuthorizer.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/WhitelistAuthorizer.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/WhitelistAuthorizer.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/WhitelistAuthorizer.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,59 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import org.apache.tapestry5.services.AssetPathAuthorizer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * AssetPathAuthorizer that determines access rights based on exact matching to a contributed whitelist. + * Any resource not explicitly specified in the whitelist is denied access. + */ +public class WhitelistAuthorizer implements AssetPathAuthorizer +{ + + public List<Order> order() + { + return Arrays.asList(Order.ALLOW, Order.DENY); + } + + //hash the resource paths for fast lookups. + private final Map<String, Boolean> _paths; + + public WhitelistAuthorizer(Collection<String> paths) + { + _paths = new ConcurrentHashMap<String, Boolean>(); + for(String path : paths) + { + _paths.put(path, true); + } + } + + public boolean accessAllowed(String resourcePath) + { + return (_paths.containsKey(resourcePath)); + } + + public boolean accessDenied(String resourcePath) + { + return !_paths.containsKey(resourcePath); + } + +} Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/AssetPathAuthorizer.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/AssetPathAuthorizer.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/AssetPathAuthorizer.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/AssetPathAuthorizer.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,76 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.services; + +import java.util.List; + +/** + * Determines whether access to an asset is allowed, denied, or undetermined. + * Each contributed authorizer makes up part of a chain of command for determining access. + * Access is explicitly allowed if accessAllowed returns true. + * Access is explicitly denied if accessDenied returns true. + * Ordering depends on the order specified by the "order" parameter. + * Hence, an implementation which specifies an order of: + * ALLOW, DENY, and returns true from both accessAllowed and accessDenied + * will allow access for all resources. With the same return values for the + * access* methods but the order switched to DENY, ALLOW, access to all resources + * would be denied. It is possible for an authorizer to have "nothing + * to say" regarding a particular resource. If accessAllowed returns false, + * it does not mean that access is denied, merely that it is not explicitly allowed. + * If accessDenied returns false, it does not mean that access is allowed, merely that + * it is not explicitly denied. Hence, if both accessAllowed and accessDenied return false, + * control will pass to the next authorizer in the chain. + * + */ +public interface AssetPathAuthorizer +{ + + /** + * Types of orderings, either ALLOW or DENY. + */ + enum Order {ALLOW, DENY;} + + /** + * Specify the ordering for this authorizer. + * @return the operations for this authorizer. + * Operations will be performed in the order returned by the iterator. + * It is assumed that the authorizer correctly implements each form of + * ordering returned. It is acceptable to only return only ALLOW or DENY. + */ + List<Order> order(); + + /** + * Determines whether a request to "resourcePath" is allowed. + * @param resourcePath + * @return true if access is explicitly allowed for the path. False otherwise. + * For example, a whitelist implementation would return true if the resource + * was listed, and false otherwise. A blacklist implementation would return + * false regardless of whether the path was in the blacklist. + * Alternatively, if the blacklist specified an order of DENY, ALLOW, it could + * return true from accessAllowed if the resource was not explicitly listed in its + * blacklist. + */ + boolean accessAllowed(String resourcePath); + + /** + * + * @param resourcePath + * @return true if access is explicitly prohibited for the path. False otherwise. + * For example, a whitelist implementation would return true if the resource was + * not explicitly listed, and false otherwise. A blacklist implementation would + * return true if the resource was explicitly denied, and false otherwise. + */ + boolean accessDenied(String resourcePath); +} Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=834151&r1=834150&r2=834151&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original) +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Mon Nov 9 17:23:10 2009 @@ -305,6 +305,9 @@ binder.bind(PageRenderLinkSource.class, PageRenderLinkSourceImpl.class); binder.bind(ClientInfrastructure.class, ClientInfrastructureImpl.class); binder.bind(URLRewriter.class, URLRewriterImpl.class); + binder.bind(Dispatcher.class, AssetProtectionDispatcher.class).withId("AssetProtectionDispatcher"); + binder.bind(AssetPathAuthorizer.class, WhitelistAuthorizer.class).withId("WhitelistAuthorizer"); + binder.bind(AssetPathAuthorizer.class, RegexAuthorizer.class).withId("RegexAuthorizer"); } // ======================================================================== @@ -1572,13 +1575,17 @@ * and forwards onto {...@link PageRenderRequestHandler}</dd> <dt>ComponentEvent</dt> <dd>Identifies the {...@link * ComponentEventRequestParameters} and forwards onto the {...@link ComponentEventRequestHandler}</dd> </dl> */ - public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration) + public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration, + @InjectService("AssetProtectionDispatcher") Dispatcher assetProt) { // Looks for the root path and renders the start page. This is maintained for compatibility // with earlier versions of Tapestry 5, it is recommended that an Index page be used instead. configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset"); + //this goes before asset to make sure that only allowed assets are streamed to the client. + configuration.add("AssetProtection", assetProt, "before:Asset"); + // This goes first because an asset to be streamed may have an file extension, such as // ".html", that will confuse the later dispatchers. @@ -1591,6 +1598,7 @@ configuration.addInstance("PageRender", PageRenderDispatcher.class); } + /** * Contributes a default object renderer for type Object, plus specialized renderers for {...@link * org.apache.tapestry5.services.Request}, {...@link org.apache.tapestry5.ioc.Location}, {...@link @@ -2480,4 +2488,41 @@ }; } + /** + * Contributes the default set of AssetPathAuthorizers into the AssetProtectionDispatcher. + * @param whitelist authorization based on explicit whitelisting. + * @param regex authorization based on pattern matching. + * @param conf + */ + public static void contributeAssetProtectionDispatcher( + @InjectService("WhitelistAuthorizer") AssetPathAuthorizer whitelist, + @InjectService("RegexAuthorizer") AssetPathAuthorizer regex, + OrderedConfiguration<AssetPathAuthorizer> conf) + { + //putting whitelist after everything ensures that, in fact, nothing falls through. + //also ensures that whitelist gives other authorizers the chance to act... + conf.add("regex",regex,"before:whitelist"); + conf.add("whitelist", whitelist,"after:*"); + } + + public void contributeRegexAuthorizer(Configuration<String> regex, + @Symbol("tapestry.scriptaculous.path") String scriptPath, + @Symbol("tapestry.blackbird.path") String blackbirdPath, + @Symbol("tapestry.datepicker.path") String datepickerPath) + { + //allow any js, jpg, jpeg, png, or css under org/chenillekit/tapstry. The funky bit of ([^/.]+/)* is what allows + //multiple paths, while not allowing any of those paths to contains ./ or ../ thereby preventing paths like: + //org/chenillekit/tapestry/../../../foo.js + String pathPattern = "([^/.]+/)*[^/.]+\\.((css)|(js)|(jpg)|(jpeg)|(png)|(gif))$"; + regex.add("^org/chenillekit/tapestry/" + pathPattern); + + regex.add("^org/apache/tapestry5/" + pathPattern); + + regex.add(blackbirdPath + "/" + pathPattern); + regex.add(datepickerPath + "/" + pathPattern); + regex.add(scriptPath + "/" + pathPattern); + //allow access to virtual assets. Critical for tapestry-combined js files. + regex.add("virtual/" + pathPattern); + } + } Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java?rev=834151&r1=834150&r2=834151&view=diff ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java (original) +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java Mon Nov 9 17:23:10 2009 @@ -257,4 +257,22 @@ { configuration.add("ReverseStringsWorker", new ReverseStringsWorker()); } + + public static void contributeRegexAuthorizer(Configuration<String> configuration) { + //use this rather than a blanket regex (^.*.jpg$, etc.); want to be sure that tests pass from the default + //configuration setup, (eg: this way, I realized that the "virtual" assets folder + //needed to be opened up in the tapestry-provided contributions) rather than from some blanket configuration in the appmodule + //opening up all css, js, etc. files. + //would contribute to whitelist except that the resource path between ctxt and the rest of the path can change. + configuration.add("^ctx/[^/]+/css/app\\.css$"); + configuration.add("^ctx/[^/]+/layout/style\\.css$"); + configuration.add("^ctx/[^/]+/layout/images/bg\\.gif$"); + configuration.add("^ctx/[^/]+/layout/images/header\\.gif$"); + configuration.add("^ctx/[^/]+/layout/images/rightsmall\\.gif$"); + configuration.add("^ctx/[^/]+/layout/images/rightbig\\.gif$"); + configuration.add("^ctx/[^/]+/layout/images/bottom\\.gif$"); + configuration.add("^ctx/[^/]+/layout/images/footer\\.gif$"); + configuration.add("^ctx/[^/]+/images/tapestry_banner\\.gif$"); + configuration.add("^ctx/[^/]+/images/asf_logo_wide\\.gif$"); + } } Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcherTest.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcherTest.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcherTest.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetProtectionDispatcherTest.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,92 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.tapestry5.internal.services.RequestConstants; +import org.apache.tapestry5.internal.services.AssetProtectionDispatcher; +import org.apache.tapestry5.services.ClasspathAssetAliasManager; +import org.apache.tapestry5.services.Request; +import org.apache.tapestry5.services.Response; +import org.apache.tapestry5.services.AssetPathAuthorizer; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.slf4j.Logger; + +public class AssetProtectionDispatcherTest extends Assert +{ + + @Test + public void ignores_nonassets() throws IOException + { + //shouldn't need any configuration here... + List<AssetPathAuthorizer> auths = Collections.emptyList(); + Logger logger = createMock(Logger.class); + AssetProtectionDispatcher disp = new AssetProtectionDispatcher(auths,null,logger); + Request request = createMock(Request.class); + expect(request.getPath()).andReturn("start"); + Response response = createMock(Response.class); + replay(request,response,logger); + assertFalse(disp.dispatch(request, response)); + verify(request,response,logger); + } + + @Test + public void checks_authorizers() throws IOException + { + Logger logger = createMock(Logger.class); + List<AssetPathAuthorizer> auths = new ArrayList<AssetPathAuthorizer>(); + AssetPathAuthorizer auth = createMock(AssetPathAuthorizer.class); + + expect(auth.order()).andReturn(Arrays.asList(AssetPathAuthorizer.Order.ALLOW, AssetPathAuthorizer.Order.DENY)).times(2); + + expect(auth.accessAllowed("/cayenne.xml")).andReturn(false); + expect(auth.accessDenied("/cayenne.xml")).andReturn(true); + expect(auth.accessAllowed("/org/apache/tapestry/default.css")).andReturn(true); + auths.add(auth); + + logger.debug("Denying access to /cayenne.xml"); + logger.debug("Allowing access to /org/apache/tapestry/default.css"); + + Request request = createMock(Request.class); + Response response = createMock(Response.class); + expect(request.getPath()).andReturn(RequestConstants.ASSET_PATH_PREFIX + "/cayenne.xml"); + expect(request.getPath()).andReturn(RequestConstants.ASSET_PATH_PREFIX + "/org/apache/tapestry/default.css"); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "/cayenne.xml"); + + ClasspathAssetAliasManager manager = createMock(ClasspathAssetAliasManager.class); + expect(manager.toResourcePath(RequestConstants.ASSET_PATH_PREFIX + "/cayenne.xml")).andReturn("/cayenne.xml"); + expect(manager.toResourcePath( + RequestConstants.ASSET_PATH_PREFIX + "/org/apache/tapestry/default.css")) + .andReturn("/org/apache/tapestry/default.css"); + replay(auth,request,response,manager,logger); + AssetProtectionDispatcher disp = new AssetProtectionDispatcher(auths,manager,logger); + + assertTrue(disp.dispatch(request,response)); + assertFalse(disp.dispatch(request, response)); + verify(auth,request,response,logger); + } +} Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RegexAuthorizerTest.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RegexAuthorizerTest.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RegexAuthorizerTest.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RegexAuthorizerTest.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,53 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import java.util.Arrays; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.apache.tapestry5.internal.services.RegexAuthorizer; + +public class RegexAuthorizerTest extends Assert +{ + + @Test + public void test_regexes() + { + List<String> patterns = Arrays.asList("^.*\\.png$","^.*\\.jpg","^.*\\.jpeg"); + RegexAuthorizer auth = new RegexAuthorizer(patterns); + String pkg = "assets/com/saiwaisolutions/resources/"; + String png = pkg + "foo.png"; + String jpg = pkg + "foo.jpg"; + String jpeg = pkg + "foo.jpeg"; + String xml = pkg + "foo.xml"; + test(auth,png,true); + test(auth,jpg,true); + test(auth,jpeg,true); + test(auth,xml,false); + } + + private static void test(RegexAuthorizer auth, String one,boolean allowed) + { + assertEquals(auth.accessAllowed(one),allowed); + assertEquals( + auth.accessAllowed( + "http://localhost:8080" + one), + allowed); + assertFalse(auth.accessDenied(one)); + assertFalse(auth.accessDenied("http://localhost:8080" + one)); + } +} Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/WhitelistAuthorizerTest.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/WhitelistAuthorizerTest.java?rev=834151&view=auto ============================================================================== --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/WhitelistAuthorizerTest.java (added) +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/WhitelistAuthorizerTest.java Mon Nov 9 17:23:10 2009 @@ -0,0 +1,45 @@ +// Copyright 2009 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Created on Jul 28, 2007 + * + * + */ +package org.apache.tapestry5.internal.services; + +import java.util.Arrays; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.apache.tapestry5.internal.services.WhitelistAuthorizer; +import org.apache.tapestry5.services.AssetPathAuthorizer; + +public class WhitelistAuthorizerTest extends Assert { + + @Test + public void run() + { + WhitelistAuthorizer auth = new WhitelistAuthorizer(Arrays.asList("foo")); + assertEquals(auth.order().get(0), AssetPathAuthorizer.Order.ALLOW); + assertEquals(auth.order().get(1), AssetPathAuthorizer.Order.DENY); + assertEquals(auth.order().size(),2); + assertTrue(auth.accessAllowed("foo")); + assertFalse(auth.accessDenied("foo")); + + assertFalse(auth.accessAllowed("bar")); + assertTrue(auth.accessDenied("bar")); + } + +}