Skip to content

Fix AD realm additional metadata #47179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static Set<Setting.AffixSetting<?>> getSettings(String type) {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@

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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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;
Expand All @@ -18,9 +19,7 @@
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;
Expand All @@ -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);
Expand All @@ -52,7 +50,6 @@ public LdapSessionFactory(RealmConfig config, SSLService sslService, ThreadPool
}
logger.info("Realm [{}] is in user-dn-template mode: [{}]", config.name(), userDnTemplates);
groupResolver = groupResolver(config);
metaDataResolver = new LdapMetaDataResolver(config, ignoreReferralErrors);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand All @@ -24,9 +25,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.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;
Expand All @@ -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
Expand All @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -78,6 +80,7 @@ protected SessionFactory(RealmConfig config, SSLService sslService, ThreadPool t
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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
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;
Expand All @@ -24,6 +26,9 @@
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;
Expand All @@ -34,31 +39,36 @@
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;
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;

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;
Expand All @@ -71,9 +81,11 @@
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
Expand Down Expand Up @@ -354,6 +366,62 @@ public void testRealmMapsUsersToRoles() throws Exception {
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");
Expand Down Expand Up @@ -469,7 +537,8 @@ private Settings settings(RealmConfig.RealmIdentifier realmIdentifier, Settings
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,8 @@ public void testLdapRealmWithTemplatedRoleMapping() throws Exception {
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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public void testParseSettings() throws Exception {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,3 +43,4 @@ tokenGroups:: AQUAAAAAAAUVAAAA4rc20emZjwwpdMkMUQQAAA==
userPrincipalName: Thor@ad.test.elasticsearch.com
userPassword: password
sn: Stark
departmentNumber: 13
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public void testStandardLdapConnectionHostnameVerificationSuccess() throws Excep
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));
Expand All @@ -223,7 +224,8 @@ public void testResolveSingleValuedAttributeFromConnection() throws Exception {
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));
Expand All @@ -239,7 +241,8 @@ public void testResolveMultiValuedAttributeFromConnection() throws Exception {
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));
Expand Down