Ensure we always respect a user specified filter in the AD realm (elastic/x-pack-elasticsearch#1161)
When the active directory realm was refactored to add support for authenticating against multiple domains, only the default authenticator respected the user_search.filter setting. This commit moves this down to the base authenticator and also changes the UPN filter to not include sAMAccountName in the filter. Original commit: elastic/x-pack-elasticsearch@d2c19c9bee
This commit is contained in:
parent
4f12f04c65
commit
f7fb02f21f
|
@ -194,6 +194,21 @@ operation are supported: failover and load balancing
|
|||
must be a valid LDAP user search filter, for example
|
||||
`(&(objectClass=user)(sAMAccountName={0}))`. For more
|
||||
information, see https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx[Search Filter Syntax].
|
||||
| `user_search.upn_filter` | no | Specifies a filter to use to lookup a user given a user principal name.
|
||||
The default filter looks up `user` objects with either
|
||||
a matching `userPrincipalName` or a `sAMAccountName` matching the account
|
||||
portion of the user principal name. If specified, this
|
||||
must be a valid LDAP user search filter, for example
|
||||
`(&(objectClass=user)(sAMAccountName={0}))`. `{0}` is the value
|
||||
preceding the `@` sign in the user principal name and `{1}` is
|
||||
the full user principal name provided by the user. For more
|
||||
information, see https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx[Search Filter Syntax].
|
||||
| `user_search.down_level_filter` | no | Specifies a filter to use to lookup a user given a down level logon name (DOMAIN\user).
|
||||
The default filter looks up `user` objects with a matching
|
||||
`sAMAccountName` in the domain provided. If specified, this
|
||||
must be a valid LDAP user search filter, for example
|
||||
`(&(objectClass=user)(sAMAccountName={0}))`. For more
|
||||
information, see https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx[Search Filter Syntax].
|
||||
| `group_search.base_dn` | no | Specifies the context to search for groups in which the user
|
||||
has membership. Defaults to the root of the Active Directory
|
||||
domain.
|
||||
|
|
|
@ -355,6 +355,23 @@ Specifies a filter to use to lookup a user given a username. The default
|
|||
filter looks up `user` objects with either `sAMAccountName` or
|
||||
`userPrincipalName`.
|
||||
|
||||
`user_search.upn_filter`::
|
||||
Specifies a filter to use to lookup a user given a user principal name.
|
||||
The default filter looks up `user` objects with either
|
||||
a matching `userPrincipalName` or a `sAMAccountName` matching the account
|
||||
portion of the user principal name. If specified, this
|
||||
must be a valid LDAP user search filter, for example
|
||||
`(&(objectClass=user)(sAMAccountName={0}))`. `{0}` is the value preceding the
|
||||
`@` sign in the user principal name and `{1}` is the full user principal name
|
||||
provided by the user.
|
||||
|
||||
`user_search.down_level_filter`::
|
||||
Specifies a filter to use to lookup a user given a down level logon name
|
||||
(DOMAIN\user). The default filter looks up `user` objects with a matching
|
||||
`sAMAccountName` in the domain provided. If specified, this
|
||||
must be a valid LDAP user search filter, for example
|
||||
`(&(objectClass=user)(sAMAccountName={0}))`.
|
||||
|
||||
`group_search.base_dn`::
|
||||
The context to search for groups in which the user has membership. Defaults
|
||||
to the root of the Active Directory domain.
|
||||
|
|
|
@ -53,12 +53,14 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
static final String AD_GROUP_SEARCH_SCOPE_SETTING = "group_search.scope";
|
||||
static final String AD_USER_SEARCH_BASEDN_SETTING = "user_search.base_dn";
|
||||
static final String AD_USER_SEARCH_FILTER_SETTING = "user_search.filter";
|
||||
static final String AD_UPN_USER_SEARCH_FILTER_SETTING = "user_search.upn_filter";
|
||||
static final String AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING = "user_search.down_level_filter";
|
||||
static final String AD_USER_SEARCH_SCOPE_SETTING = "user_search.scope";
|
||||
private static final String NETBIOS_NAME_FILTER_TEMPLATE = "(netbiosname={0})";
|
||||
|
||||
private final DefaultADAuthenticator defaultADAuthenticator;
|
||||
private final DownLevelADAuthenticator downLevelADAuthenticator;
|
||||
private final UpnADAuthenticator upnADAuthenticator;
|
||||
final DefaultADAuthenticator defaultADAuthenticator;
|
||||
final DownLevelADAuthenticator downLevelADAuthenticator;
|
||||
final UpnADAuthenticator upnADAuthenticator;
|
||||
|
||||
ActiveDirectorySessionFactory(RealmConfig config, SSLService sslService) {
|
||||
super(config, sslService);
|
||||
|
@ -124,6 +126,8 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
settings.add(Setting.simpleString(AD_GROUP_SEARCH_SCOPE_SETTING, Setting.Property.NodeScope));
|
||||
settings.add(Setting.simpleString(AD_USER_SEARCH_BASEDN_SETTING, Setting.Property.NodeScope));
|
||||
settings.add(Setting.simpleString(AD_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
|
||||
settings.add(Setting.simpleString(AD_UPN_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
|
||||
settings.add(Setting.simpleString(AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
|
||||
settings.add(Setting.simpleString(AD_USER_SEARCH_SCOPE_SETTING, Setting.Property.NodeScope));
|
||||
return settings;
|
||||
}
|
||||
|
@ -145,15 +149,18 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
final GroupsResolver groupsResolver;
|
||||
final String userSearchDN;
|
||||
final LdapSearchScope userSearchScope;
|
||||
final String userSearchFilter;
|
||||
|
||||
ADAuthenticator(Settings settings, TimeValue timeout, boolean ignoreReferralErrors,
|
||||
Logger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
Logger logger, GroupsResolver groupsResolver, String domainDN, String userSearchFilterSetting,
|
||||
String defaultUserSearchFilter) {
|
||||
this.timeout = timeout;
|
||||
this.ignoreReferralErrors = ignoreReferralErrors;
|
||||
this.logger = logger;
|
||||
this.groupsResolver = groupsResolver;
|
||||
userSearchDN = settings.get(AD_USER_SEARCH_BASEDN_SETTING, domainDN);
|
||||
userSearchScope = LdapSearchScope.resolve(settings.get(AD_USER_SEARCH_SCOPE_SETTING), LdapSearchScope.SUB_TREE);
|
||||
userSearchFilter = settings.get(userSearchFilterSetting, defaultUserSearchFilter);
|
||||
}
|
||||
|
||||
final void authenticate(LDAPConnection connection, String username, SecureString password,
|
||||
|
@ -189,6 +196,11 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
return username;
|
||||
}
|
||||
|
||||
// pkg-private for testing
|
||||
final String getUserSearchFilter() {
|
||||
return userSearchFilter;
|
||||
}
|
||||
|
||||
abstract void searchForDN(LDAPConnection connection, String username, SecureString password, int timeLimitSeconds,
|
||||
ActionListener<SearchResultEntry> listener);
|
||||
}
|
||||
|
@ -200,15 +212,13 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
*/
|
||||
static class DefaultADAuthenticator extends ADAuthenticator {
|
||||
|
||||
final String userSearchFilter;
|
||||
|
||||
final String domainName;
|
||||
|
||||
DefaultADAuthenticator(Settings settings, TimeValue timeout, boolean ignoreReferralErrors,
|
||||
Logger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
super(settings, timeout, ignoreReferralErrors, logger, groupsResolver, domainDN);
|
||||
super(settings, timeout, ignoreReferralErrors, logger, groupsResolver, domainDN, AD_USER_SEARCH_FILTER_SETTING,
|
||||
"(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={0}@" + settings.get(AD_DOMAIN_NAME_SETTING) + ")))");
|
||||
domainName = settings.get(AD_DOMAIN_NAME_SETTING);
|
||||
userSearchFilter = settings.get(AD_USER_SEARCH_FILTER_SETTING, "(&(objectClass=user)(|(sAMAccountName={0})" +
|
||||
"(userPrincipalName={0}@" + domainName + ")))");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -235,6 +245,7 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
* this class contains the logic necessary to authenticate this form of a username
|
||||
*/
|
||||
static class DownLevelADAuthenticator extends ADAuthenticator {
|
||||
static final String DOWN_LEVEL_FILTER = "(&(objectClass=user)(sAMAccountName={0}))";
|
||||
Cache<String, String> domainNameCache = CacheBuilder.<String, String>builder().setMaximumWeight(100).build();
|
||||
|
||||
final String domainDN;
|
||||
|
@ -246,8 +257,8 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
boolean ignoreReferralErrors, Logger logger,
|
||||
GroupsResolver groupsResolver, String domainDN,
|
||||
SSLService sslService) {
|
||||
super(config.settings(), timeout, ignoreReferralErrors, logger, groupsResolver,
|
||||
domainDN);
|
||||
super(config.settings(), timeout, ignoreReferralErrors, logger, groupsResolver, domainDN,
|
||||
AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, DOWN_LEVEL_FILTER);
|
||||
this.domainDN = domainDN;
|
||||
this.settings = config.settings();
|
||||
this.sslService = sslService;
|
||||
|
@ -269,7 +280,7 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
} else {
|
||||
try {
|
||||
searchForEntry(connection, domainDN, LdapSearchScope.SUB_TREE.scope(),
|
||||
createFilter("(&(objectClass=user)(sAMAccountName={0}))",
|
||||
createFilter(userSearchFilter,
|
||||
accountName), timeLimitSeconds, ignoreReferralErrors,
|
||||
listener, attributesToSearchFor(groupsResolver.attributes()));
|
||||
} catch (LDAPException e) {
|
||||
|
@ -375,11 +386,12 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
|
||||
static class UpnADAuthenticator extends ADAuthenticator {
|
||||
|
||||
private static final String UPN_USER_FILTER = "(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={1})))";
|
||||
static final String UPN_USER_FILTER = "(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={1})))";
|
||||
|
||||
UpnADAuthenticator(Settings settings, TimeValue timeout, boolean ignoreReferralErrors,
|
||||
Logger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
super(settings, timeout, ignoreReferralErrors, logger, groupsResolver, domainDN);
|
||||
super(settings, timeout, ignoreReferralErrors, logger, groupsResolver, domainDN,
|
||||
AD_UPN_USER_SEARCH_FILTER_SETTING, UPN_USER_FILTER);
|
||||
}
|
||||
|
||||
void searchForDN(LDAPConnection connection, String username, SecureString password, int timeLimitSeconds,
|
||||
|
|
|
@ -299,10 +299,9 @@ public final class LdapUtils {
|
|||
);
|
||||
}
|
||||
|
||||
public static Filter createFilter(String filterTemplate, String... arguments)
|
||||
throws LDAPException {
|
||||
public static Filter createFilter(String filterTemplate, String... arguments) throws LDAPException {
|
||||
return Filter.create(new MessageFormat(filterTemplate, Locale.ROOT)
|
||||
.format((Object[]) encodeFilterValues(arguments), new StringBuffer(), null)
|
||||
.format(encodeFilterValues(arguments), new StringBuffer(), null)
|
||||
.toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import org.elasticsearch.common.settings.SecureString;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.DownLevelADAuthenticator;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.UpnADAuthenticator;
|
||||
import org.elasticsearch.xpack.security.user.User;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||
|
@ -269,7 +271,8 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
.put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
|
||||
.put("load_balance.type", loadBalanceType)
|
||||
.build());
|
||||
RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings, new Environment(globalSettings), new ThreadContext(globalSettings));
|
||||
RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings, new Environment(globalSettings),
|
||||
new ThreadContext(globalSettings));
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(LdapRealm.AD_TYPE, config, resourceWatcherService);
|
||||
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
||||
|
@ -283,6 +286,31 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
assertThat(stats, hasEntry("load_balance_type", loadBalanceType));
|
||||
}
|
||||
|
||||
public void testDefaultSearchFilters() throws Exception {
|
||||
Settings settings = settings();
|
||||
RealmConfig config = new RealmConfig("testDefaultSearchFilters", settings, globalSettings, new Environment(globalSettings),
|
||||
new ThreadContext(globalSettings));
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
|
||||
assertEquals("(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={0}@ad.test.elasticsearch.com)))",
|
||||
sessionFactory.defaultADAuthenticator.getUserSearchFilter());
|
||||
assertEquals(UpnADAuthenticator.UPN_USER_FILTER, sessionFactory.upnADAuthenticator.getUserSearchFilter());
|
||||
assertEquals(DownLevelADAuthenticator.DOWN_LEVEL_FILTER, sessionFactory.downLevelADAuthenticator.getUserSearchFilter());
|
||||
}
|
||||
|
||||
public void testCustomSearchFilters() throws Exception {
|
||||
Settings settings = settings(Settings.builder()
|
||||
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_FILTER_SETTING, "(objectClass=default)")
|
||||
.put(ActiveDirectorySessionFactory.AD_UPN_USER_SEARCH_FILTER_SETTING, "(objectClass=upn)")
|
||||
.put(ActiveDirectorySessionFactory.AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, "(objectClass=down level)")
|
||||
.build());
|
||||
RealmConfig config = new RealmConfig("testDefaultSearchFilters", settings, globalSettings, new Environment(globalSettings),
|
||||
new ThreadContext(globalSettings));
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
|
||||
assertEquals("(objectClass=default)", sessionFactory.defaultADAuthenticator.getUserSearchFilter());
|
||||
assertEquals("(objectClass=upn)", sessionFactory.upnADAuthenticator.getUserSearchFilter());
|
||||
assertEquals("(objectClass=down level)", sessionFactory.downLevelADAuthenticator.getUserSearchFilter());
|
||||
}
|
||||
|
||||
private Settings settings() throws Exception {
|
||||
return settings(Settings.EMPTY);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue