Skip to content

Commit

Permalink
Fix AD realm additional metadata (elastic#47179)
Browse files Browse the repository at this point in the history
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 elastic#45848
  • Loading branch information
albertzaharovits committed Oct 1, 2019
1 parent a7c7c49 commit 4b5d269
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 23 deletions.
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 @@ -228,7 +228,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 @@ -244,7 +245,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 @@ -260,7 +262,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

0 comments on commit 4b5d269

Please sign in to comment.