Fix AD realm additional metadata (#47179)

Due to a regression bug the metadata Active Directory realm
setting is ignored (it works correctly for the LDAP realm type).
This commit redresses it.

Closes #45848
This commit is contained in:
Albert Zaharovits 2019-10-01 17:05:24 +03:00 committed by GitHub
parent f792dbf239
commit 78558a7b2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 93 additions and 23 deletions

View File

@ -46,7 +46,7 @@ public final class LdapRealmSettings {
settings.addAll(LdapUserSearchSessionFactorySettings.getSettings());
settings.addAll(DelegatedAuthorizationSettings.getSettings(type));
}
settings.addAll(LdapMetaDataResolverSettings.getSettings());
settings.addAll(LdapMetaDataResolverSettings.getSettings(type));
settings.addAll(RealmSettings.getStandardSettings(type));
return settings;
}

View File

@ -7,20 +7,18 @@ package org.elasticsearch.xpack.core.security.authc.ldap.support;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
public final class LdapMetaDataResolverSettings {
public static final Setting.AffixSetting<List<String>> ADDITIONAL_META_DATA_SETTING = Setting.affixKeySetting(
RealmSettings.realmSettingPrefix(LdapRealmSettings.LDAP_TYPE), "metadata",
key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Setting.Property.NodeScope));
public static final Function<String, Setting.AffixSetting<List<String>>> ADDITIONAL_META_DATA_SETTING = RealmSettings.affixSetting(
"metadata", key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Setting.Property.NodeScope));
private LdapMetaDataResolverSettings() {}
public static List<Setting.AffixSetting<?>> getSettings() {
return Collections.singletonList(ADDITIONAL_META_DATA_SETTING);
public static List<Setting.AffixSetting<?>> getSettings(String type) {
return Collections.singletonList(ADDITIONAL_META_DATA_SETTING.apply(type));
}
}

View File

@ -9,6 +9,7 @@ import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.SimpleBindRequest;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
@ -18,9 +19,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.LdapSessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.ldap.SearchGroupsResolverSettings;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapMetaDataResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils;
@ -41,7 +40,6 @@ public class LdapSessionFactory extends SessionFactory {
private final String[] userDnTemplates;
private final GroupsResolver groupResolver;
private final LdapMetaDataResolver metaDataResolver;
public LdapSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) {
super(config, sslService, threadPool);
@ -52,7 +50,6 @@ public class LdapSessionFactory extends SessionFactory {
}
logger.info("Realm [{}] is in user-dn-template mode: [{}]", config.name(), userDnTemplates);
groupResolver = groupResolver(config);
metaDataResolver = new LdapMetaDataResolver(config, ignoreReferralErrors);
}
/**

View File

@ -15,6 +15,7 @@ import com.unboundid.ldap.sdk.SimpleBindRequest;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lease.Releasable;
import org.elasticsearch.common.settings.SecureString;
@ -24,9 +25,7 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapMetaDataResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
@ -45,10 +44,8 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
private final LDAPConnectionPool connectionPool;
final SimpleBindRequest bindCredentials;
final LdapMetaDataResolver metaDataResolver;
final LdapSession.GroupsResolver groupResolver;
/**
* @param config the configuration for the realm
* @param sslService the ssl service to get a socket factory or context from
@ -63,7 +60,6 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
ThreadPool threadPool) throws LDAPException {
super(config, sslService, threadPool);
this.groupResolver = groupResolver;
this.metaDataResolver = new LdapMetaDataResolver(config, ignoreReferralErrors);
final byte[] bindPassword;
if (config.hasSetting(LEGACY_BIND_PASSWORD)) {

View File

@ -60,6 +60,8 @@ public abstract class SessionFactory {
protected final boolean sslUsed;
protected final boolean ignoreReferralErrors;
protected final LdapMetaDataResolver metaDataResolver;
protected SessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) {
this.config = config;
this.logger = LogManager.getLogger(getClass());
@ -78,6 +80,7 @@ public abstract class SessionFactory {
this.serverSet = serverSet(config, sslService, ldapServers);
this.sslUsed = ldapServers.ssl;
this.ignoreReferralErrors = config.getSetting(SessionFactorySettings.IGNORE_REFERRAL_ERRORS_SETTING);
this.metaDataResolver = new LdapMetaDataResolver(config, ignoreReferralErrors);
}
/**

View File

@ -15,7 +15,9 @@ import com.unboundid.ldap.sdk.SingleServerSet;
import com.unboundid.ldap.sdk.schema.Schema;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
@ -24,6 +26,9 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.license.TestUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustacheScriptEngine;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
@ -34,10 +39,13 @@ import org.elasticsearch.xpack.core.security.authc.ldap.ActiveDirectorySessionFa
import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapLoadBalancingSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapMetaDataResolverSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.support.CachingUsernamePasswordRealmSettings;
import org.elasticsearch.xpack.core.security.authc.support.DnRoleMapperSettings;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping;
import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.core.ssl.SSLService;
@ -45,6 +53,8 @@ import org.elasticsearch.xpack.core.ssl.VerificationMode;
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.DownLevelADAuthenticator;
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.UpnADAuthenticator;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@ -52,13 +62,13 @@ import org.junit.BeforeClass;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.URLS_SETTING;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
@ -71,9 +81,11 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Active Directory Realm tests that use the UnboundID In Memory Directory Server
@ -354,6 +366,62 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
assertThat(user.roles(), arrayContainingInAnyOrder(equalTo("group_role"), equalTo("user_role")));
}
/**
* This tests template role mappings (see
* {@link TemplateRoleName}) with an LDAP realm, using a additional
* metadata field (see {@link LdapMetaDataResolverSettings#ADDITIONAL_META_DATA_SETTING}).
*/
public void testRealmWithTemplatedRoleMapping() throws Exception {
final RealmConfig.RealmIdentifier realmId = realmId("testRealmWithTemplatedRoleMapping");
Settings settings = settings(realmId, Settings.builder()
.put(getFullSettingKey(realmId, LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "departmentNumber")
.build());
RealmConfig config = setupRealm(realmId, settings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool);
SecurityIndexManager mockSecurityIndex = mock(SecurityIndexManager.class);
when(mockSecurityIndex.isAvailable()).thenReturn(true);
when(mockSecurityIndex.isIndexUpToDate()).thenReturn(true);
when(mockSecurityIndex.isMappingUpToDate()).thenReturn(true);
Client mockClient = mock(Client.class);
when(mockClient.threadPool()).thenReturn(threadPool);
final ScriptService scriptService = new ScriptService(settings, Collections.singletonMap(MustacheScriptEngine.NAME,
new MustacheScriptEngine()), ScriptModule.CORE_CONTEXTS);
NativeRoleMappingStore roleMapper = new NativeRoleMappingStore(settings, mockClient, mockSecurityIndex, scriptService) {
@Override
protected void loadMappings(ActionListener<List<ExpressionRoleMapping>> listener) {
listener.onResponse(
Arrays.asList(
this.buildMapping("m1", new BytesArray("{" +
"\"role_templates\":[{\"template\":{\"source\":\"_role_{{metadata.departmentNumber}}\"}}]," +
"\"enabled\":true," +
"\"rules\":{ " +
" \"field\":{\"realm.name\":\"testrealmwithtemplatedrolemapping\"}" +
"}}"))));
}
};
LdapRealm realm = new LdapRealm(config, sessionFactory, roleMapper, threadPool);
realm.initialize(Collections.singleton(realm), licenseState);
PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
realm.authenticate(new UsernamePasswordToken("CN=Thor", new SecureString(PASSWORD)), future);
AuthenticationResult result = future.actionGet();
assertThat(result.getStatus(), is(AuthenticationResult.Status.SUCCESS));
User user = result.getUser();
assertThat(user, notNullValue());
assertThat(user.roles(), arrayContaining("_role_13"));
future = new PlainActionFuture<>();
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
result = future.actionGet();
assertThat(result.getStatus(), is(AuthenticationResult.Status.SUCCESS));
user = result.getUser();
assertThat(user, notNullValue());
assertThat(user.roles(), arrayContaining("_role_12"));
}
public void testRealmUsageStats() throws Exception {
final RealmConfig.RealmIdentifier realmId = realmId("testRealmUsageStats");
String loadBalanceType = randomFrom("failover", "round_robin");
@ -469,7 +537,8 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
builder.put(getFullSettingKey(realmIdentifier, SSLConfigurationSettings.VERIFICATION_MODE_SETTING_REALM),
VerificationMode.CERTIFICATE);
} else {
builder.put(getFullSettingKey(realmIdentifier, HOSTNAME_VERIFICATION_SETTING), false);
builder.put(getFullSettingKey(realmIdentifier, SSLConfigurationSettings.VERIFICATION_MODE_SETTING_REALM),
VerificationMode.NONE);
}
return builder.put(extraSettings).build();
}

View File

@ -416,7 +416,8 @@ public class LdapRealmTests extends LdapTestCase {
Settings settings = Settings.builder()
.put(defaultGlobalSettings)
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
.put(getFullSettingKey(REALM_IDENTIFIER.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "uid")
.put(getFullSettingKey(REALM_IDENTIFIER.getName(),
LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING.apply(LdapRealmSettings.LDAP_TYPE)), "uid")
.build();
RealmConfig config = getRealmConfig(REALM_IDENTIFIER, settings);

View File

@ -39,7 +39,8 @@ public class LdapMetaDataResolverTests extends ESTestCase {
final RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier(LdapRealmSettings.LDAP_TYPE, "my_ldap");
final Settings settings = Settings.builder()
.put("path.home", createTempDir())
.putList(RealmSettings.getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING),
.putList(RealmSettings.getFullSettingKey(realmId.getName(),
LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING.apply(LdapRealmSettings.LDAP_TYPE)),
"cn", "uid")
.build();
RealmConfig config = new RealmConfig(realmId,

View File

@ -29,6 +29,7 @@ userPrincipalName: ironman@ad.test.elasticsearch.com
userPrincipalName: CN=ironman@ad.test.elasticsearch.com
userPassword: password
sn: Stark
departmentNumber: 12
dn: CN=Thor,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com
objectclass: user
@ -42,3 +43,4 @@ tokenGroups:: AQUAAAAAAAUVAAAA4rc20emZjwwpdMkMUQQAAA==
userPrincipalName: Thor@ad.test.elasticsearch.com
userPassword: password
sn: Stark
departmentNumber: 13

View File

@ -228,7 +228,8 @@ public class OpenLdapTests extends ESTestCase {
public void testResolveSingleValuedAttributeFromConnection() throws Exception {
final RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier("ldap", "oldap-test");
final Settings settings = Settings.builder()
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "cn", "sn")
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING.apply("ldap")),
"cn", "sn")
.build();
final RealmConfig config = new RealmConfig(realmId, settings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY));
@ -244,7 +245,8 @@ public class OpenLdapTests extends ESTestCase {
public void testResolveMultiValuedAttributeFromConnection() throws Exception {
final RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier("ldap", "oldap-test");
final Settings settings = Settings.builder()
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "objectClass")
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING.apply("ldap")),
"objectClass")
.build();
final RealmConfig config = new RealmConfig(realmId, settings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY));
@ -260,7 +262,8 @@ public class OpenLdapTests extends ESTestCase {
public void testResolveMissingAttributeFromConnection() throws Exception {
final RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier("ldap", "oldap-test");
final Settings settings = Settings.builder()
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "alias")
.putList(getFullSettingKey(realmId.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING.apply("ldap")),
"alias")
.build();
final RealmConfig config = new RealmConfig(realmId, settings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY));