diff options
| author | Keith Wall <kwall@apache.org> | 2013-10-21 11:48:52 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2013-10-21 11:48:52 +0000 |
| commit | 67c066df96661830ff57ad569c0064506236726b (patch) | |
| tree | 6b4ef231809e2daf350ebc2810b44b93c79bf625 /qpid/java | |
| parent | 667df7d458ca7945f04b467a4c28bd164c2c3e0a (diff) | |
| download | qpid-python-67c066df96661830ff57ad569c0064506236726b.tar.gz | |
QPID-4463: [Java Broker] Change SimpleLDAPAuthManager to accept trust store model object in order to conveniently connect to a Directory secured by certificate signed by private-CA (or using self-signed cert).
* SimpleLDAPAuthManager can be associated with a truststore model object via the
* SSLSocketFactory classes generated on the fly (associated with the truststore) in order to work around limitations in the javax.naming API.
* In the Management UI, the user currently needs to enter the name of the truststore configured object (rather than select from a dropdown)
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1534105 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java')
24 files changed, 853 insertions, 86 deletions
diff --git a/qpid/java/broker-core/pom.xml b/qpid/java/broker-core/pom.xml index 7f7bc34aa4..a872bf565a 100644 --- a/qpid/java/broker-core/pom.xml +++ b/qpid/java/broker-core/pom.xml @@ -147,6 +147,20 @@ <scope>compile</scope> </dependency> + <dependency> + <groupId>org.apache.bcel</groupId> + <artifactId>bcel</artifactId> + <version>5.2</version> + <scope>compile</scope> + <exclusions> + <exclusion> + <!-- Qpid doesn't require BCEL InstructionFinder, so does not need jakarta-regexp. --> + <artifactId>jakarta-regexp</artifactId> + <groupId>jakarta-regexp</groupId> + </exclusion> + </exclusions> + </dependency> + <!-- test dependencies --> <dependency> <groupId>org.apache.qpid</groupId> diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java index 8958313fa2..f260b27259 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java @@ -21,10 +21,14 @@ package org.apache.qpid.server.configuration.startup; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import org.apache.qpid.server.BrokerOptions; @@ -88,7 +92,6 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> @Override public Broker create(RecovererProvider recovererProvider, ConfigurationEntry entry, ConfiguredObject... parents) { - //Map<String, Object> attributes = entry.getAttributes(); Map<String, Object> attributesCopy = validateAttributes(entry); attributesCopy.put(Broker.MODEL_VERSION, Model.MODEL_VERSION); @@ -99,29 +102,11 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> broker.addChangeListener(_storeChangeListener); - //Recover the SSL keystores / truststores first, then others that depend on them Map<String, Collection<ConfigurationEntry>> childEntries = new HashMap<String, Collection<ConfigurationEntry>>(entry.getChildren()); - Map<String, Collection<ConfigurationEntry>> priorityChildEntries = new HashMap<String, Collection<ConfigurationEntry>>(childEntries); - List<String> types = new ArrayList<String>(childEntries.keySet()); - for(String type : types) - { - if(KeyStore.class.getSimpleName().equals(type) || TrustStore.class.getSimpleName().equals(type) - || AuthenticationProvider.class.getSimpleName().equals(type)) - { - childEntries.remove(type); - } - else - { - priorityChildEntries.remove(type); - } - } + List<String> types = makePrioritisedListOfTypes(childEntries.keySet(), TrustStore.class.getSimpleName(), KeyStore.class.getSimpleName(), AuthenticationProvider.class.getSimpleName()); - for (String type : priorityChildEntries.keySet()) - { - recoverType(recovererProvider, _storeChangeListener, broker, priorityChildEntries, type); - } - for (String type : childEntries.keySet()) + for (String type : types) { recoverType(recovererProvider, _storeChangeListener, broker, childEntries, type); } @@ -129,6 +114,24 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> return broker; } + private List<String> makePrioritisedListOfTypes(Set<String> allTypes, String... priorityOrderedTypes) + { + List<String> prioritisedList = new ArrayList<String>(allTypes.size()); + Set<String> remainder = new HashSet<String>(allTypes); + + for (String type : priorityOrderedTypes) + { + Set<String> singleton = Collections.singleton(type); + Set<String> intersection = new HashSet<String>(allTypes); + intersection.retainAll(singleton); + remainder.removeAll(singleton); + prioritisedList.addAll(intersection); + } + + prioritisedList.addAll(remainder); + return prioritisedList; + } + private Map<String, Object> validateAttributes(ConfigurationEntry entry) { Map<String, Object> attributes = entry.getAttributes(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java index ee2cd6a5fc..656bdc9acf 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java @@ -379,7 +379,7 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana { throw new IllegalConfigurationException("Cannot find authentication provider factory for type " + newType); } - AuthenticationManager manager = managerFactory.createInstance(attributes); + AuthenticationManager manager = managerFactory.createInstance(_broker, attributes); if (manager == null) { throw new IllegalConfigurationException("Cannot change authentication provider " + newName + " of type " + newType + " with the given attributes"); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactory.java index eb16d3bbfb..322c8a0ea4 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactory.java @@ -80,7 +80,7 @@ public class AuthenticationProviderFactory { for (AuthenticationManagerFactory factory : _factories) { - AuthenticationManager manager = factory.createInstance(attributes); + AuthenticationManager manager = factory.createInstance(broker, attributes); if (manager != null) { AuthenticationProviderAdapter<?> authenticationProvider; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/AuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/AuthenticationManagerFactory.java index e183370870..4805f06760 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/AuthenticationManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/AuthenticationManagerFactory.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; public interface AuthenticationManagerFactory extends Pluggable @@ -36,15 +37,17 @@ public interface AuthenticationManagerFactory extends Pluggable /** * Creates authentication manager from the provided attributes - * + * @param broker + * broker model object * @param attributes * attributes to create authentication manager + * * @return authentication manager instance */ - AuthenticationManager createInstance(Map<String, Object> attributes); + AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes); /** - * Get the names of attributes the authentication manager which can be passed into {@link #createInstance(Map)} to create the + * Get the names of attributes the authentication manager which can be passed into {@link #createInstance(Broker, Map)} to create the * authentication manager * * @return the collection of attribute names diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractPrincipalDatabaseAuthManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractPrincipalDatabaseAuthManagerFactory.java index 5d427c4afb..86d012cc96 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractPrincipalDatabaseAuthManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractPrincipalDatabaseAuthManagerFactory.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Map; import org.apache.log4j.Logger; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; @@ -46,7 +47,7 @@ public abstract class AbstractPrincipalDatabaseAuthManagerFactory implements Aut @Override - public AuthenticationManager createInstance(Map<String, Object> attributes) + public AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes) { if (attributes == null || !getType().equals(attributes.get(ATTRIBUTE_TYPE))) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerFactory.java index 91aba71720..2160a082f4 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerFactory.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; public class AnonymousAuthenticationManagerFactory implements AuthenticationManagerFactory @@ -30,7 +31,7 @@ public class AnonymousAuthenticationManagerFactory implements AuthenticationMana public static final String PROVIDER_TYPE = "Anonymous"; @Override - public AuthenticationManager createInstance(Map<String, Object> attributes) + public AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes) { if (attributes != null && PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java index 6029674cd3..3f8449c529 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerFactory.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.util.ResourceBundleLoader; @@ -38,7 +39,7 @@ public class ExternalAuthenticationManagerFactory implements AuthenticationManag ATTRIBUTE_USE_FULL_DN)); @Override - public AuthenticationManager createInstance(Map<String, Object> attributes) + public AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes) { if (attributes != null && PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerFactory.java index a2d5bd4c8e..8b86a783a9 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerFactory.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; public class KerberosAuthenticationManagerFactory implements AuthenticationManagerFactory @@ -30,7 +31,7 @@ public class KerberosAuthenticationManagerFactory implements AuthenticationManag public static final String PROVIDER_TYPE = "Kerberos"; @Override - public AuthenticationManager createInstance(Map<String, Object> attributes) + public AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes) { if (attributes != null && PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java index 0db0d388d6..0fb8579eff 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java @@ -27,10 +27,11 @@ import javax.naming.AuthenticationException; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; -import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; @@ -38,35 +39,63 @@ import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; + import org.apache.log4j.Logger; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.manager.ldap.AbstractLDAPSSLSocketFactory; +import org.apache.qpid.server.security.auth.manager.ldap.LDAPSSLSocketFactoryGenerator; import org.apache.qpid.server.security.auth.sasl.plain.PlainPasswordCallback; import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; +import org.apache.qpid.server.util.StringUtil; +import org.apache.qpid.ssl.SSLContextFactory; public class SimpleLDAPAuthenticationManager implements AuthenticationManager { private static final Logger _logger = Logger.getLogger(SimpleLDAPAuthenticationManager.class); + /** + * Environment key to instruct {@link InitialDirContext} to override the socket factory. + */ + private static final String JAVA_NAMING_LDAP_FACTORY_SOCKET = "java.naming.ldap.factory.socket"; + + private final String _authManagerName; private final String _providerSearchURL; private final String _providerAuthURL; private final String _searchContext; private final String _searchFilter; private final String _ldapContextFactory; - SimpleLDAPAuthenticationManager(String providerSearchUrl, String providerAuthUrl, String searchContext, String searchFilter, String ldapContextFactory) + /** + * Trust store - typically used when the Directory has been secured with a certificate signed by a + * private CA (or self-signed certificate). + */ + private final TrustStore _trustStore; + + /** + * Dynamically created SSL Socket Factory implementation used in the case where user has specified a trust store. + */ + private Class<? extends SocketFactory> _sslSocketFactoryOverride; + + + SimpleLDAPAuthenticationManager(String authManagerName, String providerSearchUrl, String providerAuthUrl, String searchContext, String searchFilter, String ldapContextFactory, TrustStore trustStore) { + _authManagerName = authManagerName; _providerSearchURL = providerSearchUrl; _providerAuthURL = providerAuthUrl; _searchContext = searchContext; _searchFilter = searchFilter; _ldapContextFactory = ldapContextFactory; + _trustStore = trustStore; } @Override public void initialise() { + _sslSocketFactoryOverride = createSslSocketFactoryOverride(); + validateInitialDirContext(); } @@ -145,19 +174,16 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager return new AuthenticationResult(AuthenticationStatus.CONTINUE); } - Hashtable<Object,Object> env = new Hashtable<Object,Object>(); - env.put(Context.INITIAL_CONTEXT_FACTORY, _ldapContextFactory); - env.put(Context.PROVIDER_URL, _providerAuthURL); + Hashtable<String, Object> env = createInitialDirContentEnvironment(_providerAuthURL); env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, name); env.put(Context.SECURITY_CREDENTIALS, password); - DirContext ctx = null; + InitialDirContext ctx = null; try { - ctx = new InitialDirContext(env); + ctx = createInitialDirContext(env); //Authentication succeeded return new AuthenticationResult(new UsernamePrincipal(name)); @@ -176,14 +202,7 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager { if(ctx != null) { - try - { - ctx.close(); - } - catch (Exception e) - { - _logger.warn("Exception closing InitialDirContext", e); - } + closeSafely(ctx); } } } @@ -193,26 +212,94 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager { } - private void validateInitialDirContext() + private Hashtable<String, Object> createInitialDirContentEnvironment(String providerUrl) { - Hashtable<String,Object> env = new Hashtable<String, Object>(); + Hashtable<String,Object> env = new Hashtable<String,Object>(); env.put(Context.INITIAL_CONTEXT_FACTORY, _ldapContextFactory); - env.put(Context.PROVIDER_URL, _providerSearchURL); + env.put(Context.PROVIDER_URL, providerUrl); + return env; + } + + private InitialDirContext createInitialDirContext(Hashtable<String, Object> env) throws NamingException + { + ClassLoader existingContextClassloader = null; + + boolean isLdaps = ((String)env.get(Context.PROVIDER_URL)).startsWith("ldaps:"); + + boolean revertContentClassLoader = false; + try + { + if (isLdaps && _sslSocketFactoryOverride != null) + { + existingContextClassloader = Thread.currentThread().getContextClassLoader(); + env.put(JAVA_NAMING_LDAP_FACTORY_SOCKET, _sslSocketFactoryOverride.getName()); + Thread.currentThread().setContextClassLoader(_sslSocketFactoryOverride.getClassLoader()); + revertContentClassLoader = true; + } + return new InitialDirContext(env); + } + finally + { + if (revertContentClassLoader) + { + Thread.currentThread().setContextClassLoader(existingContextClassloader); + } + } + } + + /** + * If a trust store has been specified, create a {@link SSLContextFactory} class that is + * associated with the {@link SSLContext} generated from that trust store. + * + * @return generated socket factory class + */ + private Class<? extends SocketFactory> createSslSocketFactoryOverride() + { + if (_trustStore != null) + { + String clazzName = new StringUtil().createUniqueJavaName(_authManagerName); + SSLContext sslContext = null; + try + { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, _trustStore.getTrustManagers(), null); + } + catch (Exception e) + { + _logger.error("Exception creating SSLContext", e); + throw new RuntimeException(e); + } + Class<? extends AbstractLDAPSSLSocketFactory> clazz = LDAPSSLSocketFactoryGenerator.createSubClass(clazzName, sslContext.getSocketFactory()); + _logger.debug("Connection to Directory will use custom SSL socket factory : " + clazz); + return clazz; + } + + return null; + } + + private void validateInitialDirContext() + { + Hashtable<String,Object> env = createInitialDirContentEnvironment(_providerSearchURL); env.put(Context.SECURITY_AUTHENTICATION, "none"); + InitialDirContext ctx = null; try { - new InitialDirContext(env).close(); + ctx = createInitialDirContext(env); } catch (NamingException e) { throw new RuntimeException("Unable to establish anonymous connection to the ldap server at " + _providerSearchURL, e); } + finally + { + closeSafely(ctx); + } } + private class SimpleLDAPPlainCallbackHandler implements CallbackHandler { - @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { @@ -263,14 +350,10 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager private String getNameFromId(String id) throws NamingException { - Hashtable<Object,Object> env = new Hashtable<Object,Object>(); - env.put(Context.INITIAL_CONTEXT_FACTORY, _ldapContextFactory); - env.put(Context.PROVIDER_URL, _providerSearchURL); + Hashtable<String,Object> env = createInitialDirContentEnvironment(_providerSearchURL); env.put(Context.SECURITY_AUTHENTICATION, "none"); - DirContext ctx = null; - - ctx = new InitialDirContext(env); + InitialDirContext ctx = createInitialDirContext(env); try { @@ -291,18 +374,23 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager } finally { - try - { - ctx.close(); - } - catch (Exception e) - { - _logger.warn("Exception closing InitialDirContext", e); - } + closeSafely(ctx); } } + private void closeSafely(InitialDirContext ctx) + { + try + { + ctx.close(); + } + catch (Exception e) + { + _logger.warn("Exception closing InitialDirContext", e); + } + } + @Override public void onCreate() { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactory.java index 55e90178f8..767e675125 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactory.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactory.java @@ -24,6 +24,9 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.util.ResourceBundleLoader; @@ -34,9 +37,11 @@ public class SimpleLDAPAuthenticationManagerFactory implements AuthenticationMan public static final String PROVIDER_TYPE = "SimpleLDAP"; + public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_LDAP_CONTEXT_FACTORY = "ldapContextFactory"; public static final String ATTRIBUTE_SEARCH_FILTER = "searchFilter"; public static final String ATTRIBUTE_SEARCH_CONTEXT = "searchContext"; + public static final String ATTRIBUTE_TRUST_STORE = "trustStore"; public static final String ATTRIBUTE_PROVIDER_AUTH_URL = "providerAuthUrl"; public static final String ATTRIBUTE_PROVIDER_URL = "providerUrl"; @@ -45,19 +50,23 @@ public class SimpleLDAPAuthenticationManagerFactory implements AuthenticationMan ATTRIBUTE_PROVIDER_URL, ATTRIBUTE_SEARCH_CONTEXT, ATTRIBUTE_SEARCH_FILTER, + ATTRIBUTE_TRUST_STORE, ATTRIBUTE_PROVIDER_AUTH_URL, ATTRIBUTE_LDAP_CONTEXT_FACTORY )); @Override - public AuthenticationManager createInstance(Map<String, Object> attributes) + public AuthenticationManager createInstance(Broker broker, Map<String, Object> attributes) { if (attributes == null || !PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { return null; } + + String name = (String) attributes.get(ATTRIBUTE_NAME); String providerUrl = (String) attributes.get(ATTRIBUTE_PROVIDER_URL); String providerAuthUrl = (String) attributes.get(ATTRIBUTE_PROVIDER_AUTH_URL); + if (providerAuthUrl == null) { providerAuthUrl = providerUrl; @@ -65,13 +74,24 @@ public class SimpleLDAPAuthenticationManagerFactory implements AuthenticationMan String searchContext = (String) attributes.get(ATTRIBUTE_SEARCH_CONTEXT); String searchFilter = (String) attributes.get(ATTRIBUTE_SEARCH_FILTER); String ldapContextFactory = (String) attributes.get(ATTRIBUTE_LDAP_CONTEXT_FACTORY); + String trustStoreName = (String) attributes.get(ATTRIBUTE_TRUST_STORE); if (ldapContextFactory == null) { ldapContextFactory = DEFAULT_LDAP_CONTEXT_FACTORY; } - return new SimpleLDAPAuthenticationManager(providerUrl, providerAuthUrl, searchContext, searchFilter, - ldapContextFactory); + TrustStore trustStore = null; + if (trustStoreName != null) + { + trustStore = broker.findTrustStoreByName(trustStoreName); + if (trustStore == null) + { + throw new IllegalConfigurationException("Can't find truststore with name '" + trustStoreName + "'"); + } + } + + return new SimpleLDAPAuthenticationManager(name, providerUrl, providerAuthUrl, searchContext, + searchFilter, ldapContextFactory, trustStore); } @Override diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationProviderAttributeDescriptions.properties b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationProviderAttributeDescriptions.properties index 5439c7fff5..18e6fb8a4c 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationProviderAttributeDescriptions.properties +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationProviderAttributeDescriptions.properties @@ -20,4 +20,5 @@ ldapContextFactory= LDAP context factory searchFilter=Search filter* searchContext=Search context* providerAuthUrl=LDAP authentication URL -providerUrl=LDAP server URL*
\ No newline at end of file +providerUrl=LDAP server URL* +trustStore=Truststore name diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/AbstractLDAPSSLSocketFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/AbstractLDAPSSLSocketFactory.java new file mode 100644 index 0000000000..3e8b2e210e --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/AbstractLDAPSSLSocketFactory.java @@ -0,0 +1,117 @@ +/* + * + * 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.qpid.server.security.auth.manager.ldap; + +import static org.apache.qpid.server.security.auth.manager.ldap.LDAPSSLSocketFactoryGenerator.getStaticFieldByReflection; +import static org.apache.qpid.server.security.auth.manager.ldap.LDAPSSLSocketFactoryGenerator.SSL_SOCKET_FACTORY_FIELD; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLSocketFactory; + +/** + * Abstract base class for all LDAPSSLSocketFactory implementations. + * <p> + * Concrete implementations of this class are <b>generated dynamically</b> at runtime by + * the {@link LDAPSSLSocketFactoryGenerator#createSubClass(String, SSLSocketFactory)} method. + * </p> + * <p> + * Callers will create new instances of the concrete implementations by using the static + * <code>#getDefault()</code> method. This will return an instance of the sub-class that is + * associated with the {@link SSLSocketFactory}. + * </p> + * <p> + * If callers are passing the sub-class to an API via class-name (i.e. String), the caller + * <b>must</b> ensure that the context classloader of the thread to set to the classloader + * of sub-class for the duration of the API call(s). + * </p> + * For more details see {@link LDAPSSLSocketFactoryGenerator}. + * </p> + */ +public abstract class AbstractLDAPSSLSocketFactory extends SSLSocketFactory +{ + /** Socket factory to which this factory will delegate */ + private final SSLSocketFactory _delegate; + + protected AbstractLDAPSSLSocketFactory() + { + super(); + _delegate = getStaticFieldByReflection(getClass(), SSL_SOCKET_FACTORY_FIELD); + if (_delegate == null) + { + throw new IllegalStateException("Delegate cannot be null - static field initialisation problem?"); + } + } + + @Override + public String[] getDefaultCipherSuites() + { + return _delegate.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() + { + return _delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException + { + return _delegate.createSocket(); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException + { + return _delegate.createSocket(host, port); + } + + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException + { + return _delegate.createSocket(socket, host, port, autoClose); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException + { + return _delegate.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException + { + return _delegate.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException + { + return _delegate.createSocket(address, port, localAddress, localPort); + } + + +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGenerator.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGenerator.java new file mode 100644 index 0000000000..5cfc8b7a0b --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGenerator.java @@ -0,0 +1,298 @@ +/* + * + * 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.qpid.server.security.auth.manager.ldap; + +import static org.apache.bcel.Constants.ACC_PRIVATE; +import static org.apache.bcel.Constants.ACC_PUBLIC; +import static org.apache.bcel.Constants.ACC_STATIC; +import static org.apache.bcel.Constants.ACC_SUPER; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import org.apache.bcel.Constants; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.generic.ClassGen; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.FieldGen; +import org.apache.bcel.generic.InstructionConstants; +import org.apache.bcel.generic.InstructionFactory; +import org.apache.bcel.generic.InstructionList; +import org.apache.bcel.generic.MethodGen; +import org.apache.bcel.generic.Type; + +/** + * This class provides a single method, {@link #createSubClass(String, SSLSocketFactory)}. This creates a + * sub-class of {@link AbstractLDAPSSLSocketFactory} and associates it with the {@link SSLSocketFactory} instance.. + * <p> + * The sub-classes are <b>generated dynamically</b>. + * </p> + * <p>This approach is required in order to overcome a limitation in the javax.naming.directory API. It offers + * {@link SSLSocketFactory} customization only at the class level only (via the <code>java.naming.ldap.factory.socket</code> + * directory context environment parameter). For this reason, a mechanism that can produce distinct + * {@link AbstractLDAPSSLSocketFactory} classes each associated with a different SSLSocketFactory instance is required. + * </p> + * @see <a href="http://docs.oracle.com/javase/jndi/tutorial/ldap/security/ssl.html">Java LDAP SSL and Custom Sockets</a> + */ +public class LDAPSSLSocketFactoryGenerator +{ + /** + * The name of field used to hold the delegate {@link SSLSocketFactory}. A field with + * this name is created on each generated sub-class. + */ + static final String SSL_SOCKET_FACTORY_FIELD = "_sslSocketFactory"; + + /** Target package names used for the subclass - needs to exist */ + static final String TARGET_PACKAGE_NAME = LDAPSSLSocketFactoryGenerator.class.getPackage().getName(); + + public static Class<? extends AbstractLDAPSSLSocketFactory> createSubClass(String simpleName, final SSLSocketFactory sslSocketFactory) + { + final String fqcn = TARGET_PACKAGE_NAME + "." + simpleName; + final byte[] classBytes = createSubClassByteCode(fqcn); + + try + { + final ClassLoader classLoader = new LDAPSSLSocketFactoryAwareDelegatingClassloader(fqcn, classBytes, sslSocketFactory); + Class<? extends AbstractLDAPSSLSocketFactory> clazz = (Class<? extends AbstractLDAPSSLSocketFactory>) classLoader.loadClass(fqcn); + return clazz; + } + catch (ClassNotFoundException cnfe) + { + throw new IllegalArgumentException("Could not resolve dynamically generated class " + fqcn, cnfe); + } + } + + /** + * Creates the LDAPSocketFactoryImpl class (subclass of {@link AbstractLDAPSSLSocketFactory}. + * A static method #getDefaulta, a static field _sslContent and no-arg constructor are added + * to the class. + * + * @param className + * + * @return byte code + */ + private static byte[] createSubClassByteCode(final String className) + { + ClassGen classGen = new ClassGen(className, + AbstractLDAPSSLSocketFactory.class.getName(), + "<generated>", + ACC_PUBLIC | ACC_SUPER, + null); + ConstantPoolGen constantPoolGen = classGen.getConstantPool(); + InstructionFactory factory = new InstructionFactory(classGen); + + createSslContextStaticField(classGen, constantPoolGen); + createGetDefaultStaticMethod(classGen, constantPoolGen, factory); + + classGen.addEmptyConstructor(Constants.ACC_PROTECTED); + + JavaClass javaClass = classGen.getJavaClass(); + ByteArrayOutputStream out = null; + try + { + out = new ByteArrayOutputStream(); + javaClass.dump(out); + return out.toByteArray(); + } + catch (IOException ioex) + { + throw new IllegalStateException("Could not write to a ByteArrayOutputStream - should not happen", ioex); + } + finally + { + closeSafely(out); + } + } + + /** + * Creates a static field _sslContext of type {@link SSLSocketFactory}. + * + * @param classGen + * @param constantPoolGen + */ + private static void createSslContextStaticField(ClassGen classGen, ConstantPoolGen constantPoolGen) + { + FieldGen fieldGen = new FieldGen(ACC_PRIVATE | ACC_STATIC, + Type.getType(SSLSocketFactory.class), + SSL_SOCKET_FACTORY_FIELD, + constantPoolGen); + classGen.addField(fieldGen.getField()); + } + + /** + * Create a static method 'getDefault' returning {@link SocketFactory} + * that creates a new instance of the sub-class and calls its no-argument + * constructor, the newly created is returned to the caller. + * + * @param classGen + * @param constantPoolGen + * @param instructionFactory + */ + private static void createGetDefaultStaticMethod(ClassGen classGen, + ConstantPoolGen constantPoolGen, InstructionFactory instructionFactory) + { + InstructionList il = new InstructionList(); + + String methodName = "getDefault"; + MethodGen mg = new MethodGen(ACC_STATIC | ACC_PUBLIC, // access flags + Type.getType(SSLSocketFactory.class), // return type + new Type[0], // argument types - no args + new String[0], // arg names - no args + methodName, + classGen.getClassName(), // method, class + il, + constantPoolGen); + + il.append(instructionFactory.createNew(classGen.getClassName())); + il.append(InstructionConstants.DUP); + + il.append(instructionFactory.createInvoke(classGen.getClassName(), "<init>", Type.VOID, + new Type[] {}, + Constants.INVOKESPECIAL)); + + il.append(InstructionConstants.ARETURN); + + mg.setMaxStack(); + classGen.addMethod(mg.getMethod()); + il.dispose(); + } + + private static void closeSafely(ByteArrayOutputStream out) + { + if (out != null) + { + try + { + out.close(); + } + catch (IOException e) + { + // Ignore + } + } + } + + private static void setSslSocketFactoryFieldByReflection(Class<? extends AbstractLDAPSSLSocketFactory> clazz, String fieldName, SSLSocketFactory sslSocketFactory) + { + String exceptionMessage = "Unexpected error setting generated static field " + + fieldName + "on generated class " + clazz.getName(); + try + { + Field declaredField = clazz.getDeclaredField(fieldName); + boolean accessible = declaredField.isAccessible(); + try + { + declaredField.setAccessible(true); + declaredField.set(null, sslSocketFactory); + } + finally + { + declaredField.setAccessible(accessible); + } + } + catch (IllegalArgumentException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (IllegalAccessException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (NoSuchFieldException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (SecurityException e) + { + throw new RuntimeException(exceptionMessage, e); + } + } + + static SSLSocketFactory getStaticFieldByReflection(Class<? extends AbstractLDAPSSLSocketFactory> clazz, String fieldName) + { + String exceptionMessage = "Unexpected error getting generated static field " + + fieldName + "on generated class " + clazz.getName(); + + Field declaredField; + try + { + declaredField = clazz.getDeclaredField(fieldName); + boolean accessible = declaredField.isAccessible(); + try + { + declaredField.setAccessible(true); + return (SSLSocketFactory) declaredField.get(null); + } + finally + { + declaredField.setAccessible(accessible); + } + } + catch (NoSuchFieldException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (SecurityException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (IllegalArgumentException e) + { + throw new RuntimeException(exceptionMessage, e); + } + catch (IllegalAccessException e) + { + throw new RuntimeException(exceptionMessage, e); + } + } + + private static final class LDAPSSLSocketFactoryAwareDelegatingClassloader extends ClassLoader + { + private final String _className; + private final Class<? extends AbstractLDAPSSLSocketFactory> _clazz; + + private LDAPSSLSocketFactoryAwareDelegatingClassloader(String className, + byte[] classBytes, SSLSocketFactory sslSocketFactory) + { + super(LDAPSSLSocketFactoryGenerator.class.getClassLoader()); + _className = className; + _clazz = (Class<? extends AbstractLDAPSSLSocketFactory>) defineClass(className, classBytes, 0, classBytes.length); + setSslSocketFactoryFieldByReflection(_clazz, SSL_SOCKET_FACTORY_FIELD, sslSocketFactory); + } + + @Override + protected Class<?> findClass(String fqcn) throws ClassNotFoundException + { + if (fqcn.equals(_className)) + { + return _clazz; + } + else + { + return getParent().loadClass(fqcn); + } + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java index aa17a9493b..97e3573fff 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java @@ -22,6 +22,8 @@ package org.apache.qpid.server.util; import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; + public class StringUtil { private static final String NUMBERS = "0123456789"; @@ -41,4 +43,33 @@ public class StringUtil return new String(result); } + /** + * Builds a legal java name, based on manager name if possible, + * this is unique for the given input. + * + * @param managerName + * @return unique java name + */ + public String createUniqueJavaName(String managerName) + { + StringBuilder builder = new StringBuilder(); + boolean initialChar = true; + for (int i = 0; i < managerName.length(); i++) + { + char c = managerName.charAt(i); + if ((initialChar && Character.isJavaIdentifierStart(c)) + || (!initialChar && Character.isJavaIdentifierPart(c))) + { + builder.append(c); + initialChar = false; + } + } + if (builder.length() > 0) + { + builder.append("_"); + } + builder.append(DigestUtils.md5Hex(managerName)); + return builder.toString(); + } + } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactoryTest.java index fbb1e4105f..59eaf96ec3 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactoryTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/AuthenticationProviderFactoryTest.java @@ -93,7 +93,7 @@ public class AuthenticationProviderFactoryTest extends TestCase when(authManagerFactoryServiceLoader.atLeastOneInstanceOf(AuthenticationManagerFactory.class)).thenReturn( Collections.singleton(authenticationManagerFactory)); - when(authenticationManagerFactory.createInstance(attributes)).thenReturn(authenticationManager); + when(authenticationManagerFactory.createInstance(broker, attributes)).thenReturn(authenticationManager); AuthenticationProviderFactory providerFactory = new AuthenticationProviderFactory(authManagerFactoryServiceLoader); @@ -121,7 +121,7 @@ public class AuthenticationProviderFactoryTest extends TestCase QpidServiceLoader<AuthenticationManagerFactory> loader = mock(QpidServiceLoader.class); AuthenticationManagerFactory managerFactory = mock(AuthenticationManagerFactory.class); - when(managerFactory.createInstance(any(Map.class))).thenReturn(mock(PrincipalDatabaseAuthenticationManager.class)); + when(managerFactory.createInstance(any(Broker.class), any(Map.class))).thenReturn(mock(PrincipalDatabaseAuthenticationManager.class)); when(loader.atLeastOneInstanceOf(AuthenticationManagerFactory.class)).thenReturn(Collections.singleton(managerFactory)); AuthenticationProviderFactory providerFactory = new AuthenticationProviderFactory(loader); @@ -142,7 +142,7 @@ public class AuthenticationProviderFactoryTest extends TestCase QpidServiceLoader<AuthenticationManagerFactory> loader = mock(QpidServiceLoader.class); AuthenticationManagerFactory managerFactory = mock(AuthenticationManagerFactory.class); - when(managerFactory.createInstance(any(Map.class))).thenReturn(mock(AuthenticationManager.class)); + when(managerFactory.createInstance(any(Broker.class), any(Map.class))).thenReturn(mock(AuthenticationManager.class)); when(loader.atLeastOneInstanceOf(AuthenticationManagerFactory.class)).thenReturn(Collections.singleton(managerFactory)); AuthenticationProviderFactory providerFactory = new AuthenticationProviderFactory(loader); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/Base64MD5PasswordFileAuthenticationManagerFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/Base64MD5PasswordFileAuthenticationManagerFactoryTest.java index 04e09e073f..b3d94a5043 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/Base64MD5PasswordFileAuthenticationManagerFactoryTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/Base64MD5PasswordFileAuthenticationManagerFactoryTest.java @@ -19,6 +19,8 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.mockito.Mockito.mock; + import java.io.File; import java.io.FileNotFoundException; import java.util.HashMap; @@ -26,6 +28,7 @@ import java.util.Map; import junit.framework.TestCase; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; @@ -34,6 +37,7 @@ public class Base64MD5PasswordFileAuthenticationManagerFactoryTest extends Test AuthenticationManagerFactory _factory = new Base64MD5PasswordFileAuthenticationManagerFactory(); private Map<String, Object> _configuration = new HashMap<String, Object>(); private File _emptyPasswordFile; + private Broker _broker = mock(Broker.class); @Override protected void setUp() throws Exception @@ -48,7 +52,7 @@ public class Base64MD5PasswordFileAuthenticationManagerFactoryTest extends Test _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, Base64MD5PasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _emptyPasswordFile.getAbsolutePath()); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNotNull(manager); assertTrue(manager instanceof PrincipalDatabaseAuthenticationManager); assertTrue(((PrincipalDatabaseAuthenticationManager)manager).getPrincipalDatabase() instanceof Base64MD5PasswordFilePrincipalDatabase); @@ -64,7 +68,7 @@ public class Base64MD5PasswordFileAuthenticationManagerFactoryTest extends Test try { - _factory.createInstance(_configuration); + _factory.createInstance(_broker, _configuration); } catch (RuntimeException re) { @@ -74,14 +78,14 @@ public class Base64MD5PasswordFileAuthenticationManagerFactoryTest extends Test public void testReturnsNullWhenNoConfig() throws Exception { - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } public void testReturnsNullWhenConfigForOtherAuthManagerType() throws Exception { _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, "other-auth-manager"); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } @@ -89,7 +93,7 @@ public class Base64MD5PasswordFileAuthenticationManagerFactoryTest extends Test { _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, Base64MD5PasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/PlainPasswordFileAuthenticationManagerFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/PlainPasswordFileAuthenticationManagerFactoryTest.java index cc11a94db8..9ff8d18238 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/PlainPasswordFileAuthenticationManagerFactoryTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/PlainPasswordFileAuthenticationManagerFactoryTest.java @@ -19,12 +19,15 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.mockito.Mockito.mock; + import java.io.File; import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; @@ -33,6 +36,7 @@ public class PlainPasswordFileAuthenticationManagerFactoryTest extends TestCase AuthenticationManagerFactory _factory = new PlainPasswordFileAuthenticationManagerFactory(); private Map<String, Object> _configuration = new HashMap<String, Object>(); private File _emptyPasswordFile; + private Broker _broker = mock(Broker.class); @Override protected void setUp() throws Exception @@ -47,7 +51,7 @@ public class PlainPasswordFileAuthenticationManagerFactoryTest extends TestCase _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _emptyPasswordFile.getAbsolutePath()); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNotNull(manager); assertTrue(manager instanceof PrincipalDatabaseAuthenticationManager); assertTrue(((PrincipalDatabaseAuthenticationManager)manager).getPrincipalDatabase() instanceof PlainPasswordFilePrincipalDatabase); @@ -61,7 +65,7 @@ public class PlainPasswordFileAuthenticationManagerFactoryTest extends TestCase _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _emptyPasswordFile.getAbsolutePath()); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNotNull(manager); assertTrue(manager instanceof PrincipalDatabaseAuthenticationManager); @@ -70,14 +74,14 @@ public class PlainPasswordFileAuthenticationManagerFactoryTest extends TestCase public void testReturnsNullWhenNoConfig() throws Exception { - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } public void testReturnsNullWhenConfigForOtherAuthManagerType() throws Exception { _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, "other-auth-manager"); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } @@ -85,7 +89,7 @@ public class PlainPasswordFileAuthenticationManagerFactoryTest extends TestCase { _configuration.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactoryTest.java index 1424bee611..cadc5d3a3d 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactoryTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerFactoryTest.java @@ -19,9 +19,18 @@ */ package org.apache.qpid.server.security.auth.manager; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verifyZeroInteractions; + import java.util.HashMap; import java.util.Map; +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.TrustStore; + import junit.framework.TestCase; @@ -29,20 +38,69 @@ public class SimpleLDAPAuthenticationManagerFactoryTest extends TestCase { private SimpleLDAPAuthenticationManagerFactory _factory = new SimpleLDAPAuthenticationManagerFactory(); private Map<String, Object> _configuration = new HashMap<String, Object>(); + private Broker _broker = mock(Broker.class); + private TrustStore _trustStore = mock(TrustStore.class); - public void testInstanceCreated() throws Exception + public void testLdapInstanceCreated() throws Exception + { + _configuration.put(SimpleLDAPAuthenticationManagerFactory.ATTRIBUTE_TYPE, SimpleLDAPAuthenticationManagerFactory.PROVIDER_TYPE); + _configuration.put("providerUrl", "ldap://example.com:389/"); + _configuration.put("searchContext", "dc=example"); + + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); + assertNotNull(manager); + + verifyZeroInteractions(_broker); + } + + public void testLdapsInstanceCreated() throws Exception { _configuration.put(SimpleLDAPAuthenticationManagerFactory.ATTRIBUTE_TYPE, SimpleLDAPAuthenticationManagerFactory.PROVIDER_TYPE); _configuration.put("providerUrl", "ldaps://example.com:636/"); _configuration.put("searchContext", "dc=example"); - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNotNull(manager); + + verifyZeroInteractions(_broker); + } + + public void testLdapsWithTrustStoreInstanceCreated() throws Exception + { + when(_broker.findTrustStoreByName("mytruststore")).thenReturn(_trustStore); + + _configuration.put(SimpleLDAPAuthenticationManagerFactory.ATTRIBUTE_TYPE, SimpleLDAPAuthenticationManagerFactory.PROVIDER_TYPE); + _configuration.put("providerUrl", "ldaps://example.com:636/"); + _configuration.put("searchContext", "dc=example"); + _configuration.put("trustStore", "mytruststore"); + + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); + assertNotNull(manager); + } + + public void testLdapsWhenTrustStoreNotFound() throws Exception + { + when(_broker.findTrustStoreByName("notfound")).thenReturn(null); + + _configuration.put(SimpleLDAPAuthenticationManagerFactory.ATTRIBUTE_TYPE, SimpleLDAPAuthenticationManagerFactory.PROVIDER_TYPE); + _configuration.put("providerUrl", "ldaps://example.com:636/"); + _configuration.put("searchContext", "dc=example"); + _configuration.put("trustStore", "notfound"); + + try + { + _factory.createInstance(_broker, _configuration); + fail("Exception not thrown"); + } + catch(IllegalConfigurationException e) + { + assertEquals("Can't find truststore with name 'notfound'", e.getMessage()); + } } public void testReturnsNullWhenNoConfig() throws Exception { - AuthenticationManager manager = _factory.createInstance(_configuration); + AuthenticationManager manager = _factory.createInstance(_broker, _configuration); assertNull(manager); } } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGeneratorTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGeneratorTest.java new file mode 100644 index 0000000000..fccffc0163 --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ldap/LDAPSSLSocketFactoryGeneratorTest.java @@ -0,0 +1,88 @@ +/* + * + * 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.qpid.server.security.auth.manager.ldap; + +import static org.apache.qpid.server.security.auth.manager.ldap.LDAPSSLSocketFactoryGenerator.TARGET_PACKAGE_NAME; + +import static org.mockito.Mockito.mock; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import junit.framework.TestCase; + +public class LDAPSSLSocketFactoryGeneratorTest extends TestCase +{ + private SSLSocketFactory _sslSocketFactory = mock(SSLSocketFactory.class); + + public void testPackageAndClassName() throws Exception + { + Class<? extends SocketFactory> socketFactoryClass = LDAPSSLSocketFactoryGenerator.createSubClass("MyNewClass", _sslSocketFactory); + assertEquals("MyNewClass", socketFactoryClass.getSimpleName()); + assertEquals(TARGET_PACKAGE_NAME, socketFactoryClass.getPackage().getName()); + } + + public void testLoadingWithClassForName() throws Exception + { + Class<? extends AbstractLDAPSSLSocketFactory> socketFactoryClass = LDAPSSLSocketFactoryGenerator.createSubClass("MyNewClass", _sslSocketFactory); + String fqcn = socketFactoryClass.getName(); + + try + { + Class.forName(fqcn); + fail("Class loading by name should not have been successful"); + } + catch (ClassNotFoundException cnfe) + { + // PASS + } + + final ClassLoader sfClassloader = socketFactoryClass.getClassLoader(); + // Note: Oracle's com.sun.jndi.ldap.LdapClient uses the following form passing the context loader + Class<?> loaded = Class.forName(fqcn, true, sfClassloader); + assertEquals(socketFactoryClass, loaded); + } + + public void testClassloaderDelegatesToParent() throws Exception + { + ClassLoader classLoader = LDAPSSLSocketFactoryGenerator.createSubClass("MyNewClass", _sslSocketFactory).getClassLoader(); + assertEquals(String.class, classLoader.loadClass("java.lang.String")); + assertEquals(TestClassForLoading.class, classLoader.loadClass(TestClassForLoading.class.getName())); + } + + public void testGetDefaultCreatesInstance() throws Exception + { + Class<? extends AbstractLDAPSSLSocketFactory> socketFactoryClass = LDAPSSLSocketFactoryGenerator.createSubClass("MyNewClass", _sslSocketFactory); + + AbstractLDAPSSLSocketFactory socketFactory = invokeGetDefaultMethod(socketFactoryClass); + assertTrue(socketFactory instanceof AbstractLDAPSSLSocketFactory); + assertEquals("MyNewClass", socketFactory.getClass().getSimpleName()); + } + + private AbstractLDAPSSLSocketFactory invokeGetDefaultMethod(Class<? extends AbstractLDAPSSLSocketFactory> socketFactoryClass) throws Exception + { + return (AbstractLDAPSSLSocketFactory) socketFactoryClass.getMethod("getDefault").invoke(null); + } + + class TestClassForLoading + { + } +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java index 29dc54a8f8..c468ca2e0d 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java @@ -55,4 +55,13 @@ public class StringUtilTest extends QpidTestCase } } + public void testCreateUniqueJavaName() + { + assertEquals("MyName_973de1b4e26b629d4817c8255090e58e", _util.createUniqueJavaName("MyName")); + assertEquals("ContaisIllegalJavaCharacters_a68b2484f2eb790558d6527e56c595fa", _util.createUniqueJavaName("Contais+Illegal-Java*Characters")); + assertEquals("StartsWithIllegalInitial_93031eec569608c60c6a98ac9e84a0a7", _util.createUniqueJavaName("9StartsWithIllegalInitial")); + assertEquals("97b247ba19ff869340d3797cc73ca065", _util.createUniqueJavaName("1++++----")); + assertEquals("d41d8cd98f00b204e9800998ecf8427e", _util.createUniqueJavaName("")); + } + } diff --git a/qpid/java/build.deps b/qpid/java/build.deps index d744d1a05a..67a67783c9 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -26,6 +26,8 @@ commons-digester=lib/required/commons-digester-1.8.1.jar commons-lang=lib/required/commons-lang-2.6.jar commons-logging=lib/required/commons-logging-1.1.1.jar +bcel=lib/required/bcel-5.2.jar + derby-db=lib/required/derby-10.8.2.2.jar geronimo-jms=lib/required/geronimo-jms_1.1_spec-1.0.jar @@ -75,7 +77,7 @@ amqp-1-0-client-jms.libs=${geronimo-jms} tools.libs=${commons-configuration.libs} ${log4j} broker-core.libs=${commons-cli} ${commons-logging} ${log4j} ${slf4j-log4j} \ ${xalan} ${derby-db} ${commons-configuration.libs} \ - ${jackson-core} ${jackson-mapper} ${jetty} ${jetty-continuation} ${jetty-security} ${jetty-http} ${jetty-io} ${jetty-servlet} ${jetty-util} ${servlet-api} ${jetty-websocket} + ${jackson-core} ${jackson-mapper} ${jetty} ${jetty-continuation} ${jetty-security} ${jetty-http} ${jetty-io} ${jetty-servlet} ${jetty-util} ${servlet-api} ${jetty-websocket} ${bcel} #Borrow the broker-core libs, hack for release binary generation broker.libs=${broker-core.libs} diff --git a/qpid/java/ivy.retrieve.xml b/qpid/java/ivy.retrieve.xml index a13a45b451..388e2d0dc4 100644 --- a/qpid/java/ivy.retrieve.xml +++ b/qpid/java/ivy.retrieve.xml @@ -42,6 +42,7 @@ <dependency org="commons-digester" name="commons-digester" rev="1.8.1" transitive="false"/> <dependency org="commons-lang" name="commons-lang" rev="2.6" transitive="false"/> <dependency org="commons-logging" name="commons-logging" rev="1.1.1" transitive="false"/> + <dependency org="org.apache.bcel" name="bcel" rev="5.2" transitive="false"/> <dependency org="org.apache.derby" name="derby" rev="10.8.2.2" transitive="false"/> <dependency org="org.apache.geronimo.framework" name="geronimo-kernel" rev="2.2.1" transitive="false"/> <dependency org="org.apache.geronimo.specs" name="geronimo-ejb_3.0_spec" rev="1.0.1" transitive="false"/> diff --git a/qpid/java/lib/poms/bcel-5.2.xml b/qpid/java/lib/poms/bcel-5.2.xml new file mode 100644 index 0000000000..b218981e8a --- /dev/null +++ b/qpid/java/lib/poms/bcel-5.2.xml @@ -0,0 +1,22 @@ +<?xml version="1.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. +--> +<dep> + <groupId>org.apache.bcel</groupId> + <artifactId>bcel</artifactId> + <version>5.2</version> +</dep> |
