Updated Branches: refs/heads/master bf1f5f0cc -> f95326ec9
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/f95326ec Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f95326ec Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f95326ec Branch: refs/heads/master Commit: f95326ec9085040ec28f2e69b237ee45412d0d1b Parents: 3493d98 Author: Claus Ibsen <[email protected]> Authored: Mon Jul 15 13:48:00 2013 +0200 Committer: Claus Ibsen <[email protected]> Committed: Mon Jul 15 13:48:13 2013 +0200 ---------------------------------------------------------------------- .../ConstraintMappingContextPathMatcher.java | 87 +++++++++++++++ .../netty/http/HttpBasicAuthSubject.java | 45 -------- .../component/netty/http/HttpPrincipal.java | 52 +++++++++ .../netty/http/JAASSecurityAuthenticator.java | 107 ++++++++++++++++++ .../netty/http/NettyHttpComponent.java | 11 +- .../http/NettyHttpSecurityConfiguration.java | 14 ++- .../netty/http/SecurityAuthenticator.java | 58 ++++++++++ .../http/handlers/HttpServerChannelHandler.java | 70 +++++++----- ...ConstraintMappingContextPathMatcherTest.java | 108 +++++++++++++++++++ .../component/netty/http/MyLoginModule.java | 92 ++++++++++++++++ .../http/NettyHttpSimpleBasicAuthTest.java | 58 ++++++++-- .../src/test/resources/myjaas.config | 5 + 12 files changed, 620 insertions(+), 87 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcher.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcher.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcher.java new file mode 100644 index 0000000..11667ae --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcher.java @@ -0,0 +1,87 @@ +/** + * 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.util.LinkedHashSet; +import java.util.Set; + +import org.apache.camel.util.EndpointHelper; + +public class ConstraintMappingContextPathMatcher implements ContextPathMatcher { + + private Set<String> inclusions; + private Set<String> exclusions; + + @Override + public boolean matches(String target) { + boolean matches = true; + + if (inclusions != null && !inclusions.isEmpty()) { + boolean found = false; + for (String constraint : inclusions) { + if (EndpointHelper.matchPattern(target, constraint)) { + found = true; + break; + } + } + matches = found; + } + + // any exclusions + if (exclusions != null && !exclusions.isEmpty()) { + for (String constraint : exclusions) { + if (EndpointHelper.matchPattern(target, constraint)) { + // force false if this was an exclusion + matches = false; + break; + } + } + } + + return matches; + } + + public void addInclusion(String constraint) { + if (inclusions == null) { + inclusions = new LinkedHashSet<String>(); + } + inclusions.add(constraint); + } + + public void addExclusion(String constraint) { + if (exclusions == null) { + exclusions = new LinkedHashSet<String>(); + } + exclusions.add(constraint); + } + + public Set<String> getInclusions() { + return inclusions; + } + + public void setInclusions(Set<String> inclusions) { + this.inclusions = inclusions; + } + + public Set<String> getExclusions() { + return exclusions; + } + + public void setExclusions(Set<String> exclusions) { + this.exclusions = exclusions; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/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 deleted file mode 100644 index 2809c84..0000000 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpBasicAuthSubject.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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/f95326ec/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpPrincipal.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpPrincipal.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpPrincipal.java new file mode 100644 index 0000000..f6a95be --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpPrincipal.java @@ -0,0 +1,52 @@ +/** + * 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.security.Principal; + +/** + * Http {@link Principal}. + */ +public final class HttpPrincipal implements Principal { + + private final String username; + private final String password; + + public HttpPrincipal(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public String getName() { + return username; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + // do not display the password + return "HttpPrincipal[" + username + "]"; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/JAASSecurityAuthenticator.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/JAASSecurityAuthenticator.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/JAASSecurityAuthenticator.java new file mode 100644 index 0000000..8fb4c85 --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/JAASSecurityAuthenticator.java @@ -0,0 +1,107 @@ +/** + * 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.IOException; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A JAAS based {@link SecurityAuthenticator} implementation. + */ +public class JAASSecurityAuthenticator implements SecurityAuthenticator { + + private static final Logger LOG = LoggerFactory.getLogger(JAASSecurityAuthenticator.class); + private String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public Subject login(HttpPrincipal principal) throws LoginException { + if (ObjectHelper.isEmpty(getName())) { + throw new LoginException("Realm has not been configured on this SecurityAuthenticator: " + this); + } + + LOG.debug("Login username: {} using realm: {}", principal.getName(), getName()); + LoginContext context = new LoginContext(getName(), new HttpPrincipalCallbackHandler(principal)); + context.login(); + Subject subject = context.getSubject(); + LOG.debug("Login username: {} successful returning Subject: {}", principal.getName(), subject); + return subject; + } + + @Override + public void logout(Subject subject) throws LoginException { + if (ObjectHelper.isEmpty(getName())) { + throw new LoginException("Realm has not been configured on this SecurityAuthenticator: " + this); + } + + String username = ""; + if (!subject.getPrincipals().isEmpty()) { + username = subject.getPrincipals().iterator().next().getName(); + } + LOG.debug("Logging out username: {} using realm: {}", username, getName()); + LoginContext context = new LoginContext(getName(), subject); + context.logout(); + LOG.debug("Logout username: {} successful", username); + } + + /** + * {@link CallbackHandler} that provides the username and password. + */ + private final class HttpPrincipalCallbackHandler implements CallbackHandler { + + private final HttpPrincipal principal; + + private HttpPrincipalCallbackHandler(HttpPrincipal principal) { + this.principal = principal; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + LOG.trace("Callback {}", callback); + if (callback instanceof PasswordCallback) { + PasswordCallback pc = (PasswordCallback) callback; + LOG.trace("Setting password on callback {}", pc); + pc.setPassword(principal.getPassword().toCharArray()); + } else if (callback instanceof NameCallback) { + NameCallback nc = (NameCallback) callback; + LOG.trace("Setting username on callback {}", nc); + nc.setName(principal.getName()); + } + } + } + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/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 177ce94..5f92987 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,8 +46,7 @@ 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(); + private NettyHttpSecurityConfiguration nettyHttpSecurityConfiguration; public NettyHttpComponent() { // use the http configuration and filter strategy @@ -74,6 +73,9 @@ public class NettyHttpComponent extends NettyComponent implements HeaderFilterSt } } + // any custom security configuration + NettyHttpSecurityConfiguration securityConfiguration = resolveAndRemoveReferenceParameter(parameters, "nettyHttpSecurityConfiguration", NettyHttpSecurityConfiguration.class); + config = parseConfiguration(config, remaining, parameters); // validate config @@ -104,7 +106,10 @@ public class NettyHttpComponent extends NettyComponent implements HeaderFilterSt if (answer.getHeaderFilterStrategy() == null) { answer.setHeaderFilterStrategy(getHeaderFilterStrategy()); } - if (answer.getNettyHttpSecurityConfiguration() == null) { + + if (securityConfiguration != null) { + answer.setNettyHttpSecurityConfiguration(securityConfiguration); + } else if (answer.getNettyHttpSecurityConfiguration() == null) { answer.setNettyHttpSecurityConfiguration(getNettyHttpSecurityConfiguration()); } http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/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 index e04c497..9f0ebf9 100644 --- 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 @@ -16,12 +16,16 @@ */ package org.apache.camel.component.netty.http; +/** + * Security configuration for the {@link NettyHttpConsumer}. + */ public class NettyHttpSecurityConfiguration { private boolean authenticate = true; private String constraint = "BASIC"; - private String realm = "Camel"; + private String realm; private ContextPathMatcher contextPathMatcher; + private SecurityAuthenticator securityAuthenticator; public boolean isAuthenticate() { return authenticate; @@ -54,4 +58,12 @@ public class NettyHttpSecurityConfiguration { public void setContextPathMatcher(ContextPathMatcher contextPathMatcher) { this.contextPathMatcher = contextPathMatcher; } + + public SecurityAuthenticator getSecurityAuthenticator() { + return securityAuthenticator; + } + + public void setSecurityAuthenticator(SecurityAuthenticator securityAuthenticator) { + this.securityAuthenticator = securityAuthenticator; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticator.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticator.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticator.java new file mode 100644 index 0000000..65a976b --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticator.java @@ -0,0 +1,58 @@ +/** + * 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 javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +/** + * A {@link SecurityAuthenticator} allows to plugin custom authenticators, + * such as JAAS based or custom implementations. + */ +public interface SecurityAuthenticator { + + /** + * Sets the name of the realm to use. + */ + void setName(String name); + + /** + * Gets the name of the realm. + */ + String getName(); + + /** + * Attempts to login the {@link java.security.Principal} on this realm. + * <p/> + * The login is a success if no Exception is thrown. The implementation can return + * a {@link Subject} instance, but is not required to do so. + * + * @param principal the principal + * @return optional subject returned for successful login + * @throws LoginException is thrown if error logging in the {@link java.security.Principal} + */ + Subject login(HttpPrincipal principal) throws LoginException; + + /** + * Attempt to logout the subject. + * + * @param subject subject to logout + * @throws LoginException is thrown if error logging out subject + */ + void logout(Subject subject) throws LoginException; + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/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 ccc2415..8552f66 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 @@ -19,14 +19,17 @@ package org.apache.camel.component.netty.http.handlers; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.nio.charset.Charset; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; 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.HttpPrincipal; import org.apache.camel.component.netty.http.NettyHttpConsumer; import org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration; +import org.apache.camel.component.netty.http.SecurityAuthenticator; import org.apache.camel.util.ObjectHelper; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; @@ -109,20 +112,20 @@ public class HttpServerChannelHandler extends ServerChannelHandler { // is basic auth configured NettyHttpSecurityConfiguration security = consumer.getEndpoint().getNettyHttpSecurityConfiguration(); - if (security != null) { + if (security != null && security.isAuthenticate()) { 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) { + HttpPrincipal principal = extractBasicAuthSubject(request); + boolean authenticated = principal != null && authenticate(security.getSecurityAuthenticator(), principal) != null; + if (principal == null || !authenticated) { + if (principal == null) { LOG.debug("Http Basic Auth required for resource: {}", url); } else { - LOG.debug("Http Basic Auth not authorized for username: {}", subject.getUsername()); + LOG.debug("Http Basic Auth not authorized for username: {}", principal.getUsername()); } // restricted resource, so send back 401 to require valid username/password HttpResponse response = new DefaultHttpResponse(HTTP_1_1, UNAUTHORIZED); @@ -133,7 +136,7 @@ public class HttpServerChannelHandler extends ServerChannelHandler { messageEvent.getChannel().write(response); return; } else { - LOG.debug("Http Basic Auth authorized for username: {}", subject.getUsername()); + LOG.debug("Http Basic Auth authorized for username: {}", principal.getUsername()); } } } @@ -143,34 +146,47 @@ public class HttpServerChannelHandler extends ServerChannelHandler { } /** - * Authenticates the http basic auth subject. + * Extracts the username and password details from the HTTP basic header Authorization. + * <p/> + * This requires that the <tt>Authorization</tt> HTTP header is provided, and its using Basic. + * Currently Digest is <b>not</b> supported. * - * @param subject the subject - * @return <tt>true</tt> if username and password is valid, <tt>false</tt> if not + * @return {@link HttpPrincipal} with username and password details, or <tt>null</tt> if not possible to extract */ - protected boolean authenticate(HttpBasicAuthSubject subject) { - // TODO: an api for authentication - return subject.getPassword().equals("secret"); - //return true; - } - - protected static HttpBasicAuthSubject extractBasicAuthSubject(HttpRequest request) { + protected static HttpPrincipal 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; + if (constraint != null) { + if ("Basic".equalsIgnoreCase(constraint.trim())) { + 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, ":"); + HttpPrincipal principal = new HttpPrincipal(username, password); + + LOG.debug("Extracted Basic Auth principal from HTTP header: {}", principal); + return principal; + } + } } return null; } + /** + * Authenticates the http basic auth subject. + * + * @param authenticator the authenticator + * @param principal the principal + * @return <tt>true</tt> if username and password is valid, <tt>false</tt> if not + */ + protected Subject authenticate(SecurityAuthenticator authenticator, HttpPrincipal principal) throws LoginException { + return authenticator.login(principal); + } + @Override protected void beforeProcess(Exchange exchange, MessageEvent messageEvent) { if (consumer.getConfiguration().isBridgeEndpoint()) { http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcherTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcherTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcherTest.java new file mode 100644 index 0000000..6914c8c --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/ConstraintMappingContextPathMatcherTest.java @@ -0,0 +1,108 @@ +/** + * 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 junit.framework.TestCase; + +public class ConstraintMappingContextPathMatcherTest extends TestCase { + + public void testDefault() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + + assertTrue(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + } + + public void testFoo() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addInclusion("/foo"); + + assertFalse(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertFalse(matcher.matches("/foobar")); + assertFalse(matcher.matches("/foo/bar")); + } + + public void testFooWildcard() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addInclusion("/foo*"); + + assertFalse(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertTrue(matcher.matches("/foobar")); + assertTrue(matcher.matches("/foo/bar")); + } + + public void testFooBar() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addInclusion("/foo"); + matcher.addInclusion("/bar"); + + assertFalse(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertFalse(matcher.matches("/foobar")); + assertFalse(matcher.matches("/foo/bar")); + + assertTrue(matcher.matches("/bar")); + assertFalse(matcher.matches("/barbar")); + assertFalse(matcher.matches("/bar/bar")); + } + + public void testFooBarWildcard() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addInclusion("/foo*"); + matcher.addInclusion("/bar*"); + + assertFalse(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertTrue(matcher.matches("/foobar")); + assertTrue(matcher.matches("/foo/bar")); + + assertTrue(matcher.matches("/bar")); + assertTrue(matcher.matches("/barbar")); + assertTrue(matcher.matches("/bar/bar")); + } + + public void testFooExclusion() { + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addInclusion("/foo/*"); + matcher.addExclusion("/foo/public/*"); + + assertFalse(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertTrue(matcher.matches("/foo/bar")); + assertFalse(matcher.matches("/foo/public")); + assertFalse(matcher.matches("/foo/public/open")); + } + + public void testDefaultExclusion() { + // everything is restricted unless its from the public + ConstraintMappingContextPathMatcher matcher = new ConstraintMappingContextPathMatcher(); + matcher.addExclusion("/public/*"); + matcher.addExclusion("/index"); + matcher.addExclusion("/index.html"); + + assertTrue(matcher.matches("/")); + assertTrue(matcher.matches("/foo")); + assertTrue(matcher.matches("/foo/bar")); + assertFalse(matcher.matches("/public")); + assertFalse(matcher.matches("/public/open")); + assertFalse(matcher.matches("/index")); + assertFalse(matcher.matches("/index.html")); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyLoginModule.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyLoginModule.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyLoginModule.java new file mode 100644 index 0000000..ad5143a --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyLoginModule.java @@ -0,0 +1,92 @@ +/** + * 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.IOException; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +public class MyLoginModule implements LoginModule { + + private Subject subject; + private CallbackHandler callbackHandler; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { + this.subject = subject; + this.callbackHandler = callbackHandler; + } + + @Override + public boolean login() throws LoginException { + + // get username and password + Callback[] callbacks = new Callback[2]; + callbacks[0] = new NameCallback("username"); + callbacks[1] = new PasswordCallback("password", false); + + try { + callbackHandler.handle(callbacks); + String username = ((NameCallback)callbacks[0]).getName(); + char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); + String password = new String(tmpPassword); + ((PasswordCallback)callbacks[1]).clearPassword(); + + // only allow login if password is secret + // as this is just for testing purpose + if (!"secret".equals(password)) { + throw new LoginException("Login denied"); + } + } catch (IOException ioe) { + LoginException le = new LoginException(ioe.toString()); + le.initCause(ioe); + throw le; + } catch (UnsupportedCallbackException uce) { + LoginException le = new LoginException("Error: " + uce.getCallback().toString() + + " not available to gather authentication information from the user"); + le.initCause(uce); + throw le; + } + + return true; + } + + @Override + public boolean commit() throws LoginException { + return true; + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean logout() throws LoginException { + subject = null; + callbackHandler = null; + return true; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/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 index 048f1b4..d4e0b6e 100644 --- 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 @@ -16,22 +16,58 @@ */ package org.apache.camel.component.netty.http; +import org.apache.camel.CamelExecutionException; import org.apache.camel.builder.RouteBuilder; -import org.junit.Ignore; +import org.apache.camel.impl.JndiRegistry; import org.junit.Test; -@Ignore public class NettyHttpSimpleBasicAuthTest extends BaseNettyTest { + @Override + public void setUp() throws Exception { + System.setProperty("java.security.auth.login.config", "src/test/resources/myjaas.config"); + super.setUp(); + } + + @Override + public void tearDown() throws Exception { + System.clearProperty("java.security.auth.login.config"); + super.tearDown(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndi = super.createRegistry(); + + NettyHttpSecurityConfiguration security = new NettyHttpSecurityConfiguration(); + security.setRealm("karaf"); + SecurityAuthenticator auth = new JAASSecurityAuthenticator(); + auth.setName("karaf"); + security.setSecurityAuthenticator(auth); + + jndi.bind("mySecurityConfig", security); + + return jndi; + } + @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); + public void testBasicAuth() throws Exception { + try { + template.requestBody("netty-http:http://localhost:{{port}}/foo", "Hello World", String.class); + fail("Should send back 401"); + } catch (CamelExecutionException e) { + NettyHttpOperationFailedException cause = assertIsInstanceOf(NettyHttpOperationFailedException.class, e.getCause()); + assertEquals(401, cause.getStatusCode()); + } + + getMockEndpoint("mock:input").expectedBodiesReceived("Hello World"); + + // username:password is scott:secret + String auth = "Basic c2NvdHQ6c2VjcmV0"; + String out = template.requestBodyAndHeader("netty-http:http://localhost:{{port}}/foo", "Hello World", "Authorization", auth, String.class); + assertEquals("Bye World", out); + + assertMockEndpointsSatisfied(); } @Override @@ -39,7 +75,7 @@ public class NettyHttpSimpleBasicAuthTest extends BaseNettyTest { return new RouteBuilder() { @Override public void configure() throws Exception { - from("netty-http:http://0.0.0.0:{{port}}/foo") + from("netty-http:http://0.0.0.0:{{port}}/foo?nettyHttpSecurityConfiguration=#mySecurityConfig") .to("mock:input") .transform().constant("Bye World"); } http://git-wip-us.apache.org/repos/asf/camel/blob/f95326ec/components/camel-netty-http/src/test/resources/myjaas.config ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/resources/myjaas.config b/components/camel-netty-http/src/test/resources/myjaas.config new file mode 100644 index 0000000..40749ce --- /dev/null +++ b/components/camel-netty-http/src/test/resources/myjaas.config @@ -0,0 +1,5 @@ +/** Test Login Configuration **/ + +karaf { + org.apache.camel.component.netty.http.MyLoginModule required debug=true; +}; \ No newline at end of file
