Repository: knox Updated Branches: refs/heads/master ee332f578 -> 1c9020182
KNOX-911 - Ability to scope cookies to a given Path (Attila Kanto via lmccay) Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/1c902018 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/1c902018 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/1c902018 Branch: refs/heads/master Commit: 1c9020182aca0bfa255059fb9040463c7abe7984 Parents: ee332f5 Author: Larry McCay <[email protected]> Authored: Tue Apr 25 18:03:41 2017 -0400 Committer: Larry McCay <[email protected]> Committed: Tue Apr 25 18:03:41 2017 -0400 ---------------------------------------------------------------------- .../rewrite/api/CookieScopeServletFilter.java | 48 +++++++++++ .../impl/CookieScopeResponseWrapper.java | 59 +++++++++++++ .../impl/CookieScopeResponseWrapperTest.java | 91 ++++++++++++++++++++ gateway-release/home/conf/gateway-site.xml | 6 ++ .../apache/hadoop/gateway/GatewayMessages.java | 3 + .../gateway/config/impl/GatewayConfigImpl.java | 10 +++ .../impl/ApplicationDeploymentContributor.java | 9 ++ .../ServiceDefinitionDeploymentContributor.java | 10 +++ .../hadoop/gateway/config/GatewayConfig.java | 7 ++ .../hadoop/gateway/GatewayTestConfig.java | 5 ++ .../hadoop/gateway/GatewayTestConfig.java | 5 ++ 11 files changed, 253 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/CookieScopeServletFilter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/CookieScopeServletFilter.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/CookieScopeServletFilter.java new file mode 100644 index 0000000..8c9eed0 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/CookieScopeServletFilter.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.hadoop.gateway.filter.rewrite.api; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.gateway.filter.AbstractGatewayFilter; +import org.apache.hadoop.gateway.filter.rewrite.impl.CookieScopeResponseWrapper; + + +public class CookieScopeServletFilter extends AbstractGatewayFilter { + + private String gatewayPath; + + @Override + public void init( FilterConfig filterConfig ) throws ServletException { + super.init( filterConfig ); + gatewayPath = filterConfig.getInitParameter("gateway.path"); + } + + @Override + protected void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain ) + throws IOException, ServletException { + chain.doFilter( request, new CookieScopeResponseWrapper(response, gatewayPath)); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapper.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapper.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapper.java new file mode 100644 index 0000000..6360f33 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapper.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.hadoop.gateway.filter.rewrite.impl; + +import org.apache.hadoop.gateway.filter.GatewayResponseWrapper; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; + +public class CookieScopeResponseWrapper extends GatewayResponseWrapper { + + private static final String SET_COOKIE = "Set-Cookie"; + + private static final String COOKIE_PATH = "Path=/"; + + private final String scopePath; + + public CookieScopeResponseWrapper(HttpServletResponse response, String gatewayPath) { + super(response); + this.scopePath = COOKIE_PATH + gatewayPath + "/"; + } + + @Override + public void addHeader(String name, String value) { + if (SET_COOKIE.equals(name)) { + String updatedCookie; + if (value.contains(COOKIE_PATH)) { + updatedCookie = value.replace(COOKIE_PATH, scopePath); + } else { + // append the scope path + updatedCookie = String.format("%s %s;", value, scopePath); + } + super.addHeader(name, updatedCookie); + } else { + super.addHeader(name, value); + } + } + + @Override + public OutputStream getRawOutputStream() throws IOException { + return getResponse().getOutputStream(); + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapperTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapperTest.java b/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapperTest.java new file mode 100644 index 0000000..c03f057 --- /dev/null +++ b/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/CookieScopeResponseWrapperTest.java @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.hadoop.gateway.filter.rewrite.impl; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.servlet.http.HttpServletResponse; + +public class CookieScopeResponseWrapperTest extends EasyMockSupport { + + private HttpServletResponse mock; + + private Capture<String> captureKey; + + private Capture<String> captureValue; + + @Before + public void init(){ + mock = EasyMock.createNiceMock(HttpServletResponse.class); + captureKey = new Capture<>(); + captureValue = new Capture<>(); + mock.addHeader( EasyMock.capture(captureKey), EasyMock.capture(captureValue)); + EasyMock.replay(mock); + } + + @Test + public void testNoPath() { + CookieScopeResponseWrapper underTest = new CookieScopeResponseWrapper(mock, "gw"); + underTest.addHeader("Set-Cookie", "SESSIONID=jn0zexg59r1jo1n66hd7tg5anl;HttpOnly;"); + + Assert.assertEquals("Set-Cookie", captureKey.getValue()); + Assert.assertEquals("SESSIONID=jn0zexg59r1jo1n66hd7tg5anl;HttpOnly; Path=/gw/;", captureValue.getValue()); + } + + @Test + public void testRootPath() { + CookieScopeResponseWrapper underTest = new CookieScopeResponseWrapper(mock, "gw"); + underTest.addHeader("Set-Cookie", "SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/; HttpOnly;"); + + Assert.assertEquals("Set-Cookie", captureKey.getValue()); + Assert.assertEquals("SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/gw/; HttpOnly;", captureValue.getValue()); + } + + @Test + public void testMultiSegmentPath() { + CookieScopeResponseWrapper underTest = new CookieScopeResponseWrapper(mock, "some/path"); + underTest.addHeader("Set-Cookie", "SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/; HttpOnly;"); + + Assert.assertEquals("Set-Cookie", captureKey.getValue()); + Assert.assertEquals("SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/some/path/; HttpOnly;", captureValue.getValue()); + } + + @Test + public void testAlreadyScopedPath() { + CookieScopeResponseWrapper underTest = new CookieScopeResponseWrapper(mock, "some/path"); + underTest.addHeader("Set-Cookie", "SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/already-scoped/; HttpOnly;"); + + Assert.assertEquals("Set-Cookie", captureKey.getValue()); + Assert.assertEquals("SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/some/path/already-scoped/; HttpOnly;", captureValue.getValue()); + } + + @Test + public void testCaseSensitive() { + CookieScopeResponseWrapper underTest = new CookieScopeResponseWrapper(mock, "some/path"); + underTest.addHeader("set-cookie", "SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/not-touched/; HttpOnly;"); + + Assert.assertEquals("set-cookie", captureKey.getValue()); + Assert.assertEquals("SESSIONID=jn0zexg59r1jo1n66hd7tg5anl; Path=/not-touched/; HttpOnly;", captureValue.getValue()); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-release/home/conf/gateway-site.xml ---------------------------------------------------------------------- diff --git a/gateway-release/home/conf/gateway-site.xml b/gateway-release/home/conf/gateway-site.xml index 80cfacf..797f01a 100644 --- a/gateway-release/home/conf/gateway-site.xml +++ b/gateway-release/home/conf/gateway-site.xml @@ -67,4 +67,10 @@ limitations under the License. <description>Enable/Disable websocket feature.</description> </property> + <property> + <name>gateway.scope.cookies.feature.enabled</name> + <value>false</value> + <description>Enable/Disable cookie scoping feature.</description> + </property> + </configuration> http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java index 908589d..deb0034 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java @@ -433,4 +433,7 @@ public interface GatewayMessages { @Message( level = MessageLevel.DEBUG, text = "Failed to stop metrics reporter {0} : {1}" ) void failedToStopReporter( String name, @StackTrace( level = MessageLevel.DEBUG ) Exception e); + + @Message( level = MessageLevel.INFO, text = "Cookie scoping feature enabled: {0}" ) + void cookieScopingFeatureEnabled( boolean enabled ); } http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java index 65ba8ea..3084d8f 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java @@ -189,6 +189,9 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig { public static final String DEFAULT_MIME_TYPES_TO_COMPRESS = "text/html, text/plain, text/xml, text/css, " + "application/javascript, application/x-javascript, text/javascript"; + public static final String COOKIE_SCOPING_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".scope.cookies.feature.enabled"; + public static final boolean DEFAULT_COOKIE_SCOPING_FEATURE_ENABLED = false; + private static List<String> DEFAULT_GLOBAL_RULES_SERVICES; @@ -804,5 +807,12 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig { return p.toStandardDuration().getMillis(); } + @Override + public boolean isCookieScopingToPathEnabled() { + final boolean result = Boolean.parseBoolean(get(COOKIE_SCOPING_ENABLED, + Boolean.toString(DEFAULT_COOKIE_SCOPING_FEATURE_ENABLED))); + log.cookieScopingFeatureEnabled(result); + return result; + } } http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ApplicationDeploymentContributor.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ApplicationDeploymentContributor.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ApplicationDeploymentContributor.java index ce642d1..bd15314 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ApplicationDeploymentContributor.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ApplicationDeploymentContributor.java @@ -33,12 +33,15 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import org.apache.hadoop.gateway.config.GatewayConfig; +import org.apache.hadoop.gateway.config.impl.GatewayConfigImpl; import org.apache.hadoop.gateway.deploy.DeploymentContext; import org.apache.hadoop.gateway.deploy.DeploymentException; import org.apache.hadoop.gateway.deploy.ServiceDeploymentContributorBase; +import org.apache.hadoop.gateway.descriptor.FilterDescriptor; import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor; import org.apache.hadoop.gateway.descriptor.ResourceDescriptor; import org.apache.hadoop.gateway.filter.XForwardedHeaderFilter; +import org.apache.hadoop.gateway.filter.rewrite.api.CookieScopeServletFilter; import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteRulesDescriptorFactory; import org.apache.hadoop.gateway.service.definition.Policy; @@ -55,6 +58,8 @@ public class ApplicationDeploymentContributor extends ServiceDeploymentContribut private static final String REWRITE_RULES_FILE_NAME = "rewrite.xml"; private static final String XFORWARDED_FILTER_NAME = "XForwardedHeaderFilter"; private static final String XFORWARDED_FILTER_ROLE = "xforwardedheaders"; + private static final String COOKIE_SCOPING_FILTER_NAME = "CookieScopeServletFilter"; + private static final String COOKIE_SCOPING_FILTER_ROLE = "cookiescopef"; private ServiceDefinition serviceDefinition; @@ -171,6 +176,10 @@ public class ApplicationDeploymentContributor extends ServiceDeploymentContribut if (context.getGatewayConfig().isXForwardedEnabled()) { resource.addFilter().name(XFORWARDED_FILTER_NAME).role(XFORWARDED_FILTER_ROLE).impl(XForwardedHeaderFilter.class); } + if (context.getGatewayConfig().isCookieScopingToPathEnabled()) { + FilterDescriptor filter = resource.addFilter().name(COOKIE_SCOPING_FILTER_NAME).role(COOKIE_SCOPING_FILTER_ROLE).impl(CookieScopeServletFilter.class); + filter.param().name(GatewayConfigImpl.HTTP_PATH).value(context.getGatewayConfig().getGatewayPath()); + } List<Policy> policyBindings = binding.getPolicies(); if ( policyBindings == null ) { policyBindings = serviceDefinition.getPolicies(); http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java index a5585a9..439df3c 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java @@ -17,6 +17,7 @@ */ package org.apache.hadoop.gateway.deploy.impl; +import org.apache.hadoop.gateway.config.impl.GatewayConfigImpl; import org.apache.hadoop.gateway.deploy.DeploymentContext; import org.apache.hadoop.gateway.deploy.ServiceDeploymentContributorBase; import org.apache.hadoop.gateway.descriptor.FilterDescriptor; @@ -24,6 +25,7 @@ import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor; import org.apache.hadoop.gateway.descriptor.ResourceDescriptor; import org.apache.hadoop.gateway.dispatch.GatewayDispatchFilter; import org.apache.hadoop.gateway.filter.XForwardedHeaderFilter; +import org.apache.hadoop.gateway.filter.rewrite.api.CookieScopeServletFilter; import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; import org.apache.hadoop.gateway.service.definition.CustomDispatch; import org.apache.hadoop.gateway.service.definition.Policy; @@ -56,6 +58,10 @@ public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentCon private static final String DEFAULT_HA_DISPATCH_CLASS = "org.apache.hadoop.gateway.ha.dispatch.DefaultHaDispatch"; + private static final String COOKIE_SCOPING_FILTER_NAME = "CookieScopeServletFilter"; + + private static final String COOKIE_SCOPING_FILTER_ROLE = "cookiescopef"; + private ServiceDefinition serviceDefinition; private UrlRewriteRulesDescriptor serviceRules; @@ -122,6 +128,10 @@ public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentCon if (context.getGatewayConfig().isXForwardedEnabled()) { resource.addFilter().name(XFORWARDED_FILTER_NAME).role(XFORWARDED_FILTER_ROLE).impl(XForwardedHeaderFilter.class); } + if (context.getGatewayConfig().isCookieScopingToPathEnabled()) { + FilterDescriptor filter = resource.addFilter().name(COOKIE_SCOPING_FILTER_NAME).role(COOKIE_SCOPING_FILTER_ROLE).impl(CookieScopeServletFilter.class); + filter.param().name(GatewayConfigImpl.HTTP_PATH).value(context.getGatewayConfig().getGatewayPath()); + } List<Policy> policyBindings = binding.getPolicies(); if ( policyBindings == null ) { policyBindings = serviceDefinition.getPolicies(); http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java index c083a42..d49e2de 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java @@ -222,4 +222,11 @@ public interface GatewayConfig { */ List<String> getMimeTypesToCompress(); + /** + * Enable cookie scoping to gateway path + * + * @since 0.13 + */ + boolean isCookieScopingToPathEnabled(); + } http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java ---------------------------------------------------------------------- diff --git a/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java index eca4e01..b0253cb 100644 --- a/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java +++ b/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java @@ -467,4 +467,9 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig { public int getGraphiteReportingFrequency() { return 0; } + + @Override + public boolean isCookieScopingToPathEnabled() { + return false; + } } http://git-wip-us.apache.org/repos/asf/knox/blob/1c902018/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java ---------------------------------------------------------------------- diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java index 0ea5cf5..baf38e6 100644 --- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java +++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java @@ -525,4 +525,9 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig { public List<String> getMimeTypesToCompress() { return new ArrayList<String>(); } + + @Override + public boolean isCookieScopingToPathEnabled() { + return false; + } }
