CAMEL-6424: camel-netty-http added support for basic auth. Work in progress.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/3493d980 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/3493d980 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/3493d980 Branch: refs/heads/master Commit: 3493d980679fee253a364612148fde925b8afefd Parents: bf1f5f0 Author: Claus Ibsen <davscl...@apache.org> Authored: Mon Jul 15 10:09:36 2013 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Mon Jul 15 13:48:13 2013 +0200 ---------------------------------------------------------------------- .../netty/http/HttpBasicAuthSubject.java | 45 +++++++++++++ .../netty/http/NettyHttpComponent.java | 13 ++++ .../component/netty/http/NettyHttpEndpoint.java | 9 +++ .../http/NettyHttpSecurityConfiguration.java | 57 +++++++++++++++++ .../http/handlers/HttpServerChannelHandler.java | 67 ++++++++++++++++++++ .../http/NettyHttpSimpleBasicAuthTest.java | 49 ++++++++++++++ .../src/test/resources/log4j.properties | 2 +- 7 files changed, 241 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpBasicAuthSubject.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpBasicAuthSubject.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpBasicAuthSubject.java new file mode 100644 index 0000000..2809c84 --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpBasicAuthSubject.java @@ -0,0 +1,45 @@ +/** + * 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.camel.component.netty.http; + +import java.io.Serializable; + +public final class HttpBasicAuthSubject implements Serializable { + + private static final long serialVersionUID = 1L; + private final String username; + private final String password; + + public HttpBasicAuthSubject(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + // do not display the password + return "HttpBasicAuthSubject[" + username + "]"; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java index 6d5c4c3..177ce94 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java @@ -46,6 +46,8 @@ public class NettyHttpComponent extends NettyComponent implements HeaderFilterSt private final Map<String, HttpServerBootstrapFactory> bootstrapFactories = new HashMap<String, HttpServerBootstrapFactory>(); private NettyHttpBinding nettyHttpBinding; private HeaderFilterStrategy headerFilterStrategy; + // TODO: make it easy to configure this + private NettyHttpSecurityConfiguration nettyHttpSecurityConfiguration;// = new NettyHttpSecurityConfiguration(); public NettyHttpComponent() { // use the http configuration and filter strategy @@ -102,6 +104,9 @@ public class NettyHttpComponent extends NettyComponent implements HeaderFilterSt if (answer.getHeaderFilterStrategy() == null) { answer.setHeaderFilterStrategy(getHeaderFilterStrategy()); } + if (answer.getNettyHttpSecurityConfiguration() == null) { + answer.setNettyHttpSecurityConfiguration(getNettyHttpSecurityConfiguration()); + } answer.setNettySharedHttpServer(shared); return answer; @@ -141,6 +146,14 @@ public class NettyHttpComponent extends NettyComponent implements HeaderFilterSt this.headerFilterStrategy = headerFilterStrategy; } + public NettyHttpSecurityConfiguration getNettyHttpSecurityConfiguration() { + return nettyHttpSecurityConfiguration; + } + + public void setNettyHttpSecurityConfiguration(NettyHttpSecurityConfiguration nettyHttpSecurityConfiguration) { + this.nettyHttpSecurityConfiguration = nettyHttpSecurityConfiguration; + } + public synchronized HttpServerConsumerChannelFactory getMultiplexChannelHandler(int port) { HttpServerConsumerChannelFactory answer = multiplexChannelHandlers.get(port); if (answer == null) { http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpEndpoint.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpEndpoint.java index e481d4b..1039fcf 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpEndpoint.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpEndpoint.java @@ -46,6 +46,7 @@ public class NettyHttpEndpoint extends NettyEndpoint implements HeaderFilterStra private boolean traceEnabled; private String httpMethodRestrict; private NettySharedHttpServer nettySharedHttpServer; + private NettyHttpSecurityConfiguration nettyHttpSecurityConfiguration; public NettyHttpEndpoint(String endpointUri, NettyHttpComponent component, NettyConfiguration configuration) { super(endpointUri, component, configuration); @@ -171,6 +172,14 @@ public class NettyHttpEndpoint extends NettyEndpoint implements HeaderFilterStra this.nettySharedHttpServer = nettySharedHttpServer; } + public NettyHttpSecurityConfiguration getNettyHttpSecurityConfiguration() { + return nettyHttpSecurityConfiguration; + } + + public void setNettyHttpSecurityConfiguration(NettyHttpSecurityConfiguration nettyHttpSecurityConfiguration) { + this.nettyHttpSecurityConfiguration = nettyHttpSecurityConfiguration; + } + @Override protected void doStart() throws Exception { super.doStart(); http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpSecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpSecurityConfiguration.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpSecurityConfiguration.java new file mode 100644 index 0000000..e04c497 --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpSecurityConfiguration.java @@ -0,0 +1,57 @@ +/** + * 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.camel.component.netty.http; + +public class NettyHttpSecurityConfiguration { + + private boolean authenticate = true; + private String constraint = "BASIC"; + private String realm = "Camel"; + private ContextPathMatcher contextPathMatcher; + + public boolean isAuthenticate() { + return authenticate; + } + + public void setAuthenticate(boolean authenticate) { + this.authenticate = authenticate; + } + + public String getConstraint() { + return constraint; + } + + public void setConstraint(String constraint) { + this.constraint = constraint; + } + + public String getRealm() { + return realm; + } + + public void setRealm(String realm) { + this.realm = realm; + } + + public ContextPathMatcher getContextPathMatcher() { + return contextPathMatcher; + } + + public void setContextPathMatcher(ContextPathMatcher contextPathMatcher) { + this.contextPathMatcher = contextPathMatcher; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java index 95ca27d..ccc2415 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java @@ -18,17 +18,23 @@ package org.apache.camel.component.netty.http.handlers; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; +import java.nio.charset.Charset; import org.apache.camel.Exchange; import org.apache.camel.component.netty.NettyConsumer; import org.apache.camel.component.netty.NettyHelper; import org.apache.camel.component.netty.handlers.ServerChannelHandler; +import org.apache.camel.component.netty.http.HttpBasicAuthSubject; import org.apache.camel.component.netty.http.NettyHttpConsumer; +import org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration; +import org.apache.camel.util.ObjectHelper; +import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.codec.base64.Base64; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; @@ -40,6 +46,7 @@ import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; +import static org.jboss.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED; import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; /** @@ -100,10 +107,70 @@ public class HttpServerChannelHandler extends ServerChannelHandler { return; } + // is basic auth configured + NettyHttpSecurityConfiguration security = consumer.getEndpoint().getNettyHttpSecurityConfiguration(); + if (security != null) { + String url = request.getUri(); + + // is it a restricted resource? + boolean restricted = security.getContextPathMatcher() == null || security.getContextPathMatcher().matches(url); + if (restricted) { + // basic auth subject + HttpBasicAuthSubject subject = extractBasicAuthSubject(request); + boolean authenticated = subject != null && authenticate(subject); + if (subject == null || !authenticated) { + if (subject == null) { + LOG.debug("Http Basic Auth required for resource: {}", url); + } else { + LOG.debug("Http Basic Auth not authorized for username: {}", subject.getUsername()); + } + // restricted resource, so send back 401 to require valid username/password + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, UNAUTHORIZED); + response.setHeader("WWW-Authenticate", "Basic realm=\"" + security.getRealm() + "\""); + response.setHeader(Exchange.CONTENT_TYPE, "text/plain"); + response.setHeader(Exchange.CONTENT_LENGTH, 0); + response.setContent(ChannelBuffers.copiedBuffer(new byte[]{})); + messageEvent.getChannel().write(response); + return; + } else { + LOG.debug("Http Basic Auth authorized for username: {}", subject.getUsername()); + } + } + } + // let Camel process this message super.messageReceived(ctx, messageEvent); } + /** + * Authenticates the http basic auth subject. + * + * @param subject the subject + * @return <tt>true</tt> if username and password is valid, <tt>false</tt> if not + */ + protected boolean authenticate(HttpBasicAuthSubject subject) { + // TODO: an api for authentication + return subject.getPassword().equals("secret"); + //return true; + } + + protected static HttpBasicAuthSubject extractBasicAuthSubject(HttpRequest request) { + String auth = request.getHeader("Authorization"); + if (auth != null) { + String constraint = ObjectHelper.before(auth, " "); + String decoded = ObjectHelper.after(auth, " "); + // the decoded part is base64 encoded, so we need to decode that + ChannelBuffer buf = ChannelBuffers.copiedBuffer(decoded.getBytes()); + ChannelBuffer out = Base64.decode(buf); + String userAndPw = out.toString(Charset.defaultCharset()); + String username = ObjectHelper.before(userAndPw, ":"); + String password = ObjectHelper.after(userAndPw, ":"); + HttpBasicAuthSubject subject = new HttpBasicAuthSubject(username, password); + return subject; + } + return null; + } + @Override protected void beforeProcess(Exchange exchange, MessageEvent messageEvent) { if (consumer.getConfiguration().isBridgeEndpoint()) { http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthTest.java new file mode 100644 index 0000000..048f1b4 --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthTest.java @@ -0,0 +1,49 @@ +/** + * 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.camel.component.netty.http; + +import org.apache.camel.builder.RouteBuilder; +import org.junit.Ignore; +import org.junit.Test; + +@Ignore +public class NettyHttpSimpleBasicAuthTest extends BaseNettyTest { + + @Test + public void testHttpSimple() throws Exception { +// getMockEndpoint("mock:input").expectedBodiesReceived("Hello World"); +// +// String out = template.requestBody("netty-http:http://localhost:{{port}}/foo", "Hello World", String.class); +// assertEquals("Bye World", out); +// +// assertMockEndpointsSatisfied(); +// Thread.sleep(9999999); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("netty-http:http://0.0.0.0:{{port}}/foo") + .to("mock:input") + .transform().constant("Bye World"); + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/3493d980/components/camel-netty-http/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/resources/log4j.properties b/components/camel-netty-http/src/test/resources/log4j.properties index 7d3c19c..0bada7f 100644 --- a/components/camel-netty-http/src/test/resources/log4j.properties +++ b/components/camel-netty-http/src/test/resources/log4j.properties @@ -16,7 +16,7 @@ ## ------------------------------------------------------------------------ # -# The logging properties used for eclipse testing, We want to see debug output on the console. +# The logging properties used for testing # log4j.rootLogger=INFO, file