This is an automated email from the ASF dual-hosted git repository. orudyy pushed a commit to branch 7.1.x in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git
commit 097031e32ad5334ddd1831d563ef0d1680f2a7bd Author: Alex Rudyy <oru...@apache.org> AuthorDate: Sun Sep 15 00:14:19 2019 +0100 QPID-8363: [Broker-J] Add support for GSSAPI authentication into SimpleLDAP authentication provider (cherry picked from commit 4c7aeb273736baebd49cf5c0807359ca3f15ed7e) --- broker-core/pom.xml | 5 + .../auth/manager/LdapAuthenticationMethod.java | 27 +- .../manager/SimpleLDAPAuthenticationManager.java | 20 +- .../SimpleLDAPAuthenticationManagerImpl.java | 207 ++++++--- .../SimpleLDAPAuthenticationManagerTest.java | 495 +++++++++++++++++++++ broker-core/src/test/resources/login.config | 55 +++ broker-core/src/test/resources/users.ldif | 50 +++ .../authenticationprovider/simpleldap/add.html | 30 +- .../authenticationprovider/simpleldap/show.html | 8 + .../authenticationprovider/simpleldap/add.js | 36 +- .../authenticationprovider/simpleldap/show.js | 7 +- ...oker-Security-Authentication-Providers-LDAP.xml | 10 + pom.xml | 18 + 13 files changed, 878 insertions(+), 90 deletions(-) diff --git a/broker-core/pom.xml b/broker-core/pom.xml index a7f0325..7f7bd78 100644 --- a/broker-core/pom.xml +++ b/broker-core/pom.xml @@ -92,6 +92,11 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.directory.server</groupId> + <artifactId>apacheds-all</artifactId> + </dependency> + </dependencies> <build> diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/LdapAuthenticationMethod.java similarity index 56% copy from broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js copy to broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/LdapAuthenticationMethod.java index 9e13956..50335e5 100644 --- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js +++ b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/LdapAuthenticationMethod.java @@ -17,24 +17,23 @@ * under the License. */ -define(["qpid/common/util", "dojo/domReady!"], function (util) +package org.apache.qpid.server.security.auth.manager; + +public enum LdapAuthenticationMethod { + NONE("none"), + SIMPLE("simple"), + GSSAPI("GSSAPI"); + + private final String _method; - function SimpleLdapAuthenticationProvider(data) + LdapAuthenticationMethod(String method) { - this.fields = []; - var attributes = data.parent.management.metadata.getMetaData("AuthenticationProvider", "SimpleLDAP").attributes; - for (var name in attributes) - { - this.fields.push(name); - } - util.buildUI(data.containerNode, data.parent, "authenticationprovider/simpleldap/show.html", this.fields, this); + _method = method; } - SimpleLdapAuthenticationProvider.prototype.update = function (data) + public String getMethod() { - util.updateUI(data, this.fields, this); + return _method; } - - return SimpleLdapAuthenticationProvider; -}); +} diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java index 4820675..fe650f7 100644 --- a/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java +++ b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java @@ -43,10 +43,15 @@ public interface SimpleLDAPAuthenticationManager<X extends SimpleLDAPAuthenticat String PROVIDER_AUTH_URL = "providerAuthUrl"; String SEARCH_CONTEXT = "searchContext"; String LDAP_CONTEXT_FACTORY = "ldapContextFactory"; - String SEARCH_USERNAME = "getSearchUsername"; - String SEARCH_PASSWORD = "getSearchPassword"; + String SEARCH_USERNAME = "searchUsername"; + String SEARCH_PASSWORD = "searchPassword"; String TRUST_STORE = "trustStore"; - + String SEARCH_FILTER = "searchFilter"; + String GROUP_SEARCH_CONTEXT = "groupSearchContext"; + String GROUP_SEARCH_FILTER = "groupSearchFilter"; + String AUTHENTICATION_METHOD ="authenticationMethod"; + String LOGIN_CONFIG_SCOPE = "loginConfigScope"; + String LOGIN_CONFIG_SCOPE_DEFAULT = "qpid-broker-j"; @ManagedAttribute( description = "LDAP server URL", mandatory = true) String getProviderUrl(); @@ -90,6 +95,15 @@ public interface SimpleLDAPAuthenticationManager<X extends SimpleLDAPAuthenticat @ManagedAttribute( description = "Defines the group search scope. If true the search for group entries is performed in the entire subtree of the group search context") boolean isGroupSubtreeSearchScope(); + @ManagedAttribute(description = "Method of authentication to use when binding into LDAP. Supported methods: NONE, SIMPLE, GSSAPI.", + defaultValue = "NONE") + LdapAuthenticationMethod getAuthenticationMethod(); + + @ManagedAttribute(description = "The scope of JAAS configuration from login module to use to obtain Kerberos" + + " initiator credentials when the authentication method is GSSAPI", + defaultValue = LOGIN_CONFIG_SCOPE_DEFAULT) + String getLoginConfigScope(); + @DerivedAttribute List<String> getTlsProtocolWhiteList(); diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerImpl.java b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerImpl.java index 863f421..7a18c6c 100644 --- a/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerImpl.java +++ b/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerImpl.java @@ -25,6 +25,8 @@ import static java.util.Collections.unmodifiableList; import java.security.GeneralSecurityException; import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -47,6 +49,9 @@ import javax.naming.directory.SearchResult; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; @@ -69,23 +74,27 @@ import org.apache.qpid.server.security.auth.sasl.SaslNegotiator; import org.apache.qpid.server.security.auth.sasl.SaslSettings; import org.apache.qpid.server.security.auth.sasl.plain.PlainNegotiator; import org.apache.qpid.server.security.group.GroupPrincipal; +import org.apache.qpid.server.transport.network.security.ssl.SSLUtil; import org.apache.qpid.server.util.CipherSuiteAndProtocolRestrictingSSLSocketFactory; import org.apache.qpid.server.util.ParameterizedTypes; +import org.apache.qpid.server.util.ServerScopedRuntimeException; import org.apache.qpid.server.util.StringUtil; -import org.apache.qpid.server.transport.network.security.ssl.SSLUtil; -public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationManager<SimpleLDAPAuthenticationManagerImpl> +public class SimpleLDAPAuthenticationManagerImpl + extends AbstractAuthenticationManager<SimpleLDAPAuthenticationManagerImpl> implements SimpleLDAPAuthenticationManager<SimpleLDAPAuthenticationManagerImpl> { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleLDAPAuthenticationManagerImpl.class); private static final List<String> CONNECTIVITY_ATTRS = unmodifiableList(Arrays.asList(PROVIDER_URL, - PROVIDER_AUTH_URL, - SEARCH_CONTEXT, - LDAP_CONTEXT_FACTORY, - SEARCH_USERNAME, - SEARCH_PASSWORD, - TRUST_STORE)); + PROVIDER_AUTH_URL, + SEARCH_CONTEXT, + LDAP_CONTEXT_FACTORY, + SEARCH_USERNAME, + SEARCH_PASSWORD, + TRUST_STORE, + LOGIN_CONFIG_SCOPE, + AUTHENTICATION_METHOD)); /** * Environment key to instruct {@link InitialDirContext} to override the socket factory. @@ -131,6 +140,12 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM @ManagedAttributeField private boolean _groupSubtreeSearchScope; + @ManagedAttributeField + private LdapAuthenticationMethod _authenticationMethod; + + @ManagedAttributeField + private String _loginConfigScope; + private List<String> _tlsProtocolWhiteList; private List<String> _tlsProtocolBlackList; @@ -154,9 +169,7 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM protected void validateOnCreate() { super.validateOnCreate(); - - Class<? extends SocketFactory> sslSocketFactoryOverrideClass = createSslSocketFactoryOverrideClass(_trustStore); - validateInitialDirContext(sslSocketFactoryOverrideClass, _providerUrl, _searchUsername, _searchPassword); + validateInitialDirContext(this); } @Override @@ -166,11 +179,8 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM if (!disjoint(changedAttributes, CONNECTIVITY_ATTRS)) { - SimpleLDAPAuthenticationManager changed = (SimpleLDAPAuthenticationManager)proxyForValidation; - TrustStore changedTruststore = changed.getTrustStore(); - Class<? extends SocketFactory> sslSocketFactoryOverrideClass = createSslSocketFactoryOverrideClass(changedTruststore); - validateInitialDirContext(sslSocketFactoryOverrideClass, changed.getProviderUrl(), changed.getSearchUsername(), - changed.getSearchPassword()); + SimpleLDAPAuthenticationManager changed = (SimpleLDAPAuthenticationManager) proxyForValidation; + validateInitialDirContext(changed); } } @@ -279,6 +289,18 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM } @Override + public LdapAuthenticationMethod getAuthenticationMethod() + { + return _authenticationMethod; + } + + @Override + public String getLoginConfigScope() + { + return _loginConfigScope; + } + + @Override public List<String> getMechanisms() { return singletonList(PlainNegotiator.MECHANISM); @@ -307,10 +329,24 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM private AuthenticationResult doLDAPNameAuthentication(String userId, String password) { + Subject gssapiIdentity = null; + if (LdapAuthenticationMethod.GSSAPI.equals(getAuthenticationMethod())) + { + try + { + gssapiIdentity = doGssApiLogin(getLoginConfigScope()); + } + catch (LoginException e) + { + LOGGER.warn("JAAS Login failed", e); + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + final String name; try { - name = getNameFromId(userId); + name = getNameFromId(userId, gssapiIdentity); } catch (NamingException e) { @@ -334,7 +370,7 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM InitialDirContext ctx = null; try { - ctx = createInitialDirContext(env, _sslSocketFactoryOverrideClass); + ctx = createInitialDirContext(env, _sslSocketFactoryOverrideClass, gssapiIdentity); Set<Principal> groups = Collections.emptySet(); if (isGroupSearchRequired()) @@ -342,9 +378,9 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM if (!providerAuthUrl.equals(getProviderUrl())) { closeSafely(ctx); - ctx = createSearchInitialDirContext(); + ctx = createSearchInitialDirContext(gssapiIdentity); } - groups = findGroups(ctx, name); + groups = findGroups(ctx, name, gssapiIdentity); } //Authentication succeeded @@ -402,7 +438,8 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM return value != null && !"".equals(value); } - private Set<Principal> findGroups(DirContext context, String userDN) throws NamingException + private Set<Principal> findGroups(DirContext context, String userDN, final Subject gssapiIdentity) + throws NamingException { Set<Principal> groupPrincipals = new HashSet<>(); if (getGroupAttributeName() != null && !"".equals(getGroupAttributeName())) @@ -436,10 +473,11 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM searchControls.setSearchScope(isGroupSubtreeSearchScope() ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE); - NamingEnumeration<?> groupEnumeration = context.search(getGroupSearchContext(), - getGroupSearchFilter(), - new String[]{encode(userDN)}, - searchControls); + PrivilegedExceptionAction<NamingEnumeration<?>> search = () -> context.search(getGroupSearchContext(), + getGroupSearchFilter(), + new String[]{encode(userDN)}, + searchControls); + NamingEnumeration<?> groupEnumeration = invokeContextOperationAs(gssapiIdentity, search); while (groupEnumeration.hasMore()) { SearchResult result = (SearchResult) groupEnumeration.next(); @@ -491,8 +529,9 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM return env; } - private InitialDirContext createInitialDirContext(Hashtable<String, Object> env, - Class<? extends SocketFactory> sslSocketFactoryOverrideClass) throws NamingException + private InitialDirContext createInitialDirContext(final Hashtable<String, Object> env, + final Class<? extends SocketFactory> sslSocketFactoryOverrideClass, + final Subject gssapiIdentity) throws NamingException { ClassLoader existingContextClassLoader = null; @@ -508,7 +547,7 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM Thread.currentThread().setContextClassLoader(sslSocketFactoryOverrideClass.getClassLoader()); revertContentClassLoader = true; } - return new InitialDirContext(env); + return invokeContextOperationAs(gssapiIdentity, () -> new InitialDirContext(env)); } finally { @@ -564,27 +603,45 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM ", providerUrl=" + _providerUrl + ", providerAuthUrl=" + _providerAuthUrl + ", searchContext=" + _searchContext + ", state=" + getState() + ", searchFilter=" + _searchFilter + ", ldapContextFactory=" + _ldapContextFactory + - ", bindWithoutSearch=" + _bindWithoutSearch + ", trustStore=" + _trustStore + - ", searchUsername=" + _searchUsername + "]"; + ", bindWithoutSearch=" + _bindWithoutSearch + ", trustStore=" + _trustStore + + ", searchUsername=" + _searchUsername + ", loginConfigScope=" + _loginConfigScope + + ", authenticationMethod=" + _authenticationMethod + "]"; } - private void validateInitialDirContext(Class<? extends SocketFactory> sslSocketFactoryOverrideClass, - final String providerUrl, - final String searchUsername, final String searchPassword) + private void validateInitialDirContext(final SimpleLDAPAuthenticationManager<?> authenticationProvider) { - Hashtable<String,Object> env = createInitialDirContextEnvironment(providerUrl); + final TrustStore truststore = authenticationProvider.getTrustStore(); + final Class<? extends SocketFactory> sslSocketFactoryOverrideClass = + createSslSocketFactoryOverrideClass(truststore); - setupSearchContext(env, searchUsername, searchPassword); + final Hashtable<String, Object> env = + createInitialDirContextEnvironment(authenticationProvider.getProviderUrl()); + setAuthenticationProperties(env, + authenticationProvider.getSearchUsername(), + authenticationProvider.getSearchPassword(), + authenticationProvider.getAuthenticationMethod()); InitialDirContext ctx = null; try { - ctx = createInitialDirContext(env, sslSocketFactoryOverrideClass); + Subject gssapiIdentity = null; + if (LdapAuthenticationMethod.GSSAPI.equals(authenticationProvider.getAuthenticationMethod())) + { + gssapiIdentity = doGssApiLogin(authenticationProvider.getLoginConfigScope()); + } + ctx = createInitialDirContext(env, sslSocketFactoryOverrideClass, gssapiIdentity); } catch (NamingException e) { - LOGGER.error("Failed to establish connectivity to the ldap server for '{}'", providerUrl, e); - throw new IllegalConfigurationException("Failed to establish connectivity to the ldap server." , e); + LOGGER.debug("Failed to establish connectivity to the ldap server for '{}'", + authenticationProvider.getProviderUrl(), + e); + throw new IllegalConfigurationException("Failed to establish connectivity to the ldap server.", e); + } + catch (LoginException e) + { + LOGGER.debug("JAAS login failed ", e); + throw new IllegalConfigurationException("JAAS login failed.", e); } finally { @@ -592,14 +649,26 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM } } - private void setupSearchContext(final Hashtable<String, Object> env, - final String searchUsername, final String searchPassword) + private void setAuthenticationProperties(final Hashtable<String, Object> env, + final String userName, + final String password, + final LdapAuthenticationMethod authenticationMethod) { - if(_searchUsername != null && _searchUsername.trim().length()>0) + if (LdapAuthenticationMethod.GSSAPI.equals(authenticationMethod)) + { + env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); + } + else if (LdapAuthenticationMethod.SIMPLE.equals(authenticationMethod)) { env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, searchUsername); - env.put(Context.SECURITY_CREDENTIALS, searchPassword); + if (userName != null) + { + env.put(Context.SECURITY_PRINCIPAL, userName); + } + if (password != null) + { + env.put(Context.SECURITY_CREDENTIALS, password); + } } else { @@ -607,11 +676,12 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM } } - private String getNameFromId(String id) throws NamingException + private String getNameFromId(final String id, final Subject gssapiIdentity) + throws NamingException { - if(!isBindWithoutSearch()) + if (!isBindWithoutSearch()) { - InitialDirContext ctx = createSearchInitialDirContext(); + InitialDirContext ctx = createSearchInitialDirContext(gssapiIdentity); try { @@ -622,7 +692,13 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM NamingEnumeration<?> namingEnum = null; LOGGER.debug("Searching for '{}'", id); - namingEnum = ctx.search(_searchContext, _searchFilter, new String[]{id}, searchControls); + namingEnum = invokeContextOperationAs(gssapiIdentity, + (PrivilegedExceptionAction<NamingEnumeration<?>>) () -> ctx.search( + _searchContext, + _searchFilter, + new String[]{id}, + searchControls)); + if (namingEnum.hasMore()) { SearchResult result = (SearchResult) namingEnum.next(); @@ -645,14 +721,45 @@ public class SimpleLDAPAuthenticationManagerImpl extends AbstractAuthenticationM { return id; } + } + private <T> T invokeContextOperationAs(final Subject identity, final PrivilegedExceptionAction<T> action) + throws NamingException + { + try + { + return Subject.doAs(identity, action); + } + catch (PrivilegedActionException e) + { + final Exception exception = e.getException(); + if (exception instanceof NamingException) + { + throw (NamingException) exception; + } + else if (exception instanceof RuntimeException) + { + throw (RuntimeException) exception; + } + else + { + throw new ServerScopedRuntimeException(exception); + } + } + } + + private Subject doGssApiLogin(final String configScope) throws LoginException + { + LoginContext loginContext = new LoginContext(configScope); + loginContext.login(); + return loginContext.getSubject(); } - private InitialDirContext createSearchInitialDirContext() throws NamingException + private InitialDirContext createSearchInitialDirContext(final Subject gssapiIdentity) throws NamingException { Hashtable<String, Object> env = createInitialDirContextEnvironment(_providerUrl); - setupSearchContext(env, _searchUsername, _searchPassword); - return createInitialDirContext(env, _sslSocketFactoryOverrideClass); + setAuthenticationProperties(env, _searchUsername, _searchPassword, _authenticationMethod); + return createInitialDirContext(env, _sslSocketFactoryOverrideClass, gssapiIdentity); } diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java new file mode 100644 index 0000000..b5dd2a1 --- /dev/null +++ b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java @@ -0,0 +1,495 @@ +/* + * 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; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.qpid.server.security.auth.manager.CachingAuthenticationProvider.AUTHENTICATION_CACHE_MAX_SIZE; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; +import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.util.Strings; +import org.apache.directory.server.annotations.CreateKdcServer; +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.annotations.SaslMechanism; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.integ.CreateLdapServerRule; +import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; +import org.apache.directory.server.factory.ServerAnnotationProcessor; +import org.apache.directory.server.kerberos.kdc.KdcServer; +import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory; +import org.apache.directory.server.kerberos.shared.keytab.Keytab; +import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry; +import org.apache.directory.server.ldap.LdapServer; +import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; +import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; +import org.apache.directory.shared.kerberos.KerberosTime; +import org.apache.directory.shared.kerberos.codec.types.EncryptionType; +import org.apache.directory.shared.kerberos.components.EncryptionKey; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.BrokerTestHelper; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.SocketConnectionPrincipal; +import org.apache.qpid.server.security.auth.sasl.SaslNegotiator; +import org.apache.qpid.server.security.auth.sasl.SaslSettings; +import org.apache.qpid.test.utils.JvmVendor; +import org.apache.qpid.test.utils.SystemPropertySetter; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.test.utils.UnitTestBase; + +@CreateDS( + name = "testDS", + partitions = + { + @CreatePartition(name = "test", suffix = "dc=qpid,dc=org") + }, + additionalInterceptors = + { + KeyDerivationInterceptor.class + } +) +@CreateLdapServer( + transports = + { + @CreateTransport(protocol = "LDAP") + }, + allowAnonymousAccess = true, + saslHost = "localhost", + saslPrincipal = "ldap/localh...@qpid.org", + saslMechanisms = + { + @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class) + } +) +@CreateKdcServer( + transports = + { + @CreateTransport(protocol = "TCP", port = 0) + }, + searchBaseDn = "ou=users,dc=qpid,dc=org") +@ApplyLdifFiles("users.ldif") +public class SimpleLDAPAuthenticationManagerTest extends UnitTestBase +{ + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleLDAPAuthenticationManagerTest.class); + private static final String ROOT = "dc=qpid,dc=org"; + private static final String USERS_DN = "ou=users," + ROOT; + private static final String SEARCH_CONTEXT_VALUE = USERS_DN; + private static final String SEARCH_FILTER_VALUE = "(uid={0})"; + private static final String LDAP_URL_TEMPLATE = "ldap://localhost:%d"; + private static final String USER_1_NAME = "test1"; + private static final String USER_1_PASSWORD = "password1"; + private static final String USER_1_DN = "cn=integration-test1,ou=users,dc=qpid,dc=org"; + private static final String GROUP_SEARCH_CONTEXT_VALUE = "ou=groups,dc=qpid,dc=org"; + private static final String GROUP_SEARCH_FILTER_VALUE = "(member={0})"; + private static final String LDAP_SERVICE_NAME = "ldap"; + private static final String REALM = "QPID.ORG"; + private static final String HOSTNAME = "localhost"; + private static final String BROKER_PRINCIPAL = "service/" + HOSTNAME; + private static final String LINE_SEPARATOR = System.lineSeparator(); + private static final String LOGIN_CONFIG = "login.config"; + private static final String LOGIN_SCOPE = "ldap-gssapi-bind"; + private static final AtomicBoolean KERBEROS_SETUP = new AtomicBoolean(); + + @ClassRule + public static CreateLdapServerRule LDAP = new CreateLdapServerRule(); + + @ClassRule + public static final SystemPropertySetter SYSTEM_PROPERTY_SETTER = new SystemPropertySetter(); + + private SimpleLDAPAuthenticationManager _authenticationProvider; + + @Before + public void setUp() + { + _authenticationProvider = createAuthenticationProvider(); + } + + @After + public void tearDown() + { + if (_authenticationProvider != null) + { + _authenticationProvider.close(); + } + } + + @Test + public void testAuthenticateSuccess() + { + final AuthenticationResult result = _authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + } + + @Test + public void testAuthenticateFailure() + { + final AuthenticationResult result = _authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD + "_"); + assertEquals(AuthenticationResult.AuthenticationStatus.ERROR, result.getStatus()); + } + + @Test + public void testSaslPlainNegotiatorPlain() + { + final SaslSettings saslSettings = mock(SaslSettings.class); + when(saslSettings.getLocalFQDN()).thenReturn(HOSTNAME); + + final SaslNegotiator negotiator = _authenticationProvider.createSaslNegotiator("PLAIN", saslSettings, null); + assertNotNull("Could not create SASL negotiator for mechanism 'PLAIN'", negotiator); + + final AuthenticationResult result = negotiator.handleResponse(new byte[0]); + assertEquals("Unexpected authentication status", + AuthenticationResult.AuthenticationStatus.CONTINUE, + result.getStatus()); + + final AuthenticationResult result2 = + negotiator.handleResponse(String.format("\0%s\0%s", USER_1_NAME, USER_1_PASSWORD).getBytes(UTF_8)); + + assertEquals("Unexpected authentication status", + AuthenticationResult.AuthenticationStatus.SUCCESS, + result2.getStatus()); + } + + @Test + public void testGroups() + { + _authenticationProvider.close(); + final Map<String, Object> groupSetUp = new HashMap<>(); + groupSetUp.put(SimpleLDAPAuthenticationManager.GROUP_SEARCH_CONTEXT, GROUP_SEARCH_CONTEXT_VALUE); + groupSetUp.put(SimpleLDAPAuthenticationManager.GROUP_SEARCH_FILTER, GROUP_SEARCH_FILTER_VALUE); + _authenticationProvider = createAuthenticationProvider(groupSetUp); + + final AuthenticationResult result = _authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + + final Set<Principal> principals = result.getPrincipals(); + assertNotNull(principals); + + final Principal groupPrincipal = principals.stream() + .filter(p -> "cn=group1,ou=groups,dc=qpid,dc=org".equalsIgnoreCase(p.getName())) + .findFirst() + .orElse(null); + assertNotNull(groupPrincipal); + } + + @Test + public void testAuthenticateSuccessWhenCachingEnabled() + { + _authenticationProvider.close(); + _authenticationProvider = createCachingAuthenticationProvider(); + + final SocketConnectionPrincipal principal = mock(SocketConnectionPrincipal.class); + when(principal.getRemoteAddress()).thenReturn(new InetSocketAddress(HOSTNAME, 5672)); + final Subject subject = + new Subject(true, Collections.singleton(principal), Collections.emptySet(), Collections.emptySet()); + final AuthenticationResult result = Subject.doAs(subject, + (PrivilegedAction<AuthenticationResult>) () -> _authenticationProvider + .authenticate(USER_1_NAME, USER_1_PASSWORD)); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + } + + @Test + public void testGssapiBindWithKeyTab() throws Exception + { + setUpKerberosAndJaas(); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.AUTHENTICATION_METHOD, LdapAuthenticationMethod.GSSAPI.name()); + attributes.put(SimpleLDAPAuthenticationManager.LOGIN_CONFIG_SCOPE, LOGIN_SCOPE); + final SimpleLDAPAuthenticationManagerImpl authenticationProvider = createAuthenticationProvider(attributes); + final AuthenticationResult result = authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + } + + @Test + public void testChangeAuthenticationToGssapi() throws Exception + { + setUpKerberosAndJaas(); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.AUTHENTICATION_METHOD, LdapAuthenticationMethod.GSSAPI.name()); + attributes.put(SimpleLDAPAuthenticationManager.LOGIN_CONFIG_SCOPE, LOGIN_SCOPE); + _authenticationProvider.setAttributes(attributes); + + final AuthenticationResult result = _authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + } + + @Test + public void testChangeAuthenticationToGssapiWithInvalidScope() throws Exception + { + setUpKerberosAndJaas(); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.AUTHENTICATION_METHOD, LdapAuthenticationMethod.GSSAPI.name()); + attributes.put(SimpleLDAPAuthenticationManager.LOGIN_CONFIG_SCOPE, "non-existing"); + try + { + _authenticationProvider.setAttributes(attributes); + fail("Exception is expected"); + } + catch (IllegalConfigurationException e) + { + // pass + } + } + + @Test + public void testChangeAuthenticationToGssapiWhenConfigIsBroken() throws Exception + { + setUpKerberosAndJaas(); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.AUTHENTICATION_METHOD, LdapAuthenticationMethod.GSSAPI.name()); + attributes.put(SimpleLDAPAuthenticationManager.LOGIN_CONFIG_SCOPE, "ldap-gssapi-bind-broken"); + try + { + _authenticationProvider.setAttributes(attributes); + fail("Exception is expected"); + } + catch (IllegalConfigurationException e) + { + // pass + } + } + + @Test + public void testChangeAuthenticationToGssapiNoScopeProvided() throws Exception + { + setUpKerberosAndJaas(); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.AUTHENTICATION_METHOD, LdapAuthenticationMethod.GSSAPI.name()); + _authenticationProvider.setAttributes(attributes); + + final AuthenticationResult result = _authenticationProvider.authenticate(USER_1_NAME, USER_1_PASSWORD); + assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); + assertEquals(USER_1_DN, result.getMainPrincipal().getName()); + } + + private SimpleLDAPAuthenticationManagerImpl createAuthenticationProvider() + { + return createAuthenticationProvider(Collections.emptyMap()); + } + + private SimpleLDAPAuthenticationManagerImpl createCachingAuthenticationProvider() + { + final Map<String, String> context = Collections.singletonMap(AUTHENTICATION_CACHE_MAX_SIZE, "1"); + final Map<String, Object> attributes = + Collections.singletonMap(SimpleLDAPAuthenticationManager.CONTEXT, context); + return createAuthenticationProvider(attributes); + } + + private SimpleLDAPAuthenticationManagerImpl createAuthenticationProvider(final Map<String, Object> settings) + { + final Broker<?> broker = BrokerTestHelper.createBrokerMock(); + final Map<String, Object> attributes = new HashMap<>(); + attributes.put(SimpleLDAPAuthenticationManager.NAME, getTestName()); + attributes.put(SimpleLDAPAuthenticationManager.SEARCH_CONTEXT, SEARCH_CONTEXT_VALUE); + attributes.put(SimpleLDAPAuthenticationManager.PROVIDER_URL, + String.format(LDAP_URL_TEMPLATE, LDAP.getLdapServer().getPort())); + attributes.put(SimpleLDAPAuthenticationManager.SEARCH_FILTER, SEARCH_FILTER_VALUE); + attributes.put(SimpleLDAPAuthenticationManager.CONTEXT, + Collections.singletonMap(AUTHENTICATION_CACHE_MAX_SIZE, "0")); + attributes.putAll(settings); + final SimpleLDAPAuthenticationManagerImpl authenticationProvider = + new SimpleLDAPAuthenticationManagerImpl(attributes, broker); + authenticationProvider.open(); + return authenticationProvider; + } + + + private void setUpKerberosAndJaas() throws Exception + { + assumeThat(getJvmVendor(), not(JvmVendor.IBM)); + if (KERBEROS_SETUP.compareAndSet(false, true)) + { + setUpKerberos(); + setUpJaas(); + } + } + + private void setUpKerberos() throws Exception + { + final LdapServer ldapServer = LDAP.getLdapServer(); + final KdcServer kdcServer = + ServerAnnotationProcessor.getKdcServer(LDAP.getDirectoryService(), ldapServer.getPort() + 1); + kdcServer.getConfig().setPaEncTimestampRequired(false); + final KerberosPrincipal servicePrincipal = + new KerberosPrincipal(LDAP_SERVICE_NAME + "/" + HOSTNAME + "@" + REALM, + KerberosPrincipal.KRB_NT_SRV_HST); + final String servicePrincipalName = servicePrincipal.getName(); + ldapServer.setSaslHost(servicePrincipalName.substring(servicePrincipalName.indexOf("/") + 1, + servicePrincipalName.indexOf("@"))); + ldapServer.setSaslPrincipal(servicePrincipalName); + ldapServer.setSearchBaseDn(USERS_DN); + + final String krb5confPath = createKrb5Conf(kdcServer.getTransports()[0].getPort()); + SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.krb5.conf", krb5confPath); + + createPrincipal("KDC", "KDC", "krbtgt", UUID.randomUUID().toString(), "krbtgt/" + REALM + "@" + REALM); + createPrincipal("Service", "LDAP Service", "ldap", UUID.randomUUID().toString(), servicePrincipalName); + } + + private void setUpJaas() throws LdapException, IOException + { + createKeyTab(BROKER_PRINCIPAL); + final URL resource = SimpleLDAPAuthenticationManagerTest.class.getClassLoader().getResource(LOGIN_CONFIG); + LOGGER.debug("JAAS config:" + resource); + assertNotNull(resource); + SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.auth.login.config", resource.getPath()); + SYSTEM_PROPERTY_SETTER.setSystemProperty("sun.security.krb5.debug", "true"); + } + + private String createKrb5Conf(final int port) throws IOException + { + final File file = createFile("krb5", ".conf"); + final String config = String.format("[libdefaults]%1$s" + + " default_realm = %2$s%1$s" + + " udp_preference_limit = 1%1$s" + + "[realms]%1$s" + + " %2$s = {%1$s" + + " kdc = %3$s%1$s" + + " }%1$s" + + "[domain_realm]%1$s" + + " .%4$s = %2$s%1$s" + + " %4$s = %2$s%1$s", + LINE_SEPARATOR, + REALM, + HOSTNAME + ":" + port, + Strings.toLowerCaseAscii(REALM)); + LOGGER.debug("krb5.conf:" + config); + TestFileUtils.saveTextContentInFile(config, file); + return file.getAbsolutePath(); + } + + private void createPrincipal(final String sn, + final String cn, + final String uid, + final String userPassword, + final String kerberosPrincipalName) throws LdapException + { + final DirectoryService directoryService = LDAP.getDirectoryService(); + final Entry entry = new DefaultEntry(directoryService.getSchemaManager()); + entry.setDn(String.format("uid=%s,%s", uid, USERS_DN)); + entry.add("objectClass", "top", "person", "inetOrgPerson", "krb5principal", "krb5kdcentry"); + entry.add("cn", cn); + entry.add("sn", sn); + entry.add("uid", uid); + entry.add("userPassword", userPassword); + entry.add("krb5PrincipalName", kerberosPrincipalName); + entry.add("krb5KeyVersionNumber", "0"); + directoryService.getAdminSession().add(entry); + } + + private void createPrincipal(String uid, String userPassword) throws LdapException + { + createPrincipal(uid, uid, uid, userPassword, uid + "@" + REALM); + } + + private void createPrincipal(final File keyTabFile, final String... principals) throws LdapException, IOException + { + final Keytab keytab = new Keytab(); + final List<KeytabEntry> entries = new ArrayList<>(); + final String password = UUID.randomUUID().toString(); + for (final String principal : principals) + { + createPrincipal(principal, password); + final String principalName = principal + "@" + REALM; + final KerberosTime timestamp = new KerberosTime(); + final Map<EncryptionType, EncryptionKey> keys = KerberosKeyFactory.getKerberosKeys(principalName, password); + keys.forEach((type, key) -> entries.add(new KeytabEntry(principalName, + 1, + timestamp, + (byte) key.getKeyVersion(), + key))); + } + keytab.setEntries(entries); + keytab.write(keyTabFile); + } + + private void createKeyTab(String... principals) throws LdapException, IOException + { + final File keyTabFile = createFile("kerberos", ".keytab"); + createPrincipal(keyTabFile, principals); + } + + private File createFile(final String prefix, final String suffix) throws IOException + { + final Path targetDir = FileSystems.getDefault().getPath("target"); + final File file = new File(targetDir.toFile(), prefix + suffix); + if (file.exists()) + { + if (!file.delete()) + { + throw new IOException(String.format("Cannot delete existing file '%s'", file.getAbsolutePath())); + } + } + if (!file.createNewFile()) + { + throw new IOException(String.format("Cannot create file '%s'", file.getAbsolutePath())); + } + return file; + } +} diff --git a/broker-core/src/test/resources/login.config b/broker-core/src/test/resources/login.config new file mode 100644 index 0000000..d458516 --- /dev/null +++ b/broker-core/src/test/resources/login.config @@ -0,0 +1,55 @@ +/* + * 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. + */ + +ldap-gssapi-bind { + com.sun.security.auth.module.Krb5LoginModule required + useKeyTab=true + debug=true + storeKey=true + doNotPrompt=true + isInitiator=true + refreshKrb5Config=true + realm="QPID.ORG" + principal="service/localhost" + keyTab="target/kerberos.keytab"; +}; + +ldap-gssapi-bind-broken { + com.sun.security.auth.module.Krb5LoginModule required + useKeyTab=true + debug=true + storeKey=true + doNotPrompt=true + isInitiator=true + refreshKrb5Config=true + realm="QPID.ORG" + principal="service/localhost" + keyTab="target/kerberos-non-existing.keytab"; +}; + +qpid-broker-j { + com.sun.security.auth.module.Krb5LoginModule required + useKeyTab=true + debug=true + storeKey=true + doNotPrompt=true + isInitiator=true + refreshKrb5Config=true + realm="QPID.ORG" + principal="service/localhost" + keyTab="target/kerberos.keytab"; +}; diff --git a/broker-core/src/test/resources/users.ldif b/broker-core/src/test/resources/users.ldif new file mode 100644 index 0000000..0d76baa --- /dev/null +++ b/broker-core/src/test/resources/users.ldif @@ -0,0 +1,50 @@ +# +# 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. +# + +version: 1 +dn: dc=qpid,dc=org +objectClass: domain +objectClass: top +dc: tests + +dn: ou=users,dc=qpid,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Users + +dn: ou=groups,dc=qpid,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Groups + +dn: cn=integration-test1,ou=users,dc=qpid,dc=org +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: integration-test1 +sn: ldap-integration-test1 +uid: test1 +userPassword: password1 + +dn: cn=group1,ou=groups,dc=qpid,dc=org +cn: Group1 +member: cn=integration-test1,ou=Users,dc=qpid,dc=org +objectClass: groupOfNames +objectClass: top diff --git a/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/add.html b/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/add.html index 5b88a3c..3601540 100644 --- a/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/add.html +++ b/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/add.html @@ -68,11 +68,38 @@ promptMessage: 'Fully qualified class name for LDAP Context Factory'"/> </div> </div> - + <div class="clear"> + <div class="formLabel-labelCell tableContainer-labelCell">Authentication Method:</div> + <div class="tableContainer-valueCell formLabel-controlCell"> + <select class="authenticationMethod" data-dojo-type="dijit/form/FilteringSelect" + id="ldapAuthenticationMethod" + data-dojo-props=" + required: false, + name: 'authenticationMethod', + placeHolder: 'Select authentication method', + promptMessage: 'Select authentication method to bind into LDAP server', + title: 'Select authentication method to bind into LDAP server', + searchAttr: 'name'"> + </select> + </div> + </div> + <div class="clear"> + <div class="formLabel-labelCell tableContainer-labelCell">Login Config Scope:</div> + <div class="formLabel-controlCell tableContainer-valueCell"> + <input type="text" class="loginConfigScope" + data-dojo-type="dijit/form/ValidationTextBox" + data-dojo-props=" + name: 'loginConfigScope', + placeHolder: 'loginConfigScope', + title: 'Login configuration name for GSSAPI authentication', + promptMessage: 'Login configuration name for GSSAPI authentication'"/> + </div> + </div> <div class="clear"> <div class="formLabel-labelCell tableContainer-labelCell">Search username:</div> <div class="formLabel-controlCell tableContainer-valueCell"> <input type="text" class="searchUsername" + id="ldapSearchUsername" data-dojo-type="dijit/form/ValidationTextBox" data-dojo-props=" name: 'searchUsername', @@ -85,6 +112,7 @@ <div class="formLabel-labelCell tableContainer-labelCell">Search password:</div> <div class="formLabel-controlCell tableContainer-valueCell"> <input type="password" class="searchPassword" + id="ldapSearchPassword" data-dojo-type="dijit/form/ValidationTextBox" data-dojo-props=" name: 'searchPassword', diff --git a/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/show.html b/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/show.html index d71e01f..94bf306 100644 --- a/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/show.html +++ b/broker-plugins/management-http/src/main/java/resources/authenticationprovider/simpleldap/show.html @@ -41,6 +41,14 @@ <div class="formLabel-labelCell">Search password:</div> <div><span class="searchPassword" ></span></div> </div> + <div class="clear"> + <div class="formLabel-labelCell">Authentication method:</div> + <div><span class="authenticationMethod" ></span></div> + </div> + <div class="clear"> + <div class="formLabel-labelCell">Login config scope:</div> + <div><span class="loginConfigScope" ></span></div> + </div> <div class="clear"></div> <div class="formBox"> diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/add.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/add.js index 06bd2d5..edc8243 100644 --- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/add.js +++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/add.js @@ -81,6 +81,7 @@ define(["dojo/query", .then(function (trustStores) { that._initTrustStores(trustStores, data.containerNode); + that._initAuthenticationMethods(data.parent.management.metadata, data.containerNode); if (data.data) { that._initFields(data.data, data.containerNode, data.parent.management.metadata); @@ -137,28 +138,23 @@ define(["dojo/query", var trustStore = registry.byNode(query(".trustStore", containerNode)[0]); trustStore.set("store", trustStoresStore); }, - _initFields: function (data, containerNode, metadata) + _initAuthenticationMethods: function (metadata, containerNode) { var attributes = metadata.getMetaData("AuthenticationProvider", "SimpleLDAP").attributes; - for (var name in attributes) - { - var domNode = query("." + name, containerNode)[0]; - if (domNode) - { - var widget = registry.byNode(domNode); - if (widget) - { - if (widget instanceof dijit.form.CheckBox) - { - widget.set("checked", data[name]); - } - else - { - widget.set("value", data[name]); - } - } - } - } + var store = util.makeTypeStore(attributes.authenticationMethod.validValues); + var authenticationMethod = registry.byId("ldapAuthenticationMethod"); + authenticationMethod.set("store", store); + authenticationMethod.on("change", function (value) { + registry.byId('ldapSearchUsername').set("required", (value === "SIMPLE")); + }); + }, + _initFields: function (data, containerNode, metadata) + { + util.applyToWidgets(containerNode, + "AuthenticationProvider", + "SimpleLDAP", + data, + metadata); } }; }); diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js index 9e13956..096aa36 100644 --- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js +++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/simpleldap/show.js @@ -26,7 +26,10 @@ define(["qpid/common/util", "dojo/domReady!"], function (util) var attributes = data.parent.management.metadata.getMetaData("AuthenticationProvider", "SimpleLDAP").attributes; for (var name in attributes) { - this.fields.push(name); + if (attributes.hasOwnProperty(name)) + { + this.fields.push(name); + } } util.buildUI(data.containerNode, data.parent, "authenticationprovider/simpleldap/show.html", this.fields, this); } @@ -34,7 +37,7 @@ define(["qpid/common/util", "dojo/domReady!"], function (util) SimpleLdapAuthenticationProvider.prototype.update = function (data) { util.updateUI(data, this.fields, this); - } + }; return SimpleLdapAuthenticationProvider; }); diff --git a/doc/java-broker/src/docbkx/security/Java-Broker-Security-Authentication-Providers-LDAP.xml b/doc/java-broker/src/docbkx/security/Java-Broker-Security-Authentication-Providers-LDAP.xml index ed81c09..91ce9dc 100644 --- a/doc/java-broker/src/docbkx/security/Java-Broker-Security-Authentication-Providers-LDAP.xml +++ b/doc/java-broker/src/docbkx/security/Java-Broker-Security-Authentication-Providers-LDAP.xml @@ -57,6 +57,16 @@ certificate).</para> </listitem> <listitem> + <para><emphasis>Authentication method</emphasis> is a method of authentication to use on binding into LDAP + when <literal>bind without search</literal> mode is not selected. + Supported methods are NONE, SIMPLE, GSSAPI. The latter requires setting of <emphasis>Login Config Scope</emphasis> + which is a name of JAAS login module from JASS login configuration file specified using JVM system + property <emphasis>java.security.auth.login.config</emphasis> or Java security properties file. If + <emphasis>Login Config Scope</emphasis> is not specified with <literal>GSSAPI</literal> + <emphasis>Authentication method</emphasis>, the scope <emphasis>qpid-broker-j</emphasis> will be used. + </para> + </listitem> + <listitem> <para>Additional group information can be obtained from LDAP. There are two common ways of representing group membership in LDAP. <itemizedlist> diff --git a/pom.xml b/pom.xml index a76c4b8..e59d3ab 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,7 @@ <maven-jar-plugin-version>3.1.0</maven-jar-plugin-version> <maven-surefire-report-plugin-version>2.22.0</maven-surefire-report-plugin-version> <h2.version>1.4.199</h2.version> + <apache-directory-version>2.0.0-M23</apache-directory-version> </properties> <modules> @@ -724,6 +725,23 @@ <artifactId>maven-resolver-transport-http</artifactId> <version>${maven-resolver-version}</version> </dependency> + <!-- apacheds test dependency --> + <dependency> + <groupId>org.apache.directory.server</groupId> + <artifactId>apacheds-all</artifactId> + <version>${apache-directory-version}</version> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.apache.directory.shared</groupId> + <artifactId>shared-ldap-schema</artifactId> + </exclusion> + <exclusion> + <groupId>org.apache.directory.api</groupId> + <artifactId>api-ldap-schema-data</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> </dependencyManagement> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org