This closes #3826
This commit is contained in:
commit
72f9087c68
|
@ -17,7 +17,6 @@
|
|||
package org.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import javax.naming.AuthenticationException;
|
||||
import javax.naming.CommunicationException;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NameParser;
|
||||
|
@ -65,36 +64,59 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(LDAPLoginModule.class);
|
||||
|
||||
private static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory";
|
||||
private static final String CONNECTION_URL = "connectionURL";
|
||||
private static final String CONNECTION_USERNAME = "connectionUsername";
|
||||
private static final String CONNECTION_PASSWORD = "connectionPassword";
|
||||
private static final String CONNECTION_PROTOCOL = "connectionProtocol";
|
||||
private static final String AUTHENTICATION = "authentication";
|
||||
private static final String USER_BASE = "userBase";
|
||||
private static final String USER_SEARCH_MATCHING = "userSearchMatching";
|
||||
private static final String USER_SEARCH_SUBTREE = "userSearchSubtree";
|
||||
private static final String ROLE_BASE = "roleBase";
|
||||
private static final String ROLE_NAME = "roleName";
|
||||
private static final String ROLE_SEARCH_MATCHING = "roleSearchMatching";
|
||||
private static final String ROLE_SEARCH_SUBTREE = "roleSearchSubtree";
|
||||
private static final String USER_ROLE_NAME = "userRoleName";
|
||||
private static final String EXPAND_ROLES = "expandRoles";
|
||||
private static final String EXPAND_ROLES_MATCHING = "expandRolesMatching";
|
||||
private static final String SASL_LOGIN_CONFIG_SCOPE = "saslLoginConfigScope";
|
||||
private static final String AUTHENTICATE_USER = "authenticateUser";
|
||||
private static final String REFERRAL = "referral";
|
||||
private static final String IGNORE_PARTIAL_RESULT_EXCEPTION = "ignorePartialResultException";
|
||||
private static final String PASSWORD_CODEC = "passwordCodec";
|
||||
private static final String CONNECTION_POOL = "connectionPool";
|
||||
private static final String CONNECTION_TIMEOUT = "connectionTimeout";
|
||||
private static final String READ_TIMEOUT = "readTimeout";
|
||||
enum ConfigKey {
|
||||
|
||||
DEBUG("debug"),
|
||||
INITIAL_CONTEXT_FACTORY("initialContextFactory"),
|
||||
CONNECTION_URL("connectionURL"),
|
||||
CONNECTION_USERNAME("connectionUsername"),
|
||||
CONNECTION_PASSWORD("connectionPassword"),
|
||||
CONNECTION_PROTOCOL("connectionProtocol"),
|
||||
AUTHENTICATION("authentication"),
|
||||
USER_BASE("userBase"),
|
||||
USER_SEARCH_MATCHING("userSearchMatching"),
|
||||
USER_SEARCH_SUBTREE("userSearchSubtree"),
|
||||
ROLE_BASE("roleBase"),
|
||||
ROLE_NAME("roleName"),
|
||||
ROLE_SEARCH_MATCHING("roleSearchMatching"),
|
||||
ROLE_SEARCH_SUBTREE("roleSearchSubtree"),
|
||||
USER_ROLE_NAME("userRoleName"),
|
||||
EXPAND_ROLES("expandRoles"),
|
||||
EXPAND_ROLES_MATCHING("expandRolesMatching"),
|
||||
SASL_LOGIN_CONFIG_SCOPE("saslLoginConfigScope"),
|
||||
AUTHENTICATE_USER("authenticateUser"),
|
||||
REFERRAL("referral"),
|
||||
IGNORE_PARTIAL_RESULT_EXCEPTION("ignorePartialResultException"),
|
||||
PASSWORD_CODEC("passwordCodec"),
|
||||
CONNECTION_POOL("connectionPool"),
|
||||
CONNECTION_TIMEOUT("connectionTimeout"),
|
||||
READ_TIMEOUT("readTimeout");
|
||||
|
||||
private final String name;
|
||||
|
||||
ConfigKey(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
static boolean contains(String key) {
|
||||
for (ConfigKey k: values()) {
|
||||
if (k.name.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected DirContext context;
|
||||
|
||||
private Subject subject;
|
||||
private CallbackHandler handler;
|
||||
private LDAPLoginProperty[] config;
|
||||
private final Set<LDAPLoginProperty> config = new HashSet<>();
|
||||
private String username;
|
||||
private final Set<RolePrincipal> groups = new HashSet<>();
|
||||
private boolean userAuthenticated = false;
|
||||
|
@ -113,37 +135,20 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
this.subject = subject;
|
||||
this.handler = callbackHandler;
|
||||
|
||||
config = new LDAPLoginProperty[]{new LDAPLoginProperty(INITIAL_CONTEXT_FACTORY, (String) options.get(INITIAL_CONTEXT_FACTORY)),
|
||||
new LDAPLoginProperty(CONNECTION_URL, (String) options.get(CONNECTION_URL)),
|
||||
new LDAPLoginProperty(CONNECTION_USERNAME, (String) options.get(CONNECTION_USERNAME)),
|
||||
new LDAPLoginProperty(CONNECTION_PASSWORD, (String) options.get(CONNECTION_PASSWORD)),
|
||||
new LDAPLoginProperty(CONNECTION_PROTOCOL, (String) options.get(CONNECTION_PROTOCOL)),
|
||||
new LDAPLoginProperty(AUTHENTICATION, (String) options.get(AUTHENTICATION)),
|
||||
new LDAPLoginProperty(USER_BASE, (String) options.get(USER_BASE)),
|
||||
new LDAPLoginProperty(USER_SEARCH_MATCHING, (String) options.get(USER_SEARCH_MATCHING)),
|
||||
new LDAPLoginProperty(USER_SEARCH_SUBTREE, (String) options.get(USER_SEARCH_SUBTREE)),
|
||||
new LDAPLoginProperty(ROLE_BASE, (String) options.get(ROLE_BASE)),
|
||||
new LDAPLoginProperty(ROLE_NAME, (String) options.get(ROLE_NAME)),
|
||||
new LDAPLoginProperty(ROLE_SEARCH_MATCHING, (String) options.get(ROLE_SEARCH_MATCHING)),
|
||||
new LDAPLoginProperty(ROLE_SEARCH_SUBTREE, (String) options.get(ROLE_SEARCH_SUBTREE)),
|
||||
new LDAPLoginProperty(USER_ROLE_NAME, (String) options.get(USER_ROLE_NAME)),
|
||||
new LDAPLoginProperty(EXPAND_ROLES, (String) options.get(EXPAND_ROLES)),
|
||||
new LDAPLoginProperty(EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING)),
|
||||
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(IGNORE_PARTIAL_RESULT_EXCEPTION, (String) options.get(IGNORE_PARTIAL_RESULT_EXCEPTION)),
|
||||
new LDAPLoginProperty(CONNECTION_POOL, (String) options.get(CONNECTION_POOL)),
|
||||
new LDAPLoginProperty(CONNECTION_TIMEOUT, (String) options.get(CONNECTION_TIMEOUT)),
|
||||
new LDAPLoginProperty(READ_TIMEOUT, (String) options.get(READ_TIMEOUT))};
|
||||
|
||||
if (isLoginPropertySet(AUTHENTICATE_USER)) {
|
||||
authenticateUser = Boolean.valueOf(getLDAPPropertyValue(AUTHENTICATE_USER));
|
||||
// copy all options to config, ignoring non-string entries
|
||||
config.clear();
|
||||
for (Map.Entry<String, ?> entry : options.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
config.add(new LDAPLoginProperty(entry.getKey(), (String) entry.getValue()));
|
||||
}
|
||||
isRoleAttributeSet = isLoginPropertySet(ROLE_NAME);
|
||||
roleAttributeName = getLDAPPropertyValue(ROLE_NAME);
|
||||
codecClass = getLDAPPropertyValue(PASSWORD_CODEC);
|
||||
}
|
||||
|
||||
if (isLoginPropertySet(ConfigKey.AUTHENTICATE_USER)) {
|
||||
authenticateUser = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.AUTHENTICATE_USER));
|
||||
}
|
||||
isRoleAttributeSet = isLoginPropertySet(ConfigKey.ROLE_NAME);
|
||||
roleAttributeName = getLDAPPropertyValue(ConfigKey.ROLE_NAME);
|
||||
codecClass = getLDAPPropertyValue(ConfigKey.PASSWORD_CODEC);
|
||||
}
|
||||
|
||||
private String getPlainPassword(String password) {
|
||||
|
@ -174,13 +179,15 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
String password = null;
|
||||
|
||||
username = ((NameCallback) callbacks[0]).getName();
|
||||
if (username == null)
|
||||
if (username == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((PasswordCallback) callbacks[1]).getPassword() != null)
|
||||
if (((PasswordCallback) callbacks[1]).getPassword() != null) {
|
||||
password = new String(((PasswordCallback) callbacks[1]).getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* https://tools.ietf.org/html/rfc4513#section-6.3.1
|
||||
*
|
||||
* Clients that use the results from a simple Bind operation to make
|
||||
|
@ -188,8 +195,9 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
* requests (by verifying that the supplied password is not empty) and
|
||||
* react appropriately.
|
||||
*/
|
||||
if (password == null || password.length() == 0)
|
||||
if (password == null || password.length() == 0) {
|
||||
throw new FailedLoginException("Password cannot be null or empty");
|
||||
}
|
||||
|
||||
// authenticate will throw LoginException
|
||||
// in case of failed authentication
|
||||
|
@ -227,9 +235,8 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
}
|
||||
|
||||
for (RolePrincipal gp : groups) {
|
||||
principals.add(gp);
|
||||
}
|
||||
principals.addAll(groups);
|
||||
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
@ -259,7 +266,6 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
|
||||
protected boolean authenticate(String username, String password) throws LoginException {
|
||||
|
||||
List<String> roles = new ArrayList<>();
|
||||
try {
|
||||
String dn = resolveDN(username, roles);
|
||||
|
@ -271,11 +277,6 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
} else {
|
||||
throw new FailedLoginException("Password does not match for user: " + username);
|
||||
}
|
||||
} catch (CommunicationException e) {
|
||||
closeContext();
|
||||
FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
} catch (NamingException e) {
|
||||
closeContext();
|
||||
FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
|
||||
|
@ -299,10 +300,6 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
private String resolveDN(String username, List<String> roles) throws FailedLoginException {
|
||||
String dn = null;
|
||||
|
||||
MessageFormat userSearchMatchingFormat;
|
||||
boolean userSearchSubtreeBool;
|
||||
boolean ignorePartialResultExceptionBool;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Create the LDAP initial context.");
|
||||
}
|
||||
|
@ -314,13 +311,13 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
throw ex;
|
||||
}
|
||||
|
||||
if (!isLoginPropertySet(USER_SEARCH_MATCHING)) {
|
||||
if (!isLoginPropertySet(ConfigKey.USER_SEARCH_MATCHING)) {
|
||||
return username;
|
||||
}
|
||||
|
||||
userSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(USER_SEARCH_MATCHING));
|
||||
userSearchSubtreeBool = Boolean.valueOf(getLDAPPropertyValue(USER_SEARCH_SUBTREE)).booleanValue();
|
||||
ignorePartialResultExceptionBool = Boolean.valueOf(getLDAPPropertyValue(IGNORE_PARTIAL_RESULT_EXCEPTION)).booleanValue();
|
||||
MessageFormat userSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(ConfigKey.USER_SEARCH_MATCHING));
|
||||
boolean userSearchSubtreeBool = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.USER_SEARCH_SUBTREE));
|
||||
boolean ignorePartialResultExceptionBool = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.IGNORE_PARTIAL_RESULT_EXCEPTION));
|
||||
|
||||
try {
|
||||
|
||||
|
@ -334,8 +331,8 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
|
||||
// setup attributes
|
||||
List<String> list = new ArrayList<>();
|
||||
if (isLoginPropertySet(USER_ROLE_NAME)) {
|
||||
list.add(getLDAPPropertyValue(USER_ROLE_NAME));
|
||||
if (isLoginPropertySet(ConfigKey.USER_ROLE_NAME)) {
|
||||
list.add(getLDAPPropertyValue(ConfigKey.USER_ROLE_NAME));
|
||||
}
|
||||
String[] attribs = new String[list.size()];
|
||||
list.toArray(attribs);
|
||||
|
@ -344,13 +341,13 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Get the user DN.");
|
||||
logger.debug("Looking for the user in LDAP with ");
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(USER_BASE));
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(ConfigKey.USER_BASE));
|
||||
logger.debug(" filter: " + filter);
|
||||
}
|
||||
|
||||
NamingEnumeration<SearchResult> results = null;
|
||||
try {
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(USER_BASE), filter, constraints));
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(ConfigKey.USER_BASE), filter, constraints));
|
||||
} catch (PrivilegedActionException e) {
|
||||
Exception cause = e.getException();
|
||||
FailedLoginException ex = new FailedLoginException("Error executing search query to resolve DN");
|
||||
|
@ -382,7 +379,7 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
|
||||
NameParser parser = context.getNameParser("");
|
||||
Name contextName = parser.parse(context.getNameInNamespace());
|
||||
Name baseName = parser.parse(getLDAPPropertyValue(USER_BASE));
|
||||
Name baseName = parser.parse(getLDAPPropertyValue(ConfigKey.USER_BASE));
|
||||
Name entryName = parser.parse(result.getName());
|
||||
Name name = contextName.addAll(baseName);
|
||||
name = name.addAll(entryName);
|
||||
|
@ -415,8 +412,8 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
if (attrs == null) {
|
||||
throw new FailedLoginException("User found, but LDAP entry malformed: " + username);
|
||||
}
|
||||
if (isLoginPropertySet(USER_ROLE_NAME)) {
|
||||
Attribute roleNames = attrs.get(getLDAPPropertyValue(USER_ROLE_NAME));
|
||||
if (isLoginPropertySet(ConfigKey.USER_ROLE_NAME)) {
|
||||
Attribute roleNames = attrs.get(getLDAPPropertyValue(ConfigKey.USER_ROLE_NAME));
|
||||
if (roleNames != null) {
|
||||
NamingEnumeration<?> e = roleNames.getAll();
|
||||
while (e.hasMore()) {
|
||||
|
@ -436,11 +433,6 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (CommunicationException e) {
|
||||
closeContext();
|
||||
FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
} catch (NamingException e) {
|
||||
closeContext();
|
||||
FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
|
||||
|
@ -456,18 +448,14 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
String username,
|
||||
List<String> currentRoles) throws NamingException {
|
||||
|
||||
if (!isLoginPropertySet(ROLE_SEARCH_MATCHING)) {
|
||||
if (!isLoginPropertySet(ConfigKey.ROLE_SEARCH_MATCHING)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageFormat roleSearchMatchingFormat;
|
||||
boolean roleSearchSubtreeBool;
|
||||
boolean expandRolesBool;
|
||||
boolean ignorePartialResultExceptionBool;
|
||||
roleSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(ROLE_SEARCH_MATCHING));
|
||||
roleSearchSubtreeBool = Boolean.valueOf(getLDAPPropertyValue(ROLE_SEARCH_SUBTREE)).booleanValue();
|
||||
expandRolesBool = Boolean.valueOf(getLDAPPropertyValue(EXPAND_ROLES)).booleanValue();
|
||||
ignorePartialResultExceptionBool = Boolean.valueOf(getLDAPPropertyValue(IGNORE_PARTIAL_RESULT_EXCEPTION)).booleanValue();
|
||||
MessageFormat roleSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(ConfigKey.ROLE_SEARCH_MATCHING));
|
||||
boolean roleSearchSubtreeBool = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.ROLE_SEARCH_SUBTREE));
|
||||
boolean expandRolesBool = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.EXPAND_ROLES));
|
||||
boolean ignorePartialResultExceptionBool = Boolean.parseBoolean(getLDAPPropertyValue(ConfigKey.IGNORE_PARTIAL_RESULT_EXCEPTION));
|
||||
|
||||
final String filter = roleSearchMatchingFormat.format(new String[]{doRFC2254Encoding(dn), doRFC2254Encoding(username)});
|
||||
|
||||
|
@ -480,14 +468,14 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Get user roles.");
|
||||
logger.debug("Looking for the user roles in LDAP with ");
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(ROLE_BASE));
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(ConfigKey.ROLE_BASE));
|
||||
logger.debug(" filter: " + filter);
|
||||
}
|
||||
HashSet<String> haveSeenNames = new HashSet<>();
|
||||
Queue<String> pendingNameExpansion = new LinkedList<>();
|
||||
NamingEnumeration<SearchResult> results = null;
|
||||
try {
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(ROLE_BASE), filter, constraints));
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(ConfigKey.ROLE_BASE), filter, constraints));
|
||||
} catch (PrivilegedActionException e) {
|
||||
Exception cause = e.getException();
|
||||
NamingException ex = new NamingException("Error executing search query to resolve roles");
|
||||
|
@ -513,18 +501,18 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
}
|
||||
if (expandRolesBool) {
|
||||
MessageFormat expandRolesMatchingFormat = new MessageFormat(getLDAPPropertyValue(EXPAND_ROLES_MATCHING));
|
||||
MessageFormat expandRolesMatchingFormat = new MessageFormat(getLDAPPropertyValue(ConfigKey.EXPAND_ROLES_MATCHING));
|
||||
while (!pendingNameExpansion.isEmpty()) {
|
||||
String name = pendingNameExpansion.remove();
|
||||
final String expandFilter = expandRolesMatchingFormat.format(new String[]{name});
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Get 'expanded' user roles.");
|
||||
logger.debug("Looking for the 'expanded' user roles in LDAP with ");
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(ROLE_BASE));
|
||||
logger.debug(" base DN: " + getLDAPPropertyValue(ConfigKey.ROLE_BASE));
|
||||
logger.debug(" filter: " + expandFilter);
|
||||
}
|
||||
try {
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(ROLE_BASE), expandFilter, constraints));
|
||||
results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration<SearchResult>>) () -> context.search(getLDAPPropertyValue(ConfigKey.ROLE_BASE), expandFilter, constraints));
|
||||
} catch (PrivilegedActionException e) {
|
||||
Exception cause = e.getException();
|
||||
NamingException ex = new NamingException("Error executing search query to expand roles");
|
||||
|
@ -554,7 +542,7 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
|
||||
protected String doRFC2254Encoding(String inputString) {
|
||||
StringBuffer buf = new StringBuffer(inputString.length());
|
||||
StringBuilder buf = new StringBuilder(inputString.length());
|
||||
for (int i = 0; i < inputString.length(); i++) {
|
||||
char c = inputString.charAt(i);
|
||||
switch (c) {
|
||||
|
@ -603,17 +591,17 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
}
|
||||
|
||||
if (isLoginPropertySet(CONNECTION_USERNAME)) {
|
||||
context.addToEnvironment(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(CONNECTION_USERNAME));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_USERNAME)) {
|
||||
context.addToEnvironment(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(ConfigKey.CONNECTION_USERNAME));
|
||||
} else {
|
||||
context.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
|
||||
}
|
||||
if (isLoginPropertySet(CONNECTION_PASSWORD)) {
|
||||
context.addToEnvironment(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(CONNECTION_PASSWORD)));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_PASSWORD)) {
|
||||
context.addToEnvironment(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(ConfigKey.CONNECTION_PASSWORD)));
|
||||
} else {
|
||||
context.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
|
||||
}
|
||||
context.addToEnvironment(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(AUTHENTICATION));
|
||||
context.addToEnvironment(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(ConfigKey.AUTHENTICATION));
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
@ -634,25 +622,25 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
if (context == null) {
|
||||
try {
|
||||
Hashtable<String, String> env = new Hashtable<>();
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, getLDAPPropertyValue(INITIAL_CONTEXT_FACTORY));
|
||||
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));
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, getLDAPPropertyValue(ConfigKey.INITIAL_CONTEXT_FACTORY));
|
||||
env.put(Context.SECURITY_PROTOCOL, getLDAPPropertyValue(ConfigKey.CONNECTION_PROTOCOL));
|
||||
env.put(Context.PROVIDER_URL, getLDAPPropertyValue(ConfigKey.CONNECTION_URL));
|
||||
env.put(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(ConfigKey.AUTHENTICATION));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_POOL)) {
|
||||
env.put("com.sun.jndi.ldap.connect.pool", getLDAPPropertyValue(ConfigKey.CONNECTION_POOL));
|
||||
}
|
||||
if (isLoginPropertySet(CONNECTION_TIMEOUT)) {
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", getLDAPPropertyValue(CONNECTION_TIMEOUT));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_TIMEOUT)) {
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", getLDAPPropertyValue(ConfigKey.CONNECTION_TIMEOUT));
|
||||
}
|
||||
if (isLoginPropertySet(READ_TIMEOUT)) {
|
||||
env.put("com.sun.jndi.ldap.read.timeout", getLDAPPropertyValue(READ_TIMEOUT));
|
||||
if (isLoginPropertySet(ConfigKey.READ_TIMEOUT)) {
|
||||
env.put("com.sun.jndi.ldap.read.timeout", getLDAPPropertyValue(ConfigKey.READ_TIMEOUT));
|
||||
}
|
||||
|
||||
// handle LDAP referrals
|
||||
// valid values are "throw", "ignore" and "follow"
|
||||
String referral = "ignore";
|
||||
if (getLDAPPropertyValue(REFERRAL) != null) {
|
||||
referral = getLDAPPropertyValue(REFERRAL);
|
||||
if (getLDAPPropertyValue(ConfigKey.REFERRAL) != null) {
|
||||
referral = getLDAPPropertyValue(ConfigKey.REFERRAL);
|
||||
}
|
||||
|
||||
env.put(Context.REFERRAL, referral);
|
||||
|
@ -660,9 +648,9 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
logger.debug("Referral handling: " + referral);
|
||||
}
|
||||
|
||||
if ("GSSAPI".equalsIgnoreCase(getLDAPPropertyValue(AUTHENTICATION))) {
|
||||
if ("GSSAPI".equalsIgnoreCase(getLDAPPropertyValue(ConfigKey.AUTHENTICATION))) {
|
||||
|
||||
final String configScope = isLoginPropertySet(SASL_LOGIN_CONFIG_SCOPE) ? getLDAPPropertyValue(SASL_LOGIN_CONFIG_SCOPE) : "broker-sasl-gssapi";
|
||||
final String configScope = isLoginPropertySet(ConfigKey.SASL_LOGIN_CONFIG_SCOPE) ? getLDAPPropertyValue(ConfigKey.SASL_LOGIN_CONFIG_SCOPE) : "broker-sasl-gssapi";
|
||||
try {
|
||||
LoginContext loginContext = new LoginContext(configScope);
|
||||
loginContext.login();
|
||||
|
@ -676,19 +664,21 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
|
||||
} else {
|
||||
|
||||
if (isLoginPropertySet(CONNECTION_USERNAME)) {
|
||||
env.put(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(CONNECTION_USERNAME));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_USERNAME)) {
|
||||
env.put(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(ConfigKey.CONNECTION_USERNAME));
|
||||
} else {
|
||||
throw new NamingException("Empty username is not allowed");
|
||||
}
|
||||
|
||||
if (isLoginPropertySet(CONNECTION_PASSWORD)) {
|
||||
env.put(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(CONNECTION_PASSWORD)));
|
||||
if (isLoginPropertySet(ConfigKey.CONNECTION_PASSWORD)) {
|
||||
env.put(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(ConfigKey.CONNECTION_PASSWORD)));
|
||||
} else {
|
||||
throw new NamingException("Empty password is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
extendInitialEnvironment(config, env);
|
||||
|
||||
try {
|
||||
context = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction<DirContext>) () -> new InitialDirContext(env));
|
||||
} catch (PrivilegedActionException e) {
|
||||
|
@ -703,18 +693,32 @@ public class LDAPLoginModule implements AuditLoginModule {
|
|||
}
|
||||
}
|
||||
|
||||
private String getLDAPPropertyValue(String propertyName) {
|
||||
protected void extendInitialEnvironment(Set<LDAPLoginProperty> moduleConfig, Hashtable<String, String> initialContextEnv) {
|
||||
// sub-classes may override the method if the default implementation is not sufficient:
|
||||
// add all non-module configs to initial DirContext environment to support passing
|
||||
// any custom/future property to InitialDirContext construction
|
||||
for (LDAPLoginProperty prop: moduleConfig) {
|
||||
String propName = prop.getPropertyName();
|
||||
if (initialContextEnv.get(propName) == null && !ConfigKey.contains(propName)) {
|
||||
initialContextEnv.put(propName, prop.getPropertyValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getLDAPPropertyValue(ConfigKey key) {
|
||||
for (LDAPLoginProperty conf : config)
|
||||
if (conf.getPropertyName().equals(propertyName))
|
||||
if (conf.getPropertyName().equals(key.getName())) {
|
||||
return conf.getPropertyValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isLoginPropertySet(String propertyName) {
|
||||
private boolean isLoginPropertySet(ConfigKey key) {
|
||||
for (LDAPLoginProperty conf : config) {
|
||||
if (conf.getPropertyName().equals(propertyName) && (conf.getPropertyValue() != null && !"".equals(conf.getPropertyValue())))
|
||||
if (conf.getPropertyName().equals(key.getName()) && (conf.getPropertyValue() != null && !"".equals(conf.getPropertyValue()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.core.security.jaas;
|
||||
package org.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NameClassPair;
|
||||
|
@ -37,14 +37,12 @@ import java.lang.reflect.Modifier;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Set;
|
||||
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;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginProperty;
|
||||
import org.apache.directory.server.annotations.CreateLdapServer;
|
||||
import org.apache.directory.server.annotations.CreateTransport;
|
||||
import org.apache.directory.server.core.annotations.ApplyLdifFiles;
|
||||
|
@ -309,9 +307,9 @@ public class LDAPLoginModuleTest extends AbstractLdapTestUnit {
|
|||
}
|
||||
loginModule.initialize(new Subject(), callbackHandler, null, options);
|
||||
|
||||
LDAPLoginProperty[] ldapProps = (LDAPLoginProperty[]) configMap.get(loginModule);
|
||||
Set<LDAPLoginProperty> ldapProps = (Set<LDAPLoginProperty>) configMap.get(loginModule);
|
||||
for (String key: options.keySet()) {
|
||||
assertTrue("val set: " + key, presentInArray(ldapProps, key));
|
||||
assertTrue("val set: " + key, presentIn(ldapProps, key));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,8 +362,81 @@ public class LDAPLoginModuleTest extends AbstractLdapTestUnit {
|
|||
}
|
||||
context.logout();
|
||||
}
|
||||
@Test
|
||||
public void testEnvironmentProperties() throws Exception {
|
||||
HashMap<String, Object> options = new HashMap<>();
|
||||
|
||||
private boolean presentInArray(LDAPLoginProperty[] ldapProps, String propertyName) {
|
||||
// set module configs
|
||||
for (LDAPLoginModule.ConfigKey configKey: LDAPLoginModule.ConfigKey.values()) {
|
||||
if (configKey.getName().equals("initialContextFactory")) {
|
||||
options.put(configKey.getName(), "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
} else if (configKey.getName().equals("connectionURL")) {
|
||||
options.put(configKey.getName(), "ldap://localhost:1024");
|
||||
} else if (configKey.getName().equals("referral")) {
|
||||
options.put(configKey.getName(), "ignore");
|
||||
} else if (configKey.getName().equals("connectionTimeout")) {
|
||||
options.put(configKey.getName(), "10000");
|
||||
} else if (configKey.getName().equals("readTimeout")) {
|
||||
options.put(configKey.getName(), "11000");
|
||||
} else if (configKey.getName().equals("authentication")) {
|
||||
options.put(configKey.getName(), "simple");
|
||||
} else if (configKey.getName().equals("connectionUsername")) {
|
||||
options.put(configKey.getName(), PRINCIPAL);
|
||||
} else if (configKey.getName().equals("connectionPassword")) {
|
||||
options.put(configKey.getName(), CREDENTIALS);
|
||||
} else if (configKey.getName().equals("connectionProtocol")) {
|
||||
options.put(configKey.getName(), "s");
|
||||
} else if (configKey.getName().equals("debug")) {
|
||||
options.put(configKey.getName(), "true");
|
||||
} else {
|
||||
options.put(configKey.getName(), configKey.getName() + "_value_set");
|
||||
}
|
||||
}
|
||||
|
||||
// add extra configs
|
||||
options.put("com.sun.jndi.ldap.tls.cbtype", "tls-server-end-point");
|
||||
options.put("randomConfig", "some-value");
|
||||
|
||||
// add non-strings configs
|
||||
options.put("non.string.1", new Object());
|
||||
options.put("non.string.2", 1);
|
||||
|
||||
// create context
|
||||
LDAPLoginModule loginModule = new LDAPLoginModule();
|
||||
loginModule.initialize(new Subject(), null, null, options);
|
||||
loginModule.openContext();
|
||||
|
||||
// get created environment
|
||||
Hashtable<?, ?> environment = loginModule.context.getEnvironment();
|
||||
// cleanup
|
||||
loginModule.closeContext();
|
||||
|
||||
// module config keys should not be passed to environment
|
||||
for (LDAPLoginModule.ConfigKey configKey: LDAPLoginModule.ConfigKey.values()) {
|
||||
assertEquals("value should not be set for key: " + configKey.getName(), null, environment.get(configKey.getName()));
|
||||
}
|
||||
|
||||
// extra, non-module configs should be passed to environment
|
||||
assertEquals("value should be set for key: " + "com.sun.jndi.ldap.tls.cbtype", "tls-server-end-point", environment.get("com.sun.jndi.ldap.tls.cbtype"));
|
||||
assertEquals("value should be set for key: " + "randomConfig", "some-value", environment.get("randomConfig"));
|
||||
|
||||
// non-string configs should not be passed to environment
|
||||
assertEquals("value should not be set for key: " + "non.string.1", null, environment.get("non.string.1"));
|
||||
assertEquals("value should not be set for key: " + "non.string.2", null, environment.get("non.string.2"));
|
||||
|
||||
// environment configs should be set
|
||||
assertEquals("value should be set for key: " + Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory", environment.get(Context.INITIAL_CONTEXT_FACTORY));
|
||||
assertEquals("value should be set for key: " + Context.PROVIDER_URL, "ldap://localhost:1024", environment.get(Context.PROVIDER_URL));
|
||||
assertEquals("value should be set for key: " + Context.REFERRAL, "ignore", environment.get(Context.REFERRAL));
|
||||
assertEquals("value should be set for key: " + "com.sun.jndi.ldap.connect.timeout", "10000", environment.get("com.sun.jndi.ldap.connect.timeout"));
|
||||
assertEquals("value should be set for key: " + "com.sun.jndi.ldap.read.timeout", "11000", environment.get("com.sun.jndi.ldap.read.timeout"));
|
||||
assertEquals("value should be set for key: " + Context.SECURITY_AUTHENTICATION, "simple", environment.get(Context.SECURITY_AUTHENTICATION));
|
||||
assertEquals("value should be set for key: " + Context.SECURITY_PRINCIPAL, PRINCIPAL, environment.get(Context.SECURITY_PRINCIPAL));
|
||||
assertEquals("value should be set for key: " + Context.SECURITY_CREDENTIALS, CREDENTIALS, environment.get(Context.SECURITY_CREDENTIALS));
|
||||
assertEquals("value should be set for key: " + Context.SECURITY_PROTOCOL, "s", environment.get(Context.SECURITY_PROTOCOL));
|
||||
}
|
||||
|
||||
private boolean presentIn(Set<LDAPLoginProperty> ldapProps, String propertyName) {
|
||||
for (LDAPLoginProperty conf : ldapProps) {
|
||||
if (conf.getPropertyName().equals(propertyName) && (conf.getPropertyValue() != null && !"".equals(conf.getPropertyValue())))
|
||||
return true;
|
|
@ -819,6 +819,9 @@ system. It is implemented by
|
|||
testing or debugging; normally, it should be set to `false`, or omitted;
|
||||
default is `false`
|
||||
|
||||
Any additional configuration option not recognized by the LDAP login module itself
|
||||
is passed as-is to the underlying LDAP connection logic.
|
||||
|
||||
Add user entries under the node specified by the `userBase` option. When
|
||||
creating a new user entry in the directory, choose an object class that
|
||||
supports the `userPassword` attribute (for example, the `person` or
|
||||
|
|
Loading…
Reference in New Issue