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/342bfe1d Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/342bfe1d Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/342bfe1d Branch: refs/heads/master Commit: 342bfe1dc3966621438b5923b5f0d4427f26c3d8 Parents: 8042c33 Author: Claus Ibsen <davscl...@apache.org> Authored: Tue Jul 16 13:51:32 2013 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Tue Jul 16 13:51:46 2013 +0200 ---------------------------------------------------------------------- components/camel-netty-http/pom.xml | 2 +- .../netty/http/DefaultSecurityConstraint.java | 127 ------------------ .../netty/http/JAASSecurityAuthenticator.java | 72 +--------- .../netty/http/SecurityAuthenticator.java | 9 ++ .../http/SecurityAuthenticatorSupport.java | 127 ++++++++++++++++++ .../netty/http/SecurityConstraintMapping.java | 133 +++++++++++++++++++ .../http/DefaultSecurityConstraintTest.java | 108 --------------- .../component/netty/http/MyLoginModule.java | 10 ++ .../component/netty/http/MyRolePrincipal.java | 33 +++++ .../NettyHttpBasicAuthConstraintMapperTest.java | 2 +- ...HttpSimpleBasicAuthConstraintMapperTest.java | 2 +- .../http/SecurityConstraintMappingTest.java | 108 +++++++++++++++ .../http/SpringNettyHttpBasicAuthTest.java | 115 ++++++++++++++++ .../netty/http/SpringNettyHttpBasicAuthTest.xml | 70 ++++++++++ 14 files changed, 609 insertions(+), 309 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/pom.xml b/components/camel-netty-http/pom.xml index 4e8057a..ec51526 100644 --- a/components/camel-netty-http/pom.xml +++ b/components/camel-netty-http/pom.xml @@ -44,7 +44,7 @@ <!-- testing --> <dependency> <groupId>org.apache.camel</groupId> - <artifactId>camel-test</artifactId> + <artifactId>camel-test-spring</artifactId> <scope>test</scope> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultSecurityConstraint.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultSecurityConstraint.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultSecurityConstraint.java deleted file mode 100644 index 91effe5..0000000 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultSecurityConstraint.java +++ /dev/null @@ -1,127 +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.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.camel.util.EndpointHelper; - -/** - * A default {@link SecurityConstraint} which can be used to define a set of mappings to - * as constraints. - * <p/> - * This constraint will match as <tt>true</tt> if no inclusions has been defined. - * First all the inclusions is check for matching. If a inclusion matches, - * then the exclusion is checked, and if any of them matches, then the exclusion - * will override the match and force returning <tt>false</tt>. - * <p/> - * Wildcards and regular expressions is supported as this implementation uses - * {@link EndpointHelper#matchPattern(String, String)} method for matching. - * <p/> - * This restricted constraint allows you to setup context path rules that will restrict - * access to paths, and then override and have exclusions that may allow access to - * public paths. - */ -public class DefaultSecurityConstraint implements SecurityConstraint { - - // url -> roles - private Map<String, String> inclusions; - // url - private Set<String> exclusions; - - @Override - public String restricted(String url) { - // check excluded first - if (excludedUrl(url)) { - return null; - } - - // is the url restricted? - String constraint = includedUrl(url); - if (constraint == null) { - return null; - } - - // is there any roles for the restricted url? - String roles = inclusions != null ? inclusions.get(constraint) : null; - if (roles == null) { - // use wildcard to indicate any role is accepted - return "*"; - } else { - return roles; - } - } - - private String includedUrl(String url) { - if (inclusions != null && !inclusions.isEmpty()) { - for (String constraint : inclusions.keySet()) { - if (EndpointHelper.matchPattern(url, constraint)) { - return constraint; - } - } - // not in included so return null as its not restricted - return null; - } - - // by default if no included has been configured then everything is restricted - return "*"; - } - - private boolean excludedUrl(String url) { - if (exclusions != null && !exclusions.isEmpty()) { - for (String constraint : exclusions) { - if (EndpointHelper.matchPattern(url, constraint)) { - // force not matches if this was an exclusion - return true; - } - } - } - - return false; - } - - public void addInclusion(String constraint) { - if (inclusions == null) { - inclusions = new java.util.LinkedHashMap<String, String>(); - } - inclusions.put(constraint, null); - } - - public void addInclusion(String constraint, String roles) { - if (inclusions == null) { - inclusions = new java.util.LinkedHashMap<String, String>(); - } - inclusions.put(constraint, roles); - } - - public void addExclusion(String constraint) { - if (exclusions == null) { - exclusions = new LinkedHashSet<String>(); - } - exclusions.add(constraint); - } - - public void setInclusions(Map<String, String> inclusions) { - this.inclusions = inclusions; - } - - public void setExclusions(Set<String> exclusions) { - this.exclusions = exclusions; - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/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 index fd52ca8..09204dd 100644 --- 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 @@ -16,15 +16,8 @@ */ package org.apache.camel.component.netty.http; -import java.io.IOException; import java.security.Principal; -import java.util.Locale; 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; @@ -35,23 +28,9 @@ import org.slf4j.LoggerFactory; /** * A JAAS based {@link SecurityAuthenticator} implementation. */ -public class JAASSecurityAuthenticator implements SecurityAuthenticator { +public class JAASSecurityAuthenticator extends SecurityAuthenticatorSupport { private static final Logger LOG = LoggerFactory.getLogger(JAASSecurityAuthenticator.class); - private String name; - private String roleClassNames; - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setRoleClassNames(String roleClassNames) { - this.roleClassNames = roleClassNames; - } @Override public Subject login(HttpPrincipal principal) throws LoginException { @@ -90,53 +69,4 @@ public class JAASSecurityAuthenticator implements SecurityAuthenticator { LOG.debug("Logout username: {} successful", username); } - @Override - public String getUserRoles(Subject subject) { - StringBuilder sb = new StringBuilder(); - for (Principal p : subject.getPrincipals()) { - if (roleClassNames == null) { - // by default assume its a role when the classname has role in its name - if (p.getName().toLowerCase(Locale.US).contains("role")) { - if (sb.length() > 0) { - sb.append(","); - } - sb.append(p.getName()); - } - } - } - if (sb.length() > 0) { - return sb.toString(); - } else { - return null; - } - } - - /** - * {@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/342bfe1d/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 index e1be404..9e4ca53 100644 --- 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 @@ -35,6 +35,15 @@ public interface SecurityAuthenticator { */ String getName(); + /** + * Sets the role class names (separated by comma) + * <p/> + * By default if no explicit role class names has been configured, then this implementation + * will assume the {@link Subject} {@link java.security.Principal}s is a role if the classname + * contains the word <tt>role</tt> (lower cased). + * + * @param names a list of FQN class names for role {@link java.security.Principal} implementations. + */ void setRoleClassNames(String names); /** http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticatorSupport.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticatorSupport.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticatorSupport.java new file mode 100644 index 0000000..212736c --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityAuthenticatorSupport.java @@ -0,0 +1,127 @@ +/** + * 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.security.Principal; +import java.util.Iterator; +import java.util.Locale; +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 org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A base class for {@link SecurityAuthenticator}. + */ +public abstract class SecurityAuthenticatorSupport implements SecurityAuthenticator { + + private String name; + private String roleClassNames; + + public SecurityAuthenticatorSupport() { + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setRoleClassNames(String roleClassNames) { + this.roleClassNames = roleClassNames; + } + + /** + * Is the given principal a role class? + * + * @param principal the principal + * @return <tt>true</tt> if role class, <tt>false</tt> if not + */ + protected boolean isRoleClass(Principal principal) { + if (roleClassNames == null) { + // by default assume its a role when the classname has role in its name + return principal.getClass().getName().toLowerCase(Locale.US).contains("role"); + } + + // check each role class name if they match the principal class name + Iterator it = ObjectHelper.createIterator(roleClassNames); + while (it.hasNext()) { + String name = it.next().toString().trim(); + if (principal.getClass().getName().equals(name)) { + return true; + } + } + return false; + } + + @Override + public String getUserRoles(Subject subject) { + StringBuilder sb = new StringBuilder(); + for (Principal p : subject.getPrincipals()) { + if (isRoleClass(p)) { + if (sb.length() > 0) { + sb.append(","); + } + sb.append(p.getName()); + } + } + if (sb.length() > 0) { + return sb.toString(); + } else { + return null; + } + } + + /** + * {@link javax.security.auth.callback.CallbackHandler} that provides the username and password. + */ + public static final class HttpPrincipalCallbackHandler implements CallbackHandler { + + private static final Logger LOG = LoggerFactory.getLogger(HttpPrincipalCallbackHandler.class); + + private final HttpPrincipal principal; + + public 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()); + } + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityConstraintMapping.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityConstraintMapping.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityConstraintMapping.java new file mode 100644 index 0000000..7c84bd2 --- /dev/null +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/SecurityConstraintMapping.java @@ -0,0 +1,133 @@ +/** + * 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.Map; +import java.util.Set; + +import org.apache.camel.util.EndpointHelper; + +/** + * A default {@link SecurityConstraint} which can be used to define a set of mappings to + * as constraints. + * <p/> + * This constraint will match as <tt>true</tt> if no inclusions has been defined. + * First all the inclusions is check for matching. If a inclusion matches, + * then the exclusion is checked, and if any of them matches, then the exclusion + * will override the match and force returning <tt>false</tt>. + * <p/> + * Wildcards and regular expressions is supported as this implementation uses + * {@link EndpointHelper#matchPattern(String, String)} method for matching. + * <p/> + * This restricted constraint allows you to setup context path rules that will restrict + * access to paths, and then override and have exclusions that may allow access to + * public paths. + */ +public class SecurityConstraintMapping implements SecurityConstraint { + + // url -> roles + private Map<String, String> inclusions; + // url + private Set<String> exclusions; + + @Override + public String restricted(String url) { + // check excluded first + if (excludedUrl(url)) { + return null; + } + + // is the url restricted? + String constraint = includedUrl(url); + if (constraint == null) { + return null; + } + + // is there any roles for the restricted url? + String roles = inclusions != null ? inclusions.get(constraint) : null; + if (roles == null) { + // use wildcard to indicate any role is accepted + return "*"; + } else { + return roles; + } + } + + private String includedUrl(String url) { + String candidate = null; + if (inclusions != null && !inclusions.isEmpty()) { + for (String constraint : inclusions.keySet()) { + if (EndpointHelper.matchPattern(url, constraint)) { + if (candidate == null) { + candidate = constraint; + } else if (constraint.length() > candidate.length()) { + // we want the constraint that has the longest context-path matching as its + // the most explicit for the target url + candidate = constraint; + } + } + } + return candidate; + } + + // by default if no included has been configured then everything is restricted + return "*"; + } + + private boolean excludedUrl(String url) { + if (exclusions != null && !exclusions.isEmpty()) { + for (String constraint : exclusions) { + if (EndpointHelper.matchPattern(url, constraint)) { + // force not matches if this was an exclusion + return true; + } + } + } + + return false; + } + + public void addInclusion(String constraint) { + if (inclusions == null) { + inclusions = new java.util.LinkedHashMap<String, String>(); + } + inclusions.put(constraint, null); + } + + public void addInclusion(String constraint, String roles) { + if (inclusions == null) { + inclusions = new java.util.LinkedHashMap<String, String>(); + } + inclusions.put(constraint, roles); + } + + public void addExclusion(String constraint) { + if (exclusions == null) { + exclusions = new LinkedHashSet<String>(); + } + exclusions.add(constraint); + } + + public void setInclusions(Map<String, String> inclusions) { + this.inclusions = inclusions; + } + + public void setExclusions(Set<String> exclusions) { + this.exclusions = exclusions; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/DefaultSecurityConstraintTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/DefaultSecurityConstraintTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/DefaultSecurityConstraintTest.java deleted file mode 100644 index 16790f7..0000000 --- a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/DefaultSecurityConstraintTest.java +++ /dev/null @@ -1,108 +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 junit.framework.TestCase; - -public class DefaultSecurityConstraintTest extends TestCase { - - public void testDefault() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - - assertNotNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - } - - public void testFoo() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addInclusion("/foo"); - - assertNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNull(matcher.restricted("/foobar")); - assertNull(matcher.restricted("/foo/bar")); - } - - public void testFooWildcard() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addInclusion("/foo*"); - - assertNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNotNull(matcher.restricted("/foobar")); - assertNotNull(matcher.restricted("/foo/bar")); - } - - public void testFooBar() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addInclusion("/foo"); - matcher.addInclusion("/bar"); - - assertNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNull(matcher.restricted("/foobar")); - assertNull(matcher.restricted("/foo/bar")); - - assertNotNull(matcher.restricted("/bar")); - assertNull(matcher.restricted("/barbar")); - assertNull(matcher.restricted("/bar/bar")); - } - - public void testFooBarWildcard() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addInclusion("/foo*"); - matcher.addInclusion("/bar*"); - - assertNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNotNull(matcher.restricted("/foobar")); - assertNotNull(matcher.restricted("/foo/bar")); - - assertNotNull(matcher.restricted("/bar")); - assertNotNull(matcher.restricted("/barbar")); - assertNotNull(matcher.restricted("/bar/bar")); - } - - public void testFooExclusion() { - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addInclusion("/foo/*"); - matcher.addExclusion("/foo/public/*"); - - assertNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNotNull(matcher.restricted("/foo/bar")); - assertNull(matcher.restricted("/foo/public")); - assertNull(matcher.restricted("/foo/public/open")); - } - - public void testDefaultExclusion() { - // everything is restricted unless its from the public - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); - matcher.addExclusion("/public/*"); - matcher.addExclusion("/index"); - matcher.addExclusion("/index.html"); - - assertNotNull(matcher.restricted("/")); - assertNotNull(matcher.restricted("/foo")); - assertNotNull(matcher.restricted("/foo/bar")); - assertNull(matcher.restricted("/public")); - assertNull(matcher.restricted("/public/open")); - assertNull(matcher.restricted("/index")); - assertNull(matcher.restricted("/index.html")); - } - -} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/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 index ad5143a..f2bd5c9 100644 --- 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 @@ -58,6 +58,15 @@ public class MyLoginModule implements LoginModule { if (!"secret".equals(password)) { throw new LoginException("Login denied"); } + + // add roles + if ("scott".equals(username)) { + subject.getPrincipals().add(new MyRolePrincipal("admin")); + subject.getPrincipals().add(new MyRolePrincipal("guest")); + } else if ("guest".equals(username)) { + subject.getPrincipals().add(new MyRolePrincipal("guest")); + } + } catch (IOException ioe) { LoginException le = new LoginException(ioe.toString()); le.initCause(ioe); @@ -69,6 +78,7 @@ public class MyLoginModule implements LoginModule { throw le; } + return true; } http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyRolePrincipal.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyRolePrincipal.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyRolePrincipal.java new file mode 100644 index 0000000..e912a07 --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/MyRolePrincipal.java @@ -0,0 +1,33 @@ +/** + * 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; + +public class MyRolePrincipal implements Principal { + + private final String role; + + public MyRolePrincipal(String role) { + this.role = role; + } + + @Override + public String getName() { + return role; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpBasicAuthConstraintMapperTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpBasicAuthConstraintMapperTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpBasicAuthConstraintMapperTest.java index 890be6c..879b4f2 100644 --- a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpBasicAuthConstraintMapperTest.java +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpBasicAuthConstraintMapperTest.java @@ -45,7 +45,7 @@ public class NettyHttpBasicAuthConstraintMapperTest extends BaseNettyTest { auth.setName("karaf"); security.setSecurityAuthenticator(auth); - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); matcher.addInclusion("/foo/*"); matcher.addExclusion("/foo/public/*"); security.setSecurityConstraint(matcher); http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthConstraintMapperTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthConstraintMapperTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthConstraintMapperTest.java index c68ba0d..a241a80 100644 --- a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthConstraintMapperTest.java +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSimpleBasicAuthConstraintMapperTest.java @@ -39,7 +39,7 @@ public class NettyHttpSimpleBasicAuthConstraintMapperTest extends BaseNettyTest protected JndiRegistry createRegistry() throws Exception { JndiRegistry jndi = super.createRegistry(); - DefaultSecurityConstraint matcher = new DefaultSecurityConstraint(); + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); matcher.addInclusion("/foo/*"); matcher.addExclusion("/foo/public/*"); http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SecurityConstraintMappingTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SecurityConstraintMappingTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SecurityConstraintMappingTest.java new file mode 100644 index 0000000..5d95ae8 --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SecurityConstraintMappingTest.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 SecurityConstraintMappingTest extends TestCase { + + public void testDefault() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + + assertNotNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + } + + public void testFoo() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addInclusion("/foo"); + + assertNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNull(matcher.restricted("/foobar")); + assertNull(matcher.restricted("/foo/bar")); + } + + public void testFooWildcard() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addInclusion("/foo*"); + + assertNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNotNull(matcher.restricted("/foobar")); + assertNotNull(matcher.restricted("/foo/bar")); + } + + public void testFooBar() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addInclusion("/foo"); + matcher.addInclusion("/bar"); + + assertNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNull(matcher.restricted("/foobar")); + assertNull(matcher.restricted("/foo/bar")); + + assertNotNull(matcher.restricted("/bar")); + assertNull(matcher.restricted("/barbar")); + assertNull(matcher.restricted("/bar/bar")); + } + + public void testFooBarWildcard() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addInclusion("/foo*"); + matcher.addInclusion("/bar*"); + + assertNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNotNull(matcher.restricted("/foobar")); + assertNotNull(matcher.restricted("/foo/bar")); + + assertNotNull(matcher.restricted("/bar")); + assertNotNull(matcher.restricted("/barbar")); + assertNotNull(matcher.restricted("/bar/bar")); + } + + public void testFooExclusion() { + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addInclusion("/foo/*"); + matcher.addExclusion("/foo/public/*"); + + assertNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNotNull(matcher.restricted("/foo/bar")); + assertNull(matcher.restricted("/foo/public")); + assertNull(matcher.restricted("/foo/public/open")); + } + + public void testDefaultExclusion() { + // everything is restricted unless its from the public + SecurityConstraintMapping matcher = new SecurityConstraintMapping(); + matcher.addExclusion("/public/*"); + matcher.addExclusion("/index"); + matcher.addExclusion("/index.html"); + + assertNotNull(matcher.restricted("/")); + assertNotNull(matcher.restricted("/foo")); + assertNotNull(matcher.restricted("/foo/bar")); + assertNull(matcher.restricted("/public")); + assertNull(matcher.restricted("/public/open")); + assertNull(matcher.restricted("/index")); + assertNull(matcher.restricted("/index.html")); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.java new file mode 100644 index 0000000..01f875d --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.java @@ -0,0 +1,115 @@ +/** + * 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.annotation.Resource; + +import junit.framework.TestCase; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.xml"}) +public class SpringNettyHttpBasicAuthTest extends TestCase { + + @Produce + private ProducerTemplate template; + + @EndpointInject(uri = "mock:input") + private MockEndpoint mockEndpoint; + + private Integer port; + + public Integer getPort() { + return port; + } + + @Resource(name = "dynaPort") + public void setPort(Integer port) { + this.port = port; + } + + @BeforeClass + public static void setUpJaas() throws Exception { + System.setProperty("java.security.auth.login.config", "src/test/resources/myjaas.config"); + } + + @AfterClass + public static void tearDownJaas() throws Exception { + System.clearProperty("java.security.auth.login.config"); + } + + @Test + public void testAdminAuth() throws Exception { + try { + template.requestBody("netty-http:http://localhost:" + port + "/foo", "Hello Foo", String.class); + fail("Should send back 401"); + } catch (CamelExecutionException e) { + NettyHttpOperationFailedException cause = (NettyHttpOperationFailedException) e.getCause(); + assertEquals(401, cause.getStatusCode()); + } + + mockEndpoint.reset(); + mockEndpoint.expectedBodiesReceived("Hello Public", "Hello Foo", "Hello Admin"); + + // public do not need authentication + String out = template.requestBody("netty-http:http://localhost:" + port + "/foo/public/welcome", "Hello Public", String.class); + assertEquals("Bye /foo/public/welcome", out); + + // username:password is scott:secret + String auth = "Basic c2NvdHQ6c2VjcmV0"; + out = template.requestBodyAndHeader("netty-http:http://localhost:" + port + "/foo", "Hello Foo", "Authorization", auth, String.class); + assertEquals("Bye /foo", out); + + out = template.requestBodyAndHeader("netty-http:http://localhost:" + port + "/foo/admin/users", "Hello Admin", "Authorization", auth, String.class); + assertEquals("Bye /foo/admin/users", out); + + mockEndpoint.assertIsSatisfied(); + } + + @Test + public void testGuestAuth() throws Exception { + // username:password is guest:secret + String auth = "Basic Z3Vlc3Q6c2VjcmV0"; + String out = template.requestBodyAndHeader("netty-http:http://localhost:" + port + "/foo/guest/hello", "Hello Guest", "Authorization", auth, String.class); + assertEquals("Bye /foo/guest/hello", out); + + // accessing admin is restricted for guest user + try { + template.requestBodyAndHeader("netty-http:http://localhost:" + port + "/foo/admin/users", "Hello Admin", "Authorization", auth, String.class); + fail("Should send back 401"); + } catch (CamelExecutionException e) { + NettyHttpOperationFailedException cause = (NettyHttpOperationFailedException) e.getCause(); + assertEquals(401, cause.getStatusCode()); + } + + // but we can access foo as that is any roles + out = template.requestBodyAndHeader("netty-http:http://localhost:" + port + "/foo", "Hello Foo", "Authorization", auth, String.class); + assertEquals("Bye /foo", out); + } + + +} http://git-wip-us.apache.org/repos/asf/camel/blob/342bfe1d/components/camel-netty-http/src/test/resources/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.xml ---------------------------------------------------------------------- diff --git a/components/camel-netty-http/src/test/resources/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.xml b/components/camel-netty-http/src/test/resources/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.xml new file mode 100644 index 0000000..7f423ce --- /dev/null +++ b/components/camel-netty-http/src/test/resources/org/apache/camel/component/netty/http/SpringNettyHttpBasicAuthTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd + "> + + <bean id="dynaPort" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> + <property name="targetClass"> + <value>org.apache.camel.test.AvailablePortFinder</value> + </property> + <property name="targetMethod"> + <value>getNextAvailable</value> + </property> + <property name="arguments"> + <list> + <value>9000</value> + </list> + </property> + </bean> + + <bean id="constraint" class="org.apache.camel.component.netty.http.SecurityConstraintMapping"> + <!-- inclusions defines url -> roles restrictions --> + <!-- a * should be used for any role accepted (or even no roles) --> + <property name="inclusions"> + <map> + <entry key="/foo/*" value="*"/> + <entry key="/foo/admin/*" value="admin"/> + <entry key="/foo/guest/*" value="admin,guest"/> + </map> + </property> + <!-- exclusions is used to define public urls, which requires no authentication --> + <property name="exclusions"> + <set> + <value>/foo/public/*</value> + </set> + </property> + </bean> + + <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> + <endpoint id="input1" uri="netty-http:http://0.0.0.0:#{dynaPort}/foo?matchOnUriPrefix=true&securityConfiguration.realm=karaf&securityConfiguration.securityConstraint=#constraint"/> + + <route> + <from ref="input1"/> + <to uri="mock:input"/> + <transform> + <simple>Bye ${header.CamelHttpUri}</simple> + </transform> + </route> + + </camelContext> + +</beans>