You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2013/07/15 13:48:32 UTC
[1/2] git commit: CAMEL-6424: camel-netty-http added support for
basic auth. Work in progress.
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 <da...@apache.org>
Authored: Mon Jul 15 13:48:00 2013 +0200
Committer: Claus Ibsen <da...@apache.org>
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
[2/2] git commit: CAMEL-6424: camel-netty-http added support for
basic auth. Work in progress.
Posted by da...@apache.org.
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 <da...@apache.org>
Authored: Mon Jul 15 10:09:36 2013 +0200
Committer: Claus Ibsen <da...@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