Skip to content

Commit

Permalink
This closes #2173
Browse files Browse the repository at this point in the history
  • Loading branch information
jbertram committed Jul 6, 2018
2 parents a63b031 + d54e5a7 commit ae29edf
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -128,7 +130,9 @@ public void initialize(Subject subject,
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));
Expand Down Expand Up @@ -220,6 +224,7 @@ public boolean commit() throws LoginException {
private void clear() {
username = null;
userAuthenticated = false;
closeContext();
}

@Override
Expand Down Expand Up @@ -579,6 +584,12 @@ protected void openContext() throws Exception {
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
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;
Expand All @@ -45,6 +49,7 @@
import org.apache.directory.server.core.annotations.ApplyLdifFiles;
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
import org.apache.directory.server.core.integ.FrameworkRunner;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -60,6 +65,8 @@
@ApplyLdifFiles("test.ldif")
public class LDAPLoginModuleTest extends AbstractLdapTestUnit {

private static final Logger logger = Logger.getLogger(LDAPLoginModuleTest.class);

private static final String PRINCIPAL = "uid=admin,ou=system";
private static final String CREDENTIALS = "secret";

Expand Down Expand Up @@ -109,6 +116,8 @@ public void testRunning() throws Exception {

@Test
public void testLogin() throws LoginException {
logger.info("num session: " + ldapServer.getLdapSessionManager().getSessions().length);

LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
Expand All @@ -125,6 +134,97 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
});
context.login();
context.logout();

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;
while (numSession != expected && System.currentTimeMillis() < expiry) {
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException ok) {
break;
}
numSession = ldapServer.getLdapSessionManager().getSessions().length;
logger.info("num session " + numSession);

}
return numSession == expected;
}

@Test
Expand All @@ -150,6 +250,7 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
return;
}
fail("Should have failed authenticating");
assertTrue("no sessions after logout", waitForSessions(0));
}

@Test
Expand Down
21 changes: 21 additions & 0 deletions artemis-server/src/test/resources/login.config
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions docs/user-manual/en/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ae29edf

Please sign in to comment.