diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index f8d7db5f0a..19194fab17 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -85,6 +85,8 @@ public class LDAPLoginModule implements LoginModule { private static final String AUTHENTICATE_USER = "authenticateUser"; private static final String REFERRAL = "referral"; private static final String PASSWORD_CODEC = "passwordCodec"; + private static final String CONNECTION_POOL = "connectionPool"; + private static final String CONNECTION_TIMEOUT = "connectionTimeout"; protected DirContext context; @@ -128,7 +130,9 @@ public class LDAPLoginModule implements LoginModule { new LDAPLoginProperty(PASSWORD_CODEC, (String) options.get(PASSWORD_CODEC)), new LDAPLoginProperty(SASL_LOGIN_CONFIG_SCOPE, (String) options.get(SASL_LOGIN_CONFIG_SCOPE)), new LDAPLoginProperty(AUTHENTICATE_USER, (String) options.get(AUTHENTICATE_USER)), - new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL))}; + new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL)), + new LDAPLoginProperty(CONNECTION_POOL, (String) options.get(CONNECTION_POOL)), + new LDAPLoginProperty(CONNECTION_TIMEOUT, (String) options.get(CONNECTION_TIMEOUT))}; if (isLoginPropertySet(AUTHENTICATE_USER)) { authenticateUser = Boolean.valueOf(getLDAPPropertyValue(AUTHENTICATE_USER)); @@ -580,6 +584,12 @@ public class LDAPLoginModule implements LoginModule { env.put(Context.SECURITY_PROTOCOL, getLDAPPropertyValue(CONNECTION_PROTOCOL)); env.put(Context.PROVIDER_URL, getLDAPPropertyValue(CONNECTION_URL)); env.put(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(AUTHENTICATION)); + if (isLoginPropertySet(CONNECTION_POOL)) { + env.put("com.sun.jndi.ldap.connect.pool", getLDAPPropertyValue(CONNECTION_POOL)); + } + if (isLoginPropertySet(CONNECTION_TIMEOUT)) { + env.put("com.sun.jndi.ldap.connect.timeout", getLDAPPropertyValue(CONNECTION_TIMEOUT)); + } // handle LDAP referrals // valid values are "throw", "ignore" and "follow" diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index 97be299d6b..d28bd4c1f8 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -36,6 +36,10 @@ import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; import org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule; @@ -134,6 +138,79 @@ public class LDAPLoginModuleTest extends AbstractLdapTestUnit { assertTrue("no sessions after logout", waitForSessions(0)); } + @Test + public void testLoginPooled() throws LoginException { + + LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + + // again + + context.login(); + context.logout(); + + // new context + context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + + Executor pool = Executors.newCachedThreadPool(); + for (int i = 0; i < 10; i++) { + ((ExecutorService) pool).execute(new Runnable() { + @Override + public void run() { + try { + LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + } catch (Exception ignored) { + } + } + }); + } + assertTrue("no sessions after logout", waitForSessions(10)); + } + private boolean waitForSessions(int expected) { final long expiry = System.currentTimeMillis() + 5000; int numSession = ldapServer.getLdapSessionManager().getSessions().length; diff --git a/artemis-server/src/test/resources/login.config b/artemis-server/src/test/resources/login.config index 8e531caf06..26791d9d0b 100644 --- a/artemis-server/src/test/resources/login.config +++ b/artemis-server/src/test/resources/login.config @@ -49,6 +49,27 @@ LDAPLogin { ; }; +LDAPLoginPooled { + org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required + debug=true + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword=secret + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(member=uid={1},ou=system)" + roleSearchSubtree=false + connectionPool=true + connectionTimeout="2000" + ; +}; + UnAuthenticatedLDAPLogin { org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required debug=true diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index dfd1a41b2b..47fb22897c 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -587,6 +587,13 @@ system. It is implemented by for the connection to the directory server. This option must be set explicitly to an empty string, because it has no default value. +- `connectionPool`. boolean, enable the ldap connection pool property + 'com.sun.jndi.ldap.connect.pool'. Note that the pool is [configured at the jvm level with system properties](https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html). + + +- `connectionTimeout`. String milliseconds, that can time limit a ldap connection + attempt. The default is infinite. + - `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this option to