diff options
| author | Robert Gemmell <robbie@apache.org> | 2013-04-01 01:36:18 +0000 |
|---|---|---|
| committer | Robert Gemmell <robbie@apache.org> | 2013-04-01 01:36:18 +0000 |
| commit | 7f45c27cf8eec8e4ee381ed01c7b33544cefce47 (patch) | |
| tree | 733b6161ef315f1dcf67bb945871d0b879b534d2 /java | |
| parent | e6df5df5f1c26c0b49c730d1da967fd02e3ce865 (diff) | |
| download | qpid-python-7f45c27cf8eec8e4ee381ed01c7b33544cefce47.tar.gz | |
QPID-4676: change External auth provider to create usernames of the form <CN>@<DC1>.<DC2>....<DCN> by default
- Allows for use of SSL Client Authentication in manner more consistent with the C++ broker
- Adds 'useFullDN' attribute to the auth provider to allow enabling use of the old behaviour
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@1463074 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java')
5 files changed, 237 insertions, 17 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java index 43e0a9f64f..c503549bf2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java @@ -31,8 +31,11 @@ public class ExternalAuthenticationManager implements AuthenticationManager { private static final String EXTERNAL = "EXTERNAL"; - ExternalAuthenticationManager() + private boolean _useFullDN = false; + + ExternalAuthenticationManager(boolean useFullDN) { + _useFullDN = useFullDN; } @Override @@ -52,7 +55,7 @@ public class ExternalAuthenticationManager implements AuthenticationManager { if(EXTERNAL.equals(mechanism)) { - return new ExternalSaslServer(externalPrincipal); + return new ExternalSaslServer(externalPrincipal, _useFullDN); } else { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java index 64acfafc4a..6029674cd3 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java @@ -19,22 +19,32 @@ */ package org.apache.qpid.server.security.auth.manager; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.util.ResourceBundleLoader; public class ExternalAuthenticationManagerFactory implements AuthenticationManagerFactory { + public static final String RESOURCE_BUNDLE = "org.apache.qpid.server.security.auth.manager.ExternalAuthenticationProviderAttributeDescriptions"; public static final String PROVIDER_TYPE = "External"; + public static final String ATTRIBUTE_USE_FULL_DN = "useFullDN"; + + public static final Collection<String> ATTRIBUTES = Collections.<String> unmodifiableList(Arrays.asList( + ATTRIBUTE_TYPE, + ATTRIBUTE_USE_FULL_DN)); @Override public AuthenticationManager createInstance(Map<String, Object> attributes) { if (attributes != null && PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { - return new ExternalAuthenticationManager(); + boolean useFullDN = Boolean.valueOf(String.valueOf(attributes.get(ATTRIBUTE_USE_FULL_DN))); + + return new ExternalAuthenticationManager(useFullDN); } return null; } @@ -42,7 +52,7 @@ public class ExternalAuthenticationManagerFactory implements AuthenticationManag @Override public Collection<String> getAttributeNames() { - return Collections.<String>singletonList(ATTRIBUTE_TYPE); + return ATTRIBUTES; } @Override @@ -54,7 +64,7 @@ public class ExternalAuthenticationManagerFactory implements AuthenticationManag @Override public Map<String, String> getAttributeDescriptions() { - return null; + return ResourceBundleLoader.getResources(RESOURCE_BUNDLE); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationProviderAttributeDescriptions.properties b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationProviderAttributeDescriptions.properties new file mode 100644 index 0000000000..263254e9fe --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationProviderAttributeDescriptions.properties @@ -0,0 +1,19 @@ +# +# 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. + +useFullDN=Use the full DN as the Username
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java index 9c2bca2d0b..509442b14b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java @@ -19,19 +19,27 @@ package org.apache.qpid.server.security.auth.sasl.external; import java.security.Principal; + +import javax.security.auth.x500.X500Principal; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.UsernamePrincipal; public class ExternalSaslServer implements SaslServer { + private static final Logger LOGGER = Logger.getLogger(ExternalSaslServer.class); + public static final String MECHANISM = "EXTERNAL"; private boolean _complete = false; private final Principal _externalPrincipal; + private boolean _useFullDN = false; - public ExternalSaslServer(Principal externalPrincipal) + public ExternalSaslServer(Principal externalPrincipal, boolean useFullDN) { + _useFullDN = useFullDN; _externalPrincipal = externalPrincipal; } @@ -77,6 +85,83 @@ public class ExternalSaslServer implements SaslServer public Principal getAuthenticatedPrincipal() { - return _externalPrincipal; + if (_externalPrincipal instanceof X500Principal && !_useFullDN) + { + // Construct username as <CN>@<DC1>.<DC2>.<DC3>....<DCN> + + String username; + String dn = ((X500Principal) _externalPrincipal).getName(X500Principal.RFC2253); + + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Parsing username from Principal DN: " + dn); + } + + if (dn.contains("CN=")) + { + username = dn.substring(dn.indexOf("CN=") + 3, (dn.indexOf(",", dn.indexOf("CN=")) != -1) ? dn.indexOf(",", dn.indexOf("CN=")) : dn.length()); + + if (username.isEmpty()) + { + // CN is empty => Cannot construct username => Authentication failed => return null + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("CN value was empty in Principal name, unable to construct username"); + } + return null; + } + else + { + if (dn.contains("DC=")) + { + int start = 0; + String dc = ""; + + while (dn.indexOf("DC=", start) != -1) + { + int dcStart = dn.indexOf("DC=", start) + 3; + int dcEnd = (dn.indexOf(",", dn.indexOf("DC=", start)) != -1) ? dn.indexOf(",", dn.indexOf("DC=", start)) : dn.length(); + + if (dc.isEmpty()) + { + dc = dn.substring(dcStart, dcEnd); + } + else + { + dc = dc.concat(".").concat(dn.substring(dcStart, dcEnd)); + } + + start = dn.indexOf("DC=", start) + 1; + } + + username = username.concat("@").concat(dc); + } + } + + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Constructing Principal with username: " + username); + } + return new UsernamePrincipal(username); + } + else + { + // No CN => Cannot construct username => Authentication failed => return null + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("No CN= present in DN, unable to construct username"); + } + return null; + } + } + else + { + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Using external Principal: " + _externalPrincipal); + } + + return _externalPrincipal; + } } }
\ No newline at end of file diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java index a66d73c47d..a5d087593a 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java @@ -25,11 +25,13 @@ import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.test.utils.QpidTestCase; public class ExternalAuthenticationManagerTest extends QpidTestCase { - private AuthenticationManager _manager = new ExternalAuthenticationManager(); + private AuthenticationManager _manager = new ExternalAuthenticationManager(false); + private AuthenticationManager _managerUsingFullDN = new ExternalAuthenticationManager(true); public void testGetMechanisms() throws Exception { @@ -38,13 +40,23 @@ public class ExternalAuthenticationManagerTest extends QpidTestCase public void testCreateSaslServer() throws Exception { - SaslServer server = _manager.createSaslServer("EXTERNAL", "example.example.com", null); + createSaslServerTestImpl(_manager); + } + + public void testCreateSaslServerUsingFullDN() throws Exception + { + createSaslServerTestImpl(_managerUsingFullDN); + } + + public void createSaslServerTestImpl(AuthenticationManager manager) throws Exception + { + SaslServer server = manager.createSaslServer("EXTERNAL", "example.example.com", null); assertEquals("Sasl Server mechanism name is not as expected", "EXTERNAL", server.getMechanismName()); try { - server = _manager.createSaslServer("PLAIN", "example.example.com", null); + server = manager.createSaslServer("PLAIN", "example.example.com", null); fail("Expected creating SaslServer with incorrect mechanism to throw an exception"); } catch (SaslException e) @@ -53,12 +65,16 @@ public class ExternalAuthenticationManagerTest extends QpidTestCase } } - public void testAuthenticate() throws Exception + /** + * Test behaviour of the authentication when the useFullDN attribute is set true + * and the username is taken directly as the externally supplied Principal + */ + public void testAuthenticateWithFullDN() throws Exception { X500Principal principal = new X500Principal("CN=person, DC=example, DC=com"); - SaslServer saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + SaslServer saslServer = _managerUsingFullDN.createSaslServer("EXTERNAL", "example.example.com", principal); - AuthenticationResult result = _manager.authenticate(saslServer, new byte[0]); + AuthenticationResult result = _managerUsingFullDN.authenticate(saslServer, new byte[0]); assertNotNull(result); assertEquals("Expected authentication to be successful", AuthenticationResult.AuthenticationStatus.SUCCESS, @@ -66,15 +82,102 @@ public class ExternalAuthenticationManagerTest extends QpidTestCase assertOnlyContainsWrapped(principal, result.getPrincipals()); + saslServer = _managerUsingFullDN.createSaslServer("EXTERNAL", "example.example.com", null); + result = _managerUsingFullDN.authenticate(saslServer, new byte[0]); + + assertNotNull(result); + assertEquals("Expected authentication to be unsuccessful", + AuthenticationResult.AuthenticationStatus.ERROR, + result.getStatus()); + } + + /** + * Test behaviour of the authentication when parsing the username from + * the Principals DN as <CN>@<DC1>.<DC2>.<DC3>....<DCN> + */ + public void testAuthenticateWithUsernameBasedOnCNAndDC() throws Exception + { + X500Principal principal; + SaslServer saslServer; + AuthenticationResult result; + UsernamePrincipal expectedPrincipal; + + // DN contains only CN + principal = new X500Principal("CN=person"); + expectedPrincipal = new UsernamePrincipal("person"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + + result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); + assertEquals("Expected authentication to be successful", + AuthenticationResult.AuthenticationStatus.SUCCESS, + result.getStatus()); + assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals()); + + // Null princial saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", null); result = _manager.authenticate(saslServer, new byte[0]); assertNotNull(result); - assertEquals("Expected authentication to be unsuccessful", - AuthenticationResult.AuthenticationStatus.ERROR, - result.getStatus()); + assertEquals("Expected authentication to be unsuccessful", + AuthenticationResult.AuthenticationStatus.ERROR, + result.getStatus()); - } + // DN doesn't contain CN + principal = new X500Principal("DC=example, DC=com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=GB"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); + assertEquals("Expected authentication to be unsuccessful", + AuthenticationResult.AuthenticationStatus.ERROR, + result.getStatus()); + + // DN contains empty CN + principal = new X500Principal("CN=, DC=example, DC=com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=GB"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + result = _manager.authenticate(saslServer, new byte[0]); + + assertNotNull(result); + assertEquals("Expected authentication to be unsuccessful", + AuthenticationResult.AuthenticationStatus.ERROR, + result.getStatus()); + + // DN contains CN and DC + principal = new X500Principal("CN=person, DC=example, DC=com"); + expectedPrincipal = new UsernamePrincipal("person@example.com"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + + result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); + assertEquals("Expected authentication to be successful", + AuthenticationResult.AuthenticationStatus.SUCCESS, + result.getStatus()); + assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals()); + + // DN contains CN and DC and other components + principal = new X500Principal("CN=person, DC=example, DC=com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=GB"); + expectedPrincipal = new UsernamePrincipal("person@example.com"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + + result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); + assertEquals("Expected authentication to be successful", + AuthenticationResult.AuthenticationStatus.SUCCESS, + result.getStatus()); + assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals()); + + // DN contains CN and DC and other components + principal = new X500Principal("CN=person, O=My Company Ltd, L=Newbury, ST=Berkshire, C=GB"); + expectedPrincipal = new UsernamePrincipal("person"); + saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", principal); + + result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); + assertEquals("Expected authentication to be successful", + AuthenticationResult.AuthenticationStatus.SUCCESS, + result.getStatus()); + assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals()); + } } |
