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:
parent
f792dbf239
commit
78558a7b2f
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue