Introduced realms factories

Today it is possible to configure 3 realms in shield - `esusers`, `ldap` and `active_directory`. These realms are created once based on the configuration. There are several problems with this approach:

- Taking `ldap` as an example, it is currently not possible to have multiple `ldap` realms configured (where one serving as a fallback for the other). While the `ldap` realm itself enables defining multiple ldap URLs, it has the limitation that the fallback LDAP must have the exact same configuration as the primary LDAP (+ there's the limitation that all URLs must either us SSL or not... there cannot be a mix of SSL URL and a normal URL)

- The realms are created and bound internally by guice. This will limit the configurability at runtime of the realms which we might want to introduce in shield 2.0.

This commit changes the way realms are managed & configured. Instead of having guice bind the realms themselves. A new realm factory construct will be introduced. The realm factory will represent a realm type and guice will bind these factories. At load time, we'll read the configuration and based on the types of the configured realms, the relevant factories will create the realms based on the settings. This means that potentially we can expose the realms as a dynamic configuration and rebuild the realm chain at runtime.

A nice side effect of this approach is that the multiple URLs feature that is currently supported by both `ldap` and `active_directory` can be dropped. Instead, the users will just need to configure multiple `ldap`/`active_directory` realms.

Closes: elastic/elasticsearch#370

Original commit: elastic/x-pack-elasticsearch@3232f153bb
This commit is contained in:
uboness 2014-11-24 04:06:27 +01:00
parent f178575625
commit af74f43aea
47 changed files with 772 additions and 850 deletions

View File

@ -63,3 +63,6 @@ java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)
java.nio.channels.ScatteringByteChannel#read(java.nio.ByteBuffer[])
java.nio.channels.ScatteringByteChannel#read(java.nio.ByteBuffer[], int, int)
java.nio.channels.FileChannel#read(java.nio.ByteBuffer, long)
@defaultMessage The LdapSslSocketFactory should never be cleared manually as it may lead to threading issues.
org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory#clear()

View File

@ -5,32 +5,36 @@
*/
package org.elasticsearch.shield.authc;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.active_directory.ActiveDirectoryModule;
import org.elasticsearch.shield.authc.esusers.ESUsersModule;
import org.elasticsearch.shield.authc.ldap.LdapModule;
import org.elasticsearch.shield.authc.active_directory.ActiveDirectoryRealm;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.authc.ldap.LdapRealm;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.support.AbstractShieldModule;
/**
*
*/
public class AuthenticationModule extends AbstractShieldModule.Node.Spawn {
public class AuthenticationModule extends AbstractShieldModule.Node {
public AuthenticationModule(Settings settings) {
super(settings);
}
@Override
public Iterable<? extends Node> spawnModules() {
return ImmutableList.of(
new ESUsersModule(settings),
new LdapModule(settings),
new ActiveDirectoryModule(settings));
}
@Override
protected void configureNode() {
// This socket factory needs to be configured before any LDAP connections are created. LDAP configuration
// for JNDI invokes a static getSocketFactory method from LdapSslSocketFactory.
requestStaticInjection(LdapSslSocketFactory.class);
MapBinder<String, Realm.Factory> mapBinder = MapBinder.newMapBinder(binder(), String.class, Realm.Factory.class);
mapBinder.addBinding(ESUsersRealm.TYPE).to(ESUsersRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(ActiveDirectoryRealm.TYPE).to(ActiveDirectoryRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(LdapRealm.TYPE).to(LdapRealm.Factory.class).asEagerSingleton();
bind(Realms.class).asEagerSingleton();
bind(AuthenticationService.class).to(InternalAuthenticationService.class).asEagerSingleton();
}
}

View File

@ -30,7 +30,7 @@ public class InternalAuthenticationService extends AbstractComponent implements
static final String TOKEN_KEY = "_shield_token";
static final String USER_KEY = "_shield_user";
private final Realm[] realms;
private final Realms realms;
private final AuditTrail auditTrail;
private final SignatureService signatureService;
private final boolean signUserHeader;
@ -38,7 +38,7 @@ public class InternalAuthenticationService extends AbstractComponent implements
@Inject
public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, SignatureService signatureService) {
super(settings);
this.realms = realms.realms();
this.realms = realms;
this.auditTrail = auditTrail;
this.signatureService = signatureService;
this.signUserHeader = componentSettings.getAsBoolean("sign_user_header", true);

View File

@ -5,6 +5,9 @@
*/
package org.elasticsearch.shield.authc;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.User;
import org.elasticsearch.transport.TransportMessage;
@ -14,17 +17,52 @@ import org.elasticsearch.transport.TransportMessage;
* delegates the authentication process. Different realms may be defined, each may be based on different
* authentication mechanism supporting its own specific authentication token type.
*/
public interface Realm<T extends AuthenticationToken> {
public abstract class Realm<T extends AuthenticationToken> implements Comparable<Realm> {
protected final ESLogger logger = Loggers.getLogger(getClass());
protected final String type;
protected final String name;
protected final Settings settings;
protected final int order;
public Realm(String type, String name, Settings settings) {
this.type = type;
this.name = name;
this.settings = settings;
this.order = settings.getAsInt("order", Integer.MAX_VALUE);
}
/**
* @return The type of this realm
* @return The type of this realm
*/
String type();
public String type() {
return type;
}
/**
* @return The name of this realm.
*/
public String name() {
return name;
}
/**
* @return The order of this realm within the executing realm chain.
*/
public int order() {
return order;
}
@Override
public int compareTo(Realm other) {
return Integer.compare(order, other.order);
}
/**
* @return {@code true} if this realm supports the given authentication token, {@code false} otherwise.
*/
boolean supports(AuthenticationToken token);
public abstract boolean supports(AuthenticationToken token);
/**
* Attempts to extract an authentication token from the given rest request. If an appropriate token
@ -33,7 +71,7 @@ public interface Realm<T extends AuthenticationToken> {
* @param request The rest request
* @return The authentication token or {@code null} if not found
*/
T token(RestRequest request);
public abstract T token(RestRequest request);
/**
* Attempts to extract an authentication token from the given transport message. If an appropriate token
@ -42,7 +80,7 @@ public interface Realm<T extends AuthenticationToken> {
* @param message The transport message
* @return The authentication token or {@code null} if not found
*/
T token(TransportMessage<?> message);
public abstract T token(TransportMessage<?> message);
/**
* Authenticates the given token. A successful authentication will return the User associated
@ -51,6 +89,47 @@ public interface Realm<T extends AuthenticationToken> {
* @param token The authentication token
* @return The authenticated user or {@code null} if authentication failed.
*/
User authenticate(T token);
public abstract User authenticate(T token);
/**
* A factory for a specific realm type. Knows how to create a new realm given the appropriate
* settings
*/
public static abstract class Factory<R extends Realm> {
private final String type;
private final boolean internal;
public Factory(String type, boolean internal) {
this.type = type;
this.internal = internal;
}
/**
* @return The type of the ream this factory creates
*/
public String type() {
return type;
}
public boolean internal() {
return internal;
}
/**
* Creates a new realm based on the given settigns.
*
* @param settings The settings for the realm.
* @return The new realm (this method never returns {@code null}).
*/
public abstract R create(String name, Settings settings);
/**
* Creates a default realm, one that has no custom settings. Some realms might require minimal
* settings, in which case, this method will return {@code null}.
*/
public abstract R createDefault(String name);
}
}

View File

@ -5,46 +5,105 @@
*/
package org.elasticsearch.shield.authc;
import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.collect.Sets;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.shield.authc.active_directory.ActiveDirectoryRealm;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.authc.ldap.LdapRealm;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Serves as a realms registry (also responsible for ordering the realms appropriately)
*/
public class Realms {
public class Realms extends AbstractComponent implements Iterable<Realm> {
private static final ESLogger logger = ESLoggerFactory.getLogger(Realms.class.getName());
private final Map<String, Realm.Factory> factories;
private final Realm[] realms;
private final List<Realm> realms;
@Inject
public Realms(@Nullable ESUsersRealm esusers, @Nullable LdapRealm ldap, @Nullable ActiveDirectoryRealm activeDirectory) {
List<Realm> realms = new ArrayList<>();
if (esusers != null) {
logger.info("Realm [" + esusers.type() + "] is used");
realms.add(esusers);
}
if (ldap != null) {
logger.info("Realm [" + ldap.type() + "] is used");
realms.add(ldap);
}
if (activeDirectory != null) {
logger.info("Realm [" + activeDirectory.type() + "] is used");
realms.add(activeDirectory);
}
this.realms = realms.toArray(new Realm[realms.size()]);
public Realms(Settings settings, Map<String, Realm.Factory> factories) {
super(settings);
this.factories = factories;
realms = new CopyOnWriteArrayList<>(initRealms());
}
Realm[] realms() {
@Override
public Iterator<Realm> iterator() {
return realms.iterator();
}
public Realm.Factory realmFactory(String type) {
return factories.get(type);
}
protected List<Realm> initRealms() {
Settings realmsSettings = componentSettings.getAsSettings("realms");
Set<String> internalTypes = Sets.newHashSet();
List<Realm> realms = Lists.newArrayList();
for (String name : realmsSettings.names()) {
Settings realmSettings = realmsSettings.getAsSettings(name);
String type = realmSettings.get("type");
if (type == null) {
throw new ShieldSettingsException("Missing realm type for in [" + name + "] realm");
}
Realm.Factory factory = factories.get(type);
if (factory == null) {
throw new ShieldSettingsException("Unknown reaml type [" + type + "] set for realm [" + name + "]");
}
if (factory.internal()) {
// this is an internal realm factory, let's make sure we didn't already registered one
// (there can only be one instance of an internal realm)
if (internalTypes.contains(type)) {
throw new ShieldSettingsException("Multiple [" + type + "] realms are configured. [" + type +
"] is an internal realm and therefore there can only be one such realm configured");
}
internalTypes.add(type);
}
realms.add(factory.create(name, realmSettings));
}
if (!realms.isEmpty()) {
CollectionUtil.introSort(realms);
return realms;
}
// there is no "realms" configuration, go over all the factories and try to create defaults
// for all the internal realms
realms.add(factories.get(ESUsersRealm.TYPE).createDefault("default_" + ESUsersRealm.TYPE));
return realms;
}
/**
* returns the settings for the internal realm of the given type. Typically, internal realms may or may
* not be configured. If they are not configured, they work OOTB using default settings. If they are
* configured, there can only be one configure for an internal realm.
*/
public static Settings internalRealmSettings(Settings settings, String realmType) {
Settings realmsSettings = settings.getComponentSettings(Realms.class).getAsSettings("realms");
Settings result = null;
for (String name : realmsSettings.names()) {
Settings realmSettings = realmsSettings.getAsSettings(name);
String type = realmSettings.get("type");
if (type == null) {
throw new ShieldSettingsException("Missing realm type for in [" + name + "] realm");
}
if (type.equals(realmType)) {
if (result != null) {
throw new ShieldSettingsException("Multiple [" + realmType + "] are configured. Only one [" + realmType + "] may be configured");
}
result = realmSettings;
}
}
return result != null ? result : ImmutableSettings.EMPTY;
}
}

View File

@ -7,7 +7,6 @@ package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldSettingsException;
@ -31,7 +30,7 @@ import java.util.Hashtable;
* user entry in Active Directory that matches the user name). This eliminates the need for user templates, and simplifies
* the configuration for windows admins that may not be familiar with LDAP concepts.
*/
public class ActiveDirectoryConnectionFactory extends AbstractComponent implements ConnectionFactory {
public class ActiveDirectoryConnectionFactory extends ConnectionFactory {
public static final String AD_DOMAIN_NAME_SETTING = "domain_name";
public static final String AD_USER_SEARCH_BASEDN_SETTING = "user_search_dn";
@ -43,13 +42,13 @@ public class ActiveDirectoryConnectionFactory extends AbstractComponent implemen
@Inject
public ActiveDirectoryConnectionFactory(Settings settings) {
super(settings);
domainName = componentSettings.get(AD_DOMAIN_NAME_SETTING);
domainName = settings.get(AD_DOMAIN_NAME_SETTING);
if (domainName == null) {
throw new ShieldSettingsException("Missing [" + AD_DOMAIN_NAME_SETTING + "] setting for active directory");
}
userSearchDN = componentSettings.get(AD_USER_SEARCH_BASEDN_SETTING, buildDnFromDomain(domainName));
userSearchDN = settings.get(AD_USER_SEARCH_BASEDN_SETTING, buildDnFromDomain(domainName));
String[] ldapUrls = componentSettings.getAsArray(URLS_SETTING, new String[] { "ldaps://" + domainName + ":636" });
String[] ldapUrls = settings.getAsArray(URLS_SETTING, new String[] { "ldaps://" + domainName + ":636" });
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder()
.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")

View File

@ -18,6 +18,6 @@ public class ActiveDirectoryGroupToRoleMapper extends AbstractGroupToRoleMapper
@Inject
public ActiveDirectoryGroupToRoleMapper(Settings settings, Environment env, ResourceWatcherService watcherService) {
super(settings, ActiveDirectoryRealm.type, env, watcherService, null);
super(settings, ActiveDirectoryRealm.TYPE, env, watcherService, null);
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.support.AbstractShieldModule;
/**
* Configures ActiveDirectory Realm object injections
*/
public class ActiveDirectoryModule extends AbstractShieldModule.Node {
private final boolean enabled;
public ActiveDirectoryModule(Settings settings) {
super(settings);
enabled = enabled(settings);
}
@Override
protected void configureNode() {
if (enabled) {
/* This socket factory needs to be configured before any LDAP connections are created. LDAP configuration
for JNDI invokes a static getSocketFactory method from LdapSslSocketFactory. */
requestStaticInjection(LdapSslSocketFactory.class);
bind(ActiveDirectoryRealm.class).asEagerSingleton();
} else {
bind(ActiveDirectoryRealm.class).toProvider(Providers.<ActiveDirectoryRealm>of(null));
}
}
static boolean enabled(Settings settings) {
Settings authcSettings = settings.getAsSettings("shield.authc");
if (!authcSettings.names().contains(ActiveDirectoryRealm.type)) {
return false;
}
Settings adSettings = authcSettings.getAsSettings(ActiveDirectoryRealm.type);
return adSettings.getAsBoolean("enabled", true);
}
}

View File

@ -7,26 +7,48 @@ package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapRealm;
import org.elasticsearch.watcher.ResourceWatcherService;
/**
*
*/
public class ActiveDirectoryRealm extends AbstractLdapRealm {
public static final String type = "active_directory";
public static final String TYPE = "active_directory";
@Inject
public ActiveDirectoryRealm(Settings settings,
ActiveDirectoryConnectionFactory connectionFactory,
ActiveDirectoryGroupToRoleMapper roleMapper,
RestController restController) {
super(settings, connectionFactory, roleMapper, restController);
public ActiveDirectoryRealm(String name, Settings settings, ActiveDirectoryConnectionFactory connectionFactory,
ActiveDirectoryGroupToRoleMapper roleMapper) {
super(name, TYPE, settings, connectionFactory, roleMapper);
}
@Override
public String type() {
return type;
return TYPE;
}
public static class Factory extends AbstractLdapRealm.Factory<ActiveDirectoryRealm> {
private final Environment env;
private final ResourceWatcherService watcherService;
@Inject
public Factory(Environment env, ResourceWatcherService watcherService, RestController restController) {
super(ActiveDirectoryRealm.TYPE, restController);
this.env = env;
this.watcherService = watcherService;
}
@Override
public ActiveDirectoryRealm create(String name, Settings settings) {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(settings);
ActiveDirectoryGroupToRoleMapper roleMapper = new ActiveDirectoryGroupToRoleMapper(settings, env, watcherService);
return new ActiveDirectoryRealm(name, settings, connectionFactory, roleMapper);
}
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.esusers;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.support.AbstractShieldModule;
/**
*
*/
public class ESUsersModule extends AbstractShieldModule.Node {
private final boolean enabled;
public ESUsersModule(Settings settings) {
super(settings);
enabled = enabled(settings);
}
@Override
protected void configureNode() {
if (enabled) {
bind(ESUsersRealm.class).asEagerSingleton();
bind(FileUserPasswdStore.class).asEagerSingleton();
bind(FileUserRolesStore.class).asEagerSingleton();
} else {
bind(ESUsersRealm.class).toProvider(Providers.<ESUsersRealm>of(null));
}
}
static boolean enabled(Settings settings) {
return settings.getComponentSettings(ESUsersModule.class).getAsBoolean("enabled", true);
}
}

View File

@ -6,15 +6,19 @@
package org.elasticsearch.shield.authc.esusers;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authc.Realm;
import org.elasticsearch.shield.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.watcher.ResourceWatcherService;
/**
*
@ -26,16 +30,13 @@ public class ESUsersRealm extends CachingUsernamePasswordRealm {
final FileUserPasswdStore userPasswdStore;
final FileUserRolesStore userRolesStore;
@Inject
public ESUsersRealm(Settings settings, FileUserPasswdStore userPasswdStore,
FileUserRolesStore userRolesStore, RestController restController) {
super(settings);
public ESUsersRealm(String name, Settings settings, FileUserPasswdStore userPasswdStore, FileUserRolesStore userRolesStore) {
super(name, TYPE, settings);
Listener listener = new Listener();
this.userPasswdStore = userPasswdStore;
userPasswdStore.addListener(listener);
this.userRolesStore = userRolesStore;
userRolesStore.addListener(listener);
restController.registerRelevantHeaders(UsernamePasswordToken.BASIC_AUTH_HEADER);
}
@Override
@ -73,4 +74,30 @@ public class ESUsersRealm extends CachingUsernamePasswordRealm {
expireAll();
}
}
public static class Factory extends Realm.Factory<ESUsersRealm> {
private final Environment env;
private final ResourceWatcherService watcherService;
@Inject
public Factory(Environment env, ResourceWatcherService watcherService, RestController restController) {
super(TYPE, true);
this.env = env;
this.watcherService = watcherService;
restController.registerRelevantHeaders(UsernamePasswordToken.BASIC_AUTH_HEADER);
}
@Override
public ESUsersRealm create(String name, Settings settings) {
FileUserPasswdStore userPasswdStore = new FileUserPasswdStore(settings, env, watcherService);
FileUserRolesStore userRolesStore = new FileUserRolesStore(settings, env, watcherService);
return new ESUsersRealm(name, settings, userPasswdStore, userRolesStore);
}
@Override
public ESUsersRealm createDefault(String name) {
return create(name, ImmutableSettings.EMPTY);
}
}
}

View File

@ -8,16 +8,15 @@ package org.elasticsearch.shield.authc.esusers;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.base.Charsets;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.support.Hasher;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
@ -37,7 +36,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
*
*/
public class FileUserPasswdStore extends AbstractComponent {
public class FileUserPasswdStore {
private static final ESLogger logger = Loggers.getLogger(FileUserPasswdStore.class);
private final Path file;
final Hasher hasher = Hasher.HTPASSWD;
@ -46,13 +47,11 @@ public class FileUserPasswdStore extends AbstractComponent {
private CopyOnWriteArrayList<RefreshListener> listeners;
@Inject
public FileUserPasswdStore(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, null);
}
FileUserPasswdStore(Settings settings, Environment env, ResourceWatcherService watcherService, RefreshListener listener) {
super(settings);
file = resolveFile(settings, env);
esUsers = parseFile(file, logger);
if (esUsers.isEmpty() && logger.isDebugEnabled()) {
@ -83,7 +82,7 @@ public class FileUserPasswdStore extends AbstractComponent {
}
public static Path resolveFile(Settings settings, Environment env) {
String location = settings.get("shield.authc.esusers.files.users");
String location = settings.get("files.users");
if (location == null) {
return ShieldPlugin.resolveConfigFile(env, "users");
}

View File

@ -9,14 +9,13 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.base.Charsets;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
@ -35,23 +34,21 @@ import java.util.regex.Pattern;
/**
*
*/
public class FileUserRolesStore extends AbstractComponent {
public class FileUserRolesStore {
private static final ESLogger logger = Loggers.getLogger(FileUserPasswdStore.class);
private static final Pattern USERS_DELIM = Pattern.compile("\\s*,\\s*");
private final Path file;
private CopyOnWriteArrayList<RefreshListener> listeners;
private volatile ImmutableMap<String, String[]> userRoles;
private CopyOnWriteArrayList<RefreshListener> listeners;
@Inject
public FileUserRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, null);
}
FileUserRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, RefreshListener listener) {
super(settings);
file = resolveFile(settings, env);
userRoles = parseFile(file, logger);
FileWatcher watcher = new FileWatcher(file.getParent().toFile());
@ -76,7 +73,7 @@ public class FileUserRolesStore extends AbstractComponent {
}
public static Path resolveFile(Settings settings, Environment env) {
String location = settings.get("shield.authc.esusers.files.users_roles");
String location = settings.get("files.users_roles");
if (location == null) {
return ShieldPlugin.resolveConfigFile(env, "users_roles");
}

View File

@ -15,6 +15,8 @@ import org.elasticsearch.common.collect.*;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.Realms;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.authc.esusers.FileUserPasswdStore;
import org.elasticsearch.shield.authc.esusers.FileUserRolesStore;
import org.elasticsearch.shield.authc.support.Hasher;
@ -121,8 +123,9 @@ public class ESUsersTool extends CliTool {
@Override
public ExitStatus execute(Settings settings, Environment env) throws Exception {
Settings esusersSettings = Realms.internalRealmSettings(settings, ESUsersRealm.TYPE);
verifyRoles(terminal, settings, env, roles);
Path file = FileUserPasswdStore.resolveFile(settings, env);
Path file = FileUserPasswdStore.resolveFile(esusersSettings, env);
Map<String, char[]> users = new HashMap<>(FileUserPasswdStore.parseFile(file, null));
if (users.containsKey(username)) {
terminal.println("User [%s] already exists", username);
@ -133,7 +136,7 @@ public class ESUsersTool extends CliTool {
FileUserPasswdStore.writeFile(users, file);
if (roles != null && roles.length > 0) {
file = FileUserRolesStore.resolveFile(settings, env);
file = FileUserRolesStore.resolveFile(esusersSettings, env);
Map<String, String[]> userRoles = new HashMap<>(FileUserRolesStore.parseFile(file, null));
userRoles.put(username, roles);
FileUserRolesStore.writeFile(userRoles, file);
@ -166,7 +169,8 @@ public class ESUsersTool extends CliTool {
@Override
public ExitStatus execute(Settings settings, Environment env) throws Exception {
Path file = FileUserPasswdStore.resolveFile(settings, env);
Settings esusersSettings = Realms.internalRealmSettings(settings, ESUsersRealm.TYPE);
Path file = FileUserPasswdStore.resolveFile(esusersSettings, env);
Map<String, char[]> users = new HashMap<>(FileUserPasswdStore.parseFile(file, null));
if (!users.containsKey(username)) {
terminal.println("User [%s] doesn't exist", username);
@ -180,7 +184,7 @@ public class ESUsersTool extends CliTool {
}
}
file = FileUserRolesStore.resolveFile(settings, env);
file = FileUserRolesStore.resolveFile(esusersSettings, env);
Map<String, String[]> userRoles = new HashMap<>(FileUserRolesStore.parseFile(file, null));
if (Files.exists(file)) {
String[] roles = userRoles.remove(username);
@ -234,7 +238,8 @@ public class ESUsersTool extends CliTool {
@Override
public ExitStatus execute(Settings settings, Environment env) throws Exception {
Path file = FileUserPasswdStore.resolveFile(settings, env);
Settings esusersSettings = Realms.internalRealmSettings(settings, ESUsersRealm.TYPE);
Path file = FileUserPasswdStore.resolveFile(esusersSettings, env);
Map<String, char[]> users = new HashMap<>(FileUserPasswdStore.parseFile(file, null));
if (!users.containsKey(username)) {
terminal.println("User [%s] doesn't exist", username);
@ -302,14 +307,16 @@ public class ESUsersTool extends CliTool {
}
}
Path path = FileUserPasswdStore.resolveFile(settings, env);
Settings esusersSettings = Realms.internalRealmSettings(settings, ESUsersRealm.TYPE);
Path path = FileUserPasswdStore.resolveFile(esusersSettings, env);
Map<String, char[]> usersMap = FileUserPasswdStore.parseFile(path, null);
if (!usersMap.containsKey(username)) {
terminal.println("User [%s] doesn't exist", username);
return ExitStatus.NO_USER;
}
Path file = FileUserRolesStore.resolveFile(settings, env);
Path file = FileUserRolesStore.resolveFile(esusersSettings, env);
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(file, null);
List<String> roles = Lists.newArrayList();
@ -353,10 +360,11 @@ public class ESUsersTool extends CliTool {
@Override
public ExitStatus execute(Settings settings, Environment env) throws Exception {
Settings esusersSettings = Realms.internalRealmSettings(settings, ESUsersRealm.TYPE);
ImmutableMap<String, Permission.Global.Role> knownRoles = loadRoles(terminal, settings, env);
Path userRolesFilePath = FileUserRolesStore.resolveFile(settings, env);
Path userRolesFilePath = FileUserRolesStore.resolveFile(esusersSettings, env);
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(userRolesFilePath, null);
Path userFilePath = FileUserPasswdStore.resolveFile(settings, env);
Path userFilePath = FileUserPasswdStore.resolveFile(esusersSettings, env);
Set<String> users = FileUserPasswdStore.parseFile(userFilePath, null).keySet();
if (username != null) {
@ -372,7 +380,7 @@ public class ESUsersTool extends CliTool {
terminal.println("%-15s: %s", username, Joiner.on(",").useForNull("-").join(markedRoles));
if (!unknownRoles.isEmpty()) {
// at least one role is marked... so printing the legend
Path rolesFile = FileRolesStore.resolveFile(settings, env).toAbsolutePath();
Path rolesFile = FileRolesStore.resolveFile(esusersSettings, env).toAbsolutePath();
terminal.println();
terminal.println(" [*] An unknown role. Please check [%s] to see available roles", rolesFile.toAbsolutePath());
}
@ -397,7 +405,7 @@ public class ESUsersTool extends CliTool {
if (unknownRolesFound) {
// at least one role is marked... so printing the legend
Path rolesFile = FileRolesStore.resolveFile(settings, env).toAbsolutePath();
Path rolesFile = FileRolesStore.resolveFile(esusersSettings, env).toAbsolutePath();
terminal.println();
terminal.println(" [*] An unknown role. Please check [%s] to see available roles", rolesFile.toAbsolutePath());
}

View File

@ -7,7 +7,6 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldSettingsException;
@ -26,11 +25,11 @@ import java.util.Hashtable;
/**
* This factory creates LDAP connections via iterating through user templates.
* <p/>
*
* Note that even though there is a separate factory for Active Directory, this factory would work against AD. A template
* for each user context would need to be supplied.
*/
public class LdapConnectionFactory extends AbstractComponent implements ConnectionFactory {
public class LdapConnectionFactory extends ConnectionFactory {
public static final String USER_DN_TEMPLATES_SETTING = "user_dn_templates";
public static final String GROUP_SEARCH_SUBTREE_SETTING = "group_search.subtree_search";
@ -45,11 +44,11 @@ public class LdapConnectionFactory extends AbstractComponent implements Connecti
@Inject
public LdapConnectionFactory(Settings settings) {
super(settings);
userDnTemplates = componentSettings.getAsArray(USER_DN_TEMPLATES_SETTING);
userDnTemplates = settings.getAsArray(USER_DN_TEMPLATES_SETTING);
if (userDnTemplates == null) {
throw new ShieldSettingsException("Missing required ldap setting [" + USER_DN_TEMPLATES_SETTING + "]");
}
String[] ldapUrls = componentSettings.getAsArray(URLS_SETTING);
String[] ldapUrls = settings.getAsArray(URLS_SETTING);
if (ldapUrls == null) {
throw new ShieldSettingsException("Missing required ldap setting [" + URLS_SETTING + "]");
}
@ -62,9 +61,9 @@ public class LdapConnectionFactory extends AbstractComponent implements Connecti
LdapSslSocketFactory.configureJndiSSL(ldapUrls, builder);
sharedLdapEnv = builder.build();
groupSearchDN = componentSettings.get(GROUP_SEARCH_BASEDN_SETTING);
groupSearchDN = settings.get(GROUP_SEARCH_BASEDN_SETTING);
findGroupsByAttribute = groupSearchDN == null;
groupSubTreeSearch = componentSettings.getAsBoolean(GROUP_SEARCH_SUBTREE_SETTING, false);
groupSubTreeSearch = settings.getAsBoolean(GROUP_SEARCH_SUBTREE_SETTING, false);
}
/**

View File

@ -1,46 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.support.AbstractShieldModule;
/**
* Configures Ldap object injections
*/
public class LdapModule extends AbstractShieldModule.Node {
private final boolean enabled;
public LdapModule(Settings settings) {
super(settings);
enabled = enabled(settings);
}
@Override
protected void configureNode() {
if (enabled) {
/* This socket factory needs to be configured before any LDAP connections are created. LDAP configuration
for JNDI invokes a static getSocketFactory method from LdapSslSocketFactory. */
requestStaticInjection(LdapSslSocketFactory.class);
bind(LdapRealm.class).asEagerSingleton();
} else {
bind(LdapRealm.class).toProvider(Providers.<LdapRealm>of(null));
}
}
public static boolean enabled(Settings settings) {
Settings authcSettings = settings.getAsSettings("shield.authc");
if (!authcSettings.names().contains(LdapRealm.TYPE)) {
return false;
}
Settings ldapSettings = authcSettings.getAsSettings(LdapRealm.TYPE);
return ldapSettings.getAsBoolean("enabled", true);
}
}

View File

@ -7,8 +7,10 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapRealm;
import org.elasticsearch.watcher.ResourceWatcherService;
/**
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
@ -18,12 +20,32 @@ public class LdapRealm extends AbstractLdapRealm {
public static final String TYPE = "ldap";
@Inject
public LdapRealm(Settings settings, LdapConnectionFactory ldap, LdapGroupToRoleMapper roleMapper, RestController restController) {
super(settings, ldap, roleMapper, restController);
public LdapRealm(String name, Settings settings, LdapConnectionFactory ldap, LdapGroupToRoleMapper roleMapper) {
super(name, TYPE, settings, ldap, roleMapper);
}
@Override
public String type() {
return TYPE;
}
public static class Factory extends AbstractLdapRealm.Factory<LdapRealm> {
private final Environment env;
private final ResourceWatcherService watcherService;
@Inject
public Factory(Environment env, ResourceWatcherService watcherService, RestController restController) {
super(TYPE, restController);
this.env = env;
this.watcherService = watcherService;
}
@Override
public LdapRealm create(String name, Settings settings) {
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(settings);
LdapGroupToRoleMapper roleMapper = new LdapGroupToRoleMapper(settings, env, watcherService);
return new LdapRealm(name, settings, connectionFactory, roleMapper);
}
}
}

View File

@ -7,22 +7,17 @@ package org.elasticsearch.shield.authc.support;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.UncheckedExecutionException;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.AuthenticationException;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authc.Realm;
import org.elasticsearch.transport.TransportMessage;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public abstract class CachingUsernamePasswordRealm extends AbstractComponent implements Realm<UsernamePasswordToken> {
public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm {
private static final TimeValue DEFAULT_TTL = TimeValue.timeValueHours(1);
private static final int DEFAULT_MAX_USERS = 100000; //100k users
@ -30,13 +25,12 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
public static final String CACHE_MAX_USERS = "cache.max_users";
private final Cache<String, UserWithHash> cache;
private final Hasher hasher;
protected CachingUsernamePasswordRealm(Settings settings) {
super(settings);
hasher = Hasher.resolve(componentSettings.get("cache.hash_algo", null), Hasher.SHA2);
TimeValue ttl = componentSettings.getAsTime(CACHE_TTL, DEFAULT_TTL);
protected CachingUsernamePasswordRealm(String type, String name, Settings settings) {
super(type, name, settings);
hasher = Hasher.resolve(settings.get("cache.hash_algo", null), Hasher.SHA2);
TimeValue ttl = settings.getAsTime(CACHE_TTL, DEFAULT_TTL);
if (ttl.millis() > 0) {
cache = CacheBuilder.newBuilder()
.expireAfterWrite(ttl.getMillis(), TimeUnit.MILLISECONDS)
@ -47,21 +41,6 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
}
}
@Override
public UsernamePasswordToken token(RestRequest request) {
return UsernamePasswordToken.extractToken(request, null);
}
@Override
public UsernamePasswordToken token(TransportMessage<?> message) {
return UsernamePasswordToken.extractToken(message, null);
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
protected final void expire(String username) {
if (cache != null) {
cache.invalidate(username);

View File

@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.support;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authc.Realm;
import org.elasticsearch.transport.TransportMessage;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
/**
*
*/
public abstract class UsernamePasswordRealm extends Realm<UsernamePasswordToken> {
public UsernamePasswordRealm(String type, String name, Settings settings) {
super(type, name, settings);
}
@Override
public UsernamePasswordToken token(RestRequest request) {
return UsernamePasswordToken.extractToken(request, null);
}
@Override
public UsernamePasswordToken token(TransportMessage<?> message) {
return UsernamePasswordToken.extractToken(message, null);
}
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
public static abstract class Factory<R extends UsernamePasswordRealm> extends Realm.Factory<R> {
protected Factory(String type, boolean internal, RestController restController) {
super(type, internal);
restController.registerRelevantHeaders(BASIC_AUTH_HEADER);
}
}
}

View File

@ -8,13 +8,13 @@ package org.elasticsearch.shield.authc.support.ldap;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
@ -33,12 +33,14 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* This class loads and monitors the file defining the mappings of LDAP Group DNs to internal ES Roles.
*/
public abstract class AbstractGroupToRoleMapper extends AbstractComponent {
public abstract class AbstractGroupToRoleMapper {
public static final String DEFAULT_FILE_NAME = "role_mapping.yml";
public static final String ROLE_MAPPING_FILE_SETTING = "files.role_mapping";
public static final String USE_UNMAPPED_GROUPS_AS_ROLES_SETTING = "unmapped_groups_as_roles";
protected final ESLogger logger = Loggers.getLogger(getClass());
protected final Settings settings;
private final Path file;
private final boolean useUnmappedGroupsAsRoles;
private final String realmType;
@ -48,10 +50,10 @@ public abstract class AbstractGroupToRoleMapper extends AbstractComponent {
protected AbstractGroupToRoleMapper(Settings settings, String realmType, Environment env,
ResourceWatcherService watcherService, @Nullable RefreshListener listener) {
super(settings);
this.settings = settings;
this.realmType = realmType;
useUnmappedGroupsAsRoles = componentSettings.getAsBoolean(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, false);
file = resolveFile(componentSettings, env);
useUnmappedGroupsAsRoles = settings.getAsBoolean(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, false);
file = resolveFile(settings, env);
groupRoles = parseFile(file, logger, realmType);
FileWatcher watcher = new FileWatcher(file.getParent().toFile());
watcher.addListener(new FileListener());

View File

@ -9,42 +9,28 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.ShieldException;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authc.Realm;
import org.elasticsearch.shield.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.authc.support.UsernamePasswordRealm;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.transport.TransportMessage;
import java.util.List;
import java.util.Set;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
/**
* Supporting class for JNDI-based Realms
*/
public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm implements Realm<UsernamePasswordToken> {
public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm {
protected final ConnectionFactory connectionFactory;
protected final AbstractGroupToRoleMapper roleMapper;
protected AbstractLdapRealm(Settings settings, ConnectionFactory connectionFactory,
AbstractGroupToRoleMapper roleMapper, RestController restController) {
super(settings);
protected AbstractLdapRealm(String type, String name, Settings settings,
ConnectionFactory connectionFactory, AbstractGroupToRoleMapper roleMapper) {
super(type, name, settings);
this.connectionFactory = connectionFactory;
this.roleMapper = roleMapper;
roleMapper.addListener(new Listener());
restController.registerRelevantHeaders(BASIC_AUTH_HEADER);
}
@Override
public UsernamePasswordToken token(TransportMessage<?> message) {
return UsernamePasswordToken.extractToken(message, null);
}
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
/**
@ -72,4 +58,21 @@ public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm imp
expireAll();
}
}
public static abstract class Factory<R extends AbstractLdapRealm> extends UsernamePasswordRealm.Factory<R> {
public Factory(String type, RestController restController) {
super(type, false, restController);
}
/**
* LDAP realms require minimum settings (e.g. URL), therefore they'll never create a default.
*
* @return {@code null} always
*/
@Override
public final R createDefault(String name) {
return null;
}
}
}

View File

@ -5,6 +5,9 @@
*/
package org.elasticsearch.shield.authc.support.ldap;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.support.SecuredString;
/**
@ -19,9 +22,16 @@ import org.elasticsearch.shield.authc.support.SecuredString;
}
</pre>
*/
public interface ConnectionFactory {
public abstract class ConnectionFactory {
static final String URLS_SETTING = "url";
public static final String URLS_SETTING = "url";
protected final ESLogger logger = Loggers.getLogger(getClass());
private final Settings settings;
protected ConnectionFactory(Settings settings) {
this.settings = settings;
}
/**
* Authenticates the given user and opens a new connection that bound to it (meaning, all operations
@ -30,6 +40,6 @@ public interface ConnectionFactory {
* @param user The name of the user to authenticate the connection with.
* @param password The password of the user
*/
AbstractLdapConnection open(String user, SecuredString password) ;
public abstract AbstractLdapConnection open(String user, SecuredString password) ;
}

View File

@ -5,12 +5,12 @@
*/
package org.elasticsearch.shield.authc.support.ldap;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.ssl.SSLService;
@ -35,34 +35,32 @@ import static org.elasticsearch.common.collect.Iterables.all;
*/
public class LdapSslSocketFactory extends SocketFactory {
private static ESLogger logger = Loggers.getLogger(LdapSslSocketFactory.class);
static final String JAVA_NAMING_LDAP_FACTORY_SOCKET = "java.naming.ldap.factory.socket";
private static final Pattern STARTS_WITH_LDAPS = Pattern.compile("^ldaps:.*", Pattern.CASE_INSENSITIVE);
private static final Pattern STARTS_WITH_LDAP = Pattern.compile("^ldap:.*", Pattern.CASE_INSENSITIVE);
private static ESLogger logger = ESLoggerFactory.getLogger(LdapSslSocketFactory.class.getName());
private static LdapSslSocketFactory instance;
private static Provider<SSLService> sslServiceProvider;
/**
* This should only be invoked once to establish a static instance that will be used for each constructor.
*/
@Inject
public static void init(@Nullable SSLService ssl) {
if (instance != null) {
logger.error("LdapSslSocketFactory already configured, this change could lead to threading issues");
}
if (ssl == null) {
logger.warn("no keystore has been configured for LDAP");
} else {
instance = new LdapSslSocketFactory(ssl.getSSLSocketFactory());
}
public static void init(Provider<SSLService> sslServiceProvider) {
LdapSslSocketFactory.sslServiceProvider = sslServiceProvider;
}
/**
* This clears the static factory. There are threading issues with this. But for
* testing this is useful.
*
* WARNING: THIS METHOD SHOULD ONLY BE CALLED IN TESTS!!!!
*
* TODO: find a way to change the tests such that we can remove this method
*/
@Deprecated
public static void clear() {
logger.error("clear should only be called by tests");
instance = null;
@ -73,8 +71,10 @@ public class LdapSslSocketFactory extends SocketFactory {
*
* @return a singleton instance of LdapSslSocketFactory set by calling the init static method.
*/
public static SocketFactory getDefault() {
assert instance != null;
public static synchronized SocketFactory getDefault() {
if (instance == null) {
instance = new LdapSslSocketFactory(sslServiceProvider.get().getSSLSocketFactory());
}
return instance;
}
@ -87,23 +87,23 @@ public class LdapSslSocketFactory extends SocketFactory {
//The following methods are all wrappers around the instance of socketFactory
@Override
public Socket createSocket(String s, int i) throws IOException {
return socketFactory.createSocket(s, i);
public Socket createSocket(String host, int port) throws IOException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i2) throws IOException {
return socketFactory.createSocket(s, i, inetAddress, i2);
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return socketFactory.createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return socketFactory.createSocket(inetAddress, i);
public Socket createSocket(InetAddress host, int port) throws IOException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress2, int i2) throws IOException {
return socketFactory.createSocket(inetAddress, i, inetAddress2, i2);
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
return socketFactory.createSocket(host, port, localHost, localPort);
}
/**
@ -115,12 +115,12 @@ public class LdapSslSocketFactory extends SocketFactory {
*/
public static void configureJndiSSL(String[] ldapUrls, ImmutableMap.Builder<String, Serializable> builder) {
boolean secureProtocol = secureUrls(ldapUrls);
if (secureProtocol && instance != null) {
if (secureProtocol) {
builder.put(JAVA_NAMING_LDAP_FACTORY_SOCKET, LdapSslSocketFactory.class.getName());
} else {
logger.warn("LdapSslSocketFactory not used for LDAP connections");
if (logger.isDebugEnabled()) {
logger.debug("LdapSslSocketFactory: secureProtocol = [{}], instance != null [{}]", secureProtocol, instance != null);
logger.debug("LdapSslSocketFactory: secureProtocol = [{}]", secureProtocol);
}
}
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.inject.Scopes;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.support.AbstractShieldModule;
@ -20,10 +21,6 @@ public class SSLModule extends AbstractShieldModule {
@Override
protected void configure(boolean clientMode) {
if (SSLService.isSSLEnabled(settings)) {
bind(SSLService.class).asEagerSingleton();
} else {
bind(SSLService.class).toProvider(Providers.<SSLService>of(null));
}
bind(SSLService.class).in(Scopes.SINGLETON);
}
}

View File

@ -10,8 +10,6 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.authc.ldap.LdapModule;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import javax.net.ssl.*;
import java.io.FileInputStream;
@ -25,9 +23,6 @@ import java.util.Arrays;
public class SSLService extends AbstractComponent {
static final String[] DEFAULT_CIPHERS = new String[]{ "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" };
public static final String SHIELD_TRANSPORT_SSL = "shield.transport.ssl";
public static final String SHIELD_HTTP_SSL = "shield.http.ssl";
public static final String SHIELD_AUTHC_LDAP_URL = "shield.authc.ldap.url";
private final TrustManagerFactory trustFactory;
private final KeyManagerFactory keyManagerFactory;
@ -162,11 +157,4 @@ public class SSLService extends AbstractComponent {
throw new ElasticsearchException("Failed to initialize a TrustManagerFactory", e);
}
}
public static boolean isSSLEnabled(Settings settings) {
return settings.getAsBoolean(SHIELD_TRANSPORT_SSL, false) ||
settings.getAsBoolean(SHIELD_HTTP_SSL, false) ||
(LdapSslSocketFactory.secureUrls(settings.getAsArray(SHIELD_AUTHC_LDAP_URL)) &&
LdapModule.enabled(settings));
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.shield.transport.netty;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.netty.channel.ChannelPipeline;
import org.elasticsearch.common.netty.channel.ChannelPipelineFactory;
@ -26,18 +27,17 @@ public class NettySecuredHttpServerTransport extends NettyHttpServerTransport {
private final boolean ssl;
private final boolean ipFilterEnabled;
private final IPFilteringN2NAuthenticator authenticator;
private final SSLService sslService;
private final @Nullable IPFilteringN2NAuthenticator authenticator;
private final @Nullable SSLService sslService;
@Inject
public NettySecuredHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays,
@Nullable IPFilteringN2NAuthenticator authenticator, @Nullable SSLService sslService) {
@Nullable IPFilteringN2NAuthenticator authenticator, Provider<SSLService> sslServiceProvider) {
super(settings, networkService, bigArrays);
this.authenticator = authenticator;
this.ssl = settings.getAsBoolean("shield.http.ssl", false);
this.sslService = sslService;
assert !ssl || sslService != null : "ssl is enabled yet the ssl service is null";
this.ipFilterEnabled = settings.getAsBoolean("shield.transport.filter.enabled", true);
this.sslService = ssl ? sslServiceProvider.get() : null;
}
@Override

View File

@ -7,6 +7,7 @@ package org.elasticsearch.shield.transport.netty;
import org.elasticsearch.Version;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.netty.channel.ChannelPipeline;
import org.elasticsearch.common.netty.channel.ChannelPipelineFactory;
@ -27,19 +28,18 @@ import javax.net.ssl.SSLEngine;
public class NettySecuredTransport extends NettyTransport {
private final boolean ssl;
private final SSLService sslService;
private final boolean ipFilterEnabled;
private final IPFilteringN2NAuthenticator authenticator;
private final @Nullable SSLService sslService;
private final @Nullable IPFilteringN2NAuthenticator authenticator;
@Inject
public NettySecuredTransport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays, Version version,
@Nullable IPFilteringN2NAuthenticator authenticator, @Nullable SSLService sslService) {
@Nullable IPFilteringN2NAuthenticator authenticator, Provider<SSLService> sslServiceProvider) {
super(settings, threadPool, networkService, bigArrays, version);
this.authenticator = authenticator;
this.ipFilterEnabled = settings.getAsBoolean("shield.transport.filter.enabled", true);
this.sslService = sslService;
this.ssl = settings.getAsBoolean("shield.transport.ssl", false);
assert !ssl || sslService != null : "ssl is enabled yet the ssl service is null";
this.sslService = ssl ? sslServiceProvider.get() : null;
}
@Override

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.shield.authc;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.BytesStreamInput;
@ -21,7 +22,10 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.*;
@ -55,8 +59,12 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.type()).thenReturn("first");
secondRealm = mock(Realm.class);
when(secondRealm.type()).thenReturn("second");
realms = mock(Realms.class);
when(realms.realms()).thenReturn(new Realm[] {firstRealm, secondRealm});
realms = new Realms(ImmutableSettings.EMPTY, Collections.<String, Realm.Factory>emptyMap()) {
@Override
protected List<Realm> initRealms() {
return ImmutableList.of(firstRealm, secondRealm);
}
};
signatureService = mock(SignatureService.class);
auditTrail = mock(AuditTrail.class);
@ -317,8 +325,8 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService);
User user1 = new User.Simple("username", "r1", "r2");
when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.supports(token)).thenReturn(true);
when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.authenticate(token)).thenReturn(user1);
User user2 = service.authenticate("_action", message, User.SYSTEM);
assertThat(user1, sameInstance(user2));

View File

@ -0,0 +1,133 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.transport.TransportMessage;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
/**
*
*/
public class RealmsTests extends ElasticsearchTestCase {
private Map<String, Realm.Factory> factories;
@Before
public void init() throws Exception {
factories = new HashMap<>();
factories.put("esusers", new DummyRealm.Factory("esusers", true));
for (int i = 0; i < randomIntBetween(1, 5); i++) {
DummyRealm.Factory factory = new DummyRealm.Factory("type_" + i, rarely());
factories.put("type_" + i, factory);
}
}
@Test
public void testWithSettings() throws Exception {
ImmutableSettings.Builder builder = ImmutableSettings.builder();
List<Integer> orders = new ArrayList<>(factories.size() - 1);
for (int i = 0; i < factories.size() - 1; i++) {
orders.add(i);
}
Collections.shuffle(orders);
Map<Integer, Integer> orderToIndex = new HashMap<>();
for (int i = 0; i < factories.size() - 1; i++) {
builder.put("shield.authc.realms.realm_" + i + ".type", "type_" + i);
builder.put("shield.authc.realms.realm_" + i + ".order", orders.get(i));
orderToIndex.put(orders.get(i), i);
}
Realms realms = new Realms(builder.build(), factories);
int i = 0;
for (Realm realm : realms) {
assertThat(realm.order(), equalTo(i));
int index = orderToIndex.get(i);
assertThat(realm.type(), equalTo("type_" + index));
assertThat(realm.name(), equalTo("realm_" + index));
i++;
}
}
@Test(expected = ShieldSettingsException.class)
public void testWithSettings_WithMultipleInternalRealmsOfSameType() throws Exception {
ImmutableSettings.Builder builder = ImmutableSettings.builder();
builder.put("shield.authc.realms.realm_1.type", ESUsersRealm.TYPE);
builder.put("shield.authc.realms.realm_1.order", 0);
builder.put("shield.authc.realms.realm_2.type", ESUsersRealm.TYPE);
builder.put("shield.authc.realms.realm_2.order", 1);
new Realms(builder.build(), factories);
}
@Test
public void testWithEmptySettings() throws Exception {
Realms realms = new Realms(ImmutableSettings.EMPTY, factories);
Iterator<Realm> iter = realms.iterator();
assertThat(iter.hasNext(), is(true));
Realm realm = iter.next();
assertThat(realm, notNullValue());
assertThat(realm.type(), equalTo(ESUsersRealm.TYPE));
assertThat(realm.name(), equalTo("default_" + ESUsersRealm.TYPE));
assertThat(iter.hasNext(), is(false));
}
static class DummyRealm extends Realm {
public DummyRealm(String type, String name, Settings settings) {
super(type, name, settings);
}
@Override
public boolean supports(AuthenticationToken token) {
return false;
}
@Override
public AuthenticationToken token(RestRequest request) {
return null;
}
@Override
public AuthenticationToken token(TransportMessage message) {
return null;
}
@Override
public User authenticate(AuthenticationToken token) {
return null;
}
static class Factory extends Realm.Factory<DummyRealm> {
public Factory(String type, boolean internal) {
super(type, internal);
}
@Override
public DummyRealm create(String name, Settings settings) {
return new DummyRealm(type(), name, settings);
}
@Override
public DummyRealm createDefault(String name) {
return type().equals("esusers") ? new DummyRealm("esusers", name, ImmutableSettings.EMPTY) : null;
}
}
}
}

View File

@ -5,14 +5,17 @@
*/
package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.ldap.*;
import org.elasticsearch.shield.authc.ldap.LdapConnection;
import org.elasticsearch.shield.authc.ldap.LdapConnectionFactory;
import org.elasticsearch.shield.authc.ldap.LdapConnectionTests;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapConnection;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
import org.elasticsearch.shield.authc.support.ldap.LdapTest;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.hamcrest.Matchers;
@ -28,18 +31,18 @@ import static org.hamcrest.Matchers.*;
@Network
public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
public static final String AD_LDAP_URL = "ldaps://54.213.145.20:636";
public static final String PASSWORD = "NickFuryHeartsES";
public static final String AD_DOMAIN = "ad.test.elasticsearch.com";
public static String SETTINGS_PREFIX = ActiveDirectoryRealm.class.getPackage().getName().substring("com.elasticsearch.".length()) + '.';
@BeforeClass
public static void setTrustStore() throws URISyntaxException {
File filename = new File(LdapConnectionTests.class.getResource("../support/ldap/ldaptrust.jks").toURI()).getAbsoluteFile();
LdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
LdapSslSocketFactory.init(Providers.of(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", filename)
.put("shield.ssl.keystore.password", "changeit")
.build()));
.build())));
}
@AfterClass
@ -47,7 +50,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
LdapSslSocketFactory.clear();
}
@Test
@Test @SuppressWarnings("unchecked")
public void testAdAuth() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN));
@ -64,8 +67,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
containsString("SHIELD"),
containsString("Users"),
containsString("Domain Users"),
containsString("Supers")
));
containsString("Supers")));
}
}
@ -82,11 +84,10 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
}
}
@Test
@Test @SuppressWarnings("unchecked")
public void testAdAuth_specificUserSearch() {
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(
buildAdSettings(AD_LDAP_URL, AD_DOMAIN,
"CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com"));
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com");
ActiveDirectoryConnectionFactory connectionFactory = new ActiveDirectoryConnectionFactory(settings);
String userName = "hulk";
try (AbstractLdapConnection ldap = connectionFactory.open(userName, SecuredStringTests.build(PASSWORD))) {
@ -99,18 +100,16 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
containsString("Philanthropists"),
//containsString("Users"), Users group is in a different user context
containsString("Domain Users"),
containsString("Supers")
));
containsString("Supers")));
}
}
@Test
@Test @SuppressWarnings("unchecked")
public void testAD_standardLdapConnection(){
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
boolean isSubTreeSearch = true;
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(
LdapTest.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, isSubTreeSearch));
Settings settings = LdapTest.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, true);
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(settings);
String user = "Bruce Banner";
try (LdapConnection ldap = connectionFactory.open(user, SecuredStringTests.build(PASSWORD))) {
@ -122,6 +121,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
containsString("SHIELD"),
containsString("Geniuses"),
containsString("Philanthropists")));
assertThat(groups2, containsInAnyOrder(
containsString("Avengers"),
containsString("SHIELD"),
@ -132,16 +132,16 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
public static Settings buildAdSettings(String ldapUrl, String adDomainName) {
return ImmutableSettings.builder()
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.build();
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN) {
return ImmutableSettings.builder()
.putArray(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(SETTINGS_PREFIX + ActiveDirectoryConnectionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
.putArray(ActiveDirectoryConnectionFactory.URLS_SETTING, ldapUrl)
.put(ActiveDirectoryConnectionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectoryConnectionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
.build();
}
}

View File

@ -1,92 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.active_directory;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Guice;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.mock;
/**
*
*/
public class ActiveDirectoryModuleTests extends ElasticsearchTestCase {
private ThreadPool threadPool;
@Before
public void init() throws Exception {
threadPool = new ThreadPool("test");
}
@After
public void shutdown() {
threadPool.shutdownNow();
}
@Test
public void testEnabled() throws Exception {
assertThat(ActiveDirectoryModule.enabled(ImmutableSettings.EMPTY), is(false));
Settings settings = ImmutableSettings.builder()
.put("shield.authc", false)
.build();
assertThat(ActiveDirectoryModule.enabled(settings), is(false));
settings = ImmutableSettings.builder()
.put("shield.authc.active_directory.enabled", false)
.build();
assertThat(ActiveDirectoryModule.enabled(settings), is(false));
settings = ImmutableSettings.builder()
.put("shield.authc.active_directory.enabled", true)
.build();
assertThat(ActiveDirectoryModule.enabled(settings), is(true));
}
@Test
public void test() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("client.type", "node")
.put("shield.authc.active_directory.url", "ldap://example.com:389")
.put("shield.authc.active_directory.domain_name", "example.com")
.build();
Injector injector = Guice.createInjector(new TestModule(settings), new ActiveDirectoryModule(settings));
ActiveDirectoryRealm realm = injector.getInstance(ActiveDirectoryRealm.class);
assertThat(realm, notNullValue());
}
public class TestModule extends AbstractModule {
private final Settings settings;
public TestModule(Settings settings) {
this.settings = settings;
}
@Override
protected void configure() {
Environment env = new Environment(settings);
bind(Settings.class).toInstance(settings);
bind(Environment.class).toInstance(env);
bind(ThreadPool.class).toInstance(threadPool);
bind(ResourceWatcherService.class).asEagerSingleton();
bind(RestController.class).toInstance(mock(RestController.class));
bind(SSLService.class).toProvider(Providers.<SSLService>of(null));
}
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.esusers;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Guice;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
/**
*
*/
public class ESUsersModuleTests extends ElasticsearchTestCase {
private Path users;
private Path usersRoles;
private ThreadPool threadPool;
@Before
public void init() throws Exception {
users = Paths.get(getClass().getResource("users").toURI());
usersRoles = Paths.get(getClass().getResource("users_roles").toURI());
threadPool = new ThreadPool("test");
}
@After
public void shutdown() {
threadPool.shutdownNow();
}
@Test
public void test() throws Exception {
Settings settings = ImmutableSettings.builder().put("client.type", "node").build();
Injector injector = Guice.createInjector(new TestModule(users, usersRoles, threadPool), new ESUsersModule(settings));
ESUsersRealm realm = injector.getInstance(ESUsersRealm.class);
assertThat(realm, notNullValue());
assertThat(realm.userPasswdStore, notNullValue());
assertThat(realm.userPasswdStore, instanceOf(FileUserPasswdStore.class));
assertThat(realm.userRolesStore, notNullValue());
assertThat(realm.userRolesStore, instanceOf(FileUserRolesStore.class));
}
@Test
public void testEnabled() throws Exception {
assertThat(ESUsersModule.enabled(ImmutableSettings.EMPTY), is(true));
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.enabled", false)
.build();
assertThat(ESUsersModule.enabled(settings), is(false));
settings = ImmutableSettings.builder()
.put("shield.authc.esusers.enabled", true)
.build();
assertThat(ESUsersModule.enabled(settings), is(true));
}
public static class TestModule extends AbstractModule {
final Path users;
final Path usersRoles;
private ThreadPool threadPool;
public TestModule(Path users, Path usersRoles, ThreadPool threadPool) {
this.users = users;
this.usersRoles = usersRoles;
this.threadPool = threadPool;
}
@Override
protected void configure() {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.file.users", users.toAbsolutePath())
.put("shield.authc.esusers.file.users_roles", usersRoles.toAbsolutePath())
.build();
Environment env = new Environment(settings);
bind(Settings.class).toInstance(settings);
bind(Environment.class).toInstance(env);
bind(ThreadPool.class).toInstance(threadPool);
bind(ResourceWatcherService.class).asEagerSingleton();
bind(RestController.class).toInstance(mock(RestController.class));
}
}
}

View File

@ -60,16 +60,15 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
@Test
public void testRestHeaderRegistration() {
new ESUsersRealm(ImmutableSettings.EMPTY, mock(FileUserPasswdStore.class), mock(FileUserRolesStore.class), restController);
new ESUsersRealm.Factory(mock(Environment.class), mock(ResourceWatcherService.class), restController);
verify(restController).registerRelevantHeaders(UsernamePasswordToken.BASIC_AUTH_HEADER);
}
@Test
public void testAuthenticate() throws Exception {
Settings settings = ImmutableSettings.builder().build();
when(userPasswdStore.verifyPassword("user1", SecuredStringTests.build("test123"))).thenReturn(true);
when(userRolesStore.roles("user1")).thenReturn(new String[] { "role1", "role2" });
ESUsersRealm realm = new ESUsersRealm(settings, userPasswdStore, userRolesStore, restController);
ESUsersRealm realm = new ESUsersRealm("esusers-test", ImmutableSettings.EMPTY, userPasswdStore, userRolesStore);
User user = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
assertThat(user, notNullValue());
assertThat(user.principal(), equalTo("user1"));
@ -81,11 +80,11 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
@Test @Repeat(iterations = 20)
public void testAuthenticate_Caching() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.cache.hash_algo", Hasher.values()[randomIntBetween(0, Hasher.values().length - 1)].name().toLowerCase(Locale.ROOT))
.put("cache.hash_algo", Hasher.values()[randomIntBetween(0, Hasher.values().length - 1)].name().toLowerCase(Locale.ROOT))
.build();
when(userPasswdStore.verifyPassword("user1", SecuredStringTests.build("test123"))).thenReturn(true);
when(userRolesStore.roles("user1")).thenReturn(new String[] { "role1", "role2" });
ESUsersRealm realm = new ESUsersRealm(settings, userPasswdStore, userRolesStore, restController);
ESUsersRealm realm = new ESUsersRealm("esusers-test", settings, userPasswdStore, userRolesStore);
User user1 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
User user2 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
assertThat(user1, sameInstance(user2));
@ -96,7 +95,7 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
userRolesStore = spy(new UserRolesStore());
doReturn(true).when(userPasswdStore).verifyPassword("user1", SecuredStringTests.build("test123"));
doReturn(new String[] { "role1", "role2" }).when(userRolesStore).roles("user1");
ESUsersRealm realm = new ESUsersRealm(ImmutableSettings.EMPTY, userPasswdStore, userRolesStore, restController);
ESUsersRealm realm = new ESUsersRealm("esusers-test", ImmutableSettings.EMPTY, userPasswdStore, userRolesStore);
User user1 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
User user2 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
assertThat(user1, sameInstance(user2));
@ -114,10 +113,9 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
@Test
public void testToken() throws Exception {
Settings settings = ImmutableSettings.builder().build();
when(userPasswdStore.verifyPassword("user1", SecuredStringTests.build("test123"))).thenReturn(true);
when(userRolesStore.roles("user1")).thenReturn(new String[] { "role1", "role2" });
ESUsersRealm realm = new ESUsersRealm(settings, userPasswdStore, userRolesStore, restController);
ESUsersRealm realm = new ESUsersRealm("esusers-test", ImmutableSettings.EMPTY, userPasswdStore, userRolesStore);
TransportRequest request = new TransportRequest() {};
UsernamePasswordToken.putTokenHeader(request, new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
@ -132,7 +130,7 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
@Test @SuppressWarnings("unchecked")
public void testRestHeadersAreCopied() throws Exception {
// the required header will be registered only if ESUsersRealm is actually used.
new ESUsersRealm(ImmutableSettings.EMPTY, new UserPasswdStore(), new UserRolesStore(), restController);
new ESUsersRealm("esusers-test", ImmutableSettings.EMPTY, new UserPasswdStore(), new UserRolesStore());
when(restController.relevantHeaders()).thenReturn(ImmutableSet.of(UsernamePasswordToken.BASIC_AUTH_HEADER));
when(client.admin()).thenReturn(adminClient);
when(adminClient.cluster()).thenReturn(mock(ClusterAdminClient.class));

View File

@ -69,14 +69,18 @@ public class FileUserPasswdStoreTests extends ElasticsearchTestCase {
Settings settings = ImmutableSettings.builder()
.put("watcher.interval.high", "2s")
.put("shield.authc.esusers.files.users", tmp.toAbsolutePath())
.build();
Settings esusersSettings = ImmutableSettings.builder()
.put("files.users", tmp.toAbsolutePath())
.build();
Environment env = new Environment(settings);
threadPool = new ThreadPool("test");
watcherService = new ResourceWatcherService(settings, threadPool);
final CountDownLatch latch = new CountDownLatch(1);
FileUserPasswdStore store = new FileUserPasswdStore(settings, env, watcherService, new RefreshListener() {
FileUserPasswdStore store = new FileUserPasswdStore(esusersSettings, env, watcherService, new RefreshListener() {
@Override
public void onRefresh() {
latch.countDown();

View File

@ -62,14 +62,17 @@ public class FileUserRolesStoreTests extends ElasticsearchTestCase {
Settings settings = ImmutableSettings.builder()
.put("watcher.interval.high", "2s")
.put("shield.authc.esusers.files.users_roles", tmp.toAbsolutePath())
.build();
Settings esusersSettings = ImmutableSettings.builder()
.put("files.users_roles", tmp.toAbsolutePath())
.build();
Environment env = new Environment(settings);
threadPool = new ThreadPool("test");
watcherService = new ResourceWatcherService(settings, threadPool);
final CountDownLatch latch = new CountDownLatch(1);
FileUserRolesStore store = new FileUserRolesStore(settings, env, watcherService, new RefreshListener() {
FileUserRolesStore store = new FileUserRolesStore(esusersSettings, env, watcherService, new RefreshListener() {
@Override
public void onRefresh() {
latch.countDown();
@ -114,13 +117,18 @@ public class FileUserRolesStoreTests extends ElasticsearchTestCase {
try {
threadPool = new ThreadPool("test");
File usersRoles = writeUsersRoles("role1:admin");
Settings settings = ImmutableSettings.builder()
.put("watcher.enabled", "false")
.put("shield.authc.esusers.files.users_roles", usersRoles.toPath().toAbsolutePath())
.build();
Settings esusersSettings = ImmutableSettings.builder()
.put("files.users_roles", usersRoles.toPath().toAbsolutePath())
.build();
Environment env = new Environment(settings);
ResourceWatcherService watcherService = new ResourceWatcherService(settings, threadPool);
FileUserRolesStore store = new FileUserRolesStore(settings, env, watcherService);
FileUserRolesStore store = new FileUserRolesStore(esusersSettings, env, watcherService);
assertThat(store.roles("user"), equalTo(Strings.EMPTY_ARRAY));
} finally {
if (threadPool != null) {

View File

@ -76,8 +76,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = new File(tmpFolder, "users");
File userRolesFile = new File(tmpFolder, "users_roles");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new MockTerminal(), "user1", SecuredStringTests.build("changeme"), "r1", "r2");
@ -106,8 +107,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = writeFile("user2:hash2");
File userRolesFile = writeFile("r3:user2\nr4:user2");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new MockTerminal(), "user1", SecuredStringTests.build("changeme"), "r1", "r2");
@ -141,8 +143,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = writeFile("user2:hash2");
File userRolesFile = writeFile("r3:user2\nr4:user2");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new MockTerminal(), "user1", SecuredStringTests.build("changeme"));
@ -161,8 +164,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = writeFile("user1:hash1");
File userRolesFile = newTempFile();
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new MockTerminal(), "user1", SecuredStringTests.build("changeme"), "r1", "r2");
@ -194,8 +198,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = writeFile("user1:hash2");
File userRolesFile = writeFile("r3:user1\nr4:user1");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Userdel cmd = new ESUsersTool.Userdel(new MockTerminal(), "user1");
@ -217,8 +222,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = writeFile("user1:hash2");
File userRolesFile = writeFile("r3:user1\nr4:user1");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
CaptureOutputTerminal terminal = new CaptureOutputTerminal();
@ -246,8 +252,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File userFile = new File(dir, "users");
File userRolesFile = new File(dir, "users_roles");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.esusers.files.users_roles", userRolesFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.files.users_roles", userRolesFile)
.build();
ESUsersTool.Userdel cmd = new ESUsersTool.Userdel(new MockTerminal(), "user2");
@ -301,7 +308,8 @@ public class ESUsersToolTests extends CliToolTestCase {
public void testPasswd_Cmd() throws Exception {
File userFile = writeFile("user1:hash2");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.build();
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new MockTerminal(), "user1", "changeme".toCharArray());
@ -322,7 +330,8 @@ public class ESUsersToolTests extends CliToolTestCase {
public void testPasswd_Cmd_UnknownUser() throws Exception {
File userFile = writeFile("user1:hash2");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.build();
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new MockTerminal(), "user2", "changeme".toCharArray());
@ -334,7 +343,8 @@ public class ESUsersToolTests extends CliToolTestCase {
public void testPasswd_Cmd_MissingFiles() throws Exception {
File userFile = newTempFile();
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", userFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", userFile)
.build();
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new MockTerminal(), "user2", "changeme".toCharArray());
@ -359,8 +369,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash");
File usersRoleFile = writeFile("admin: admin\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
// invalid role names
@ -383,8 +394,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash\nuser:hash");
File usersRoleFile = writeFile("admin: admin\nuser: user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new MockTerminal(), "user", new String[]{"foo"}, Strings.EMPTY_ARRAY);
@ -404,8 +416,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash\nuser:hash");
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo: user\nbar: user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new MockTerminal(), "user", Strings.EMPTY_ARRAY, new String[]{"foo"});
@ -425,8 +438,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash\nuser:hash");
File usersRoleFile = writeFile("admin: admin\nuser:user\nfoo:user\nbar:user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new MockTerminal(), "user", new String[]{"newrole"}, new String[]{"foo"});
@ -446,8 +460,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash\nuser:hash");
File usersRoleFile = writeFile("admin: admin\nuser:user\nfoo:user\nbar:user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new MockTerminal(), "user", Strings.EMPTY_ARRAY, new String[]{"user", "foo", "bar"});
@ -464,8 +479,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:hash\nuser:hash");
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo:user\nbar:user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new MockTerminal(), "does-not-exist", Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY);
@ -480,8 +496,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("admin: admin\nuser:user\nfoo:user\nbar:user\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all\n\nfoo:\n cluster: all\n\nbar:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -499,8 +516,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("admin: admin\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nmyrole:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -529,7 +547,8 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo:user\nbar:user\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all\n\nfoo:\n cluster: all\n\nbar:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -548,7 +567,8 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo:user\nbar:user\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -568,8 +588,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:{plain}changeme\nuser:{plain}changeme\nno-roles-user:{plain}changeme\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all\n\nfoo:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -587,7 +608,8 @@ public class ESUsersToolTests extends CliToolTestCase {
public void testListUsersAndRoles_Cmd_listSingleUserNotFound() throws Exception {
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo:user\nbar:user\n");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.build();
CaptureOutputTerminal catchTerminalOutput = new CaptureOutputTerminal();
@ -603,8 +625,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("admin: admin\nuser: user\nfoo:user\nbar:user\n");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all\n\nfoo:\n cluster: all\n\nbar:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -625,8 +648,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersRoleFile = writeFile("");
File rolesFile = writeFile("admin:\n cluster: all\n\nuser:\n cluster: all\n\nfoo:\n cluster: all\n\nbar:\n cluster: all");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.files.users", usersFile)
.put("shield.authz.store.files.roles", rolesFile)
.build();
@ -646,8 +670,9 @@ public class ESUsersToolTests extends CliToolTestCase {
File usersFile = writeFile("admin:{plain}changeme");
File usersRoleFile = writeFile("");
Settings settings = ImmutableSettings.builder()
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.esusers.files.users", usersFile)
.put("shield.authc.realms.esusers.type", "esusers")
.put("shield.authc.realms.esusers.files.users_roles", usersRoleFile)
.put("shield.authc.realms.esusers.files.users", usersFile)
.build();
CaptureOutputTerminal loggingTerminal = new CaptureOutputTerminal();

View File

@ -19,23 +19,21 @@ public class LdapConnectionTests extends LdapTest {
@Test
public void testBindWithTemplates() {
String[] ldapUrls = new String[]{ldapUrl()};
String[] ldapUrls = new String[] { ldapUrl() };
String groupSearchBase = "o=sevenSeas";
boolean isSubTreeSearch = true;
String[] userTemplates = new String[]{
String[] userTemplates = new String[] {
"cn={0},ou=something,ou=obviously,ou=incorrect,o=sevenSeas",
"wrongname={0},ou=people,o=sevenSeas",
"cn={0},ou=people,o=sevenSeas", //this last one should work
};
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(
buildLdapSettings(ldapUrls, userTemplates, groupSearchBase, isSubTreeSearch));
buildLdapSettings(ldapUrls, userTemplates, groupSearchBase, true));
String user = "Horatio Hornblower";
SecuredString userPass = SecuredStringTests.build("pass");
try (LdapConnection ldap = connectionFactory.open(user, userPass)) {
String dn = ldap.authenticatedUserDn();
assertThat(dn, containsString(user));
//assertThat( attrs.get("uid"), arrayContaining("hhornblo"));
}
@ -43,10 +41,10 @@ public class LdapConnectionTests extends LdapTest {
@Test(expected = LdapException.class)
public void testBindWithBogusTemplates() {
String[] ldapUrl = new String[]{ldapUrl()};
String[] ldapUrl = new String[] { ldapUrl() };
String groupSearchBase = "o=sevenSeas";
boolean isSubTreeSearch = true;
String[] userTemplates = new String[]{
String[] userTemplates = new String[] {
"cn={0},ou=something,ou=obviously,ou=incorrect,o=sevenSeas",
"wrongname={0},ou=people,o=sevenSeas",
"asdf={0},ou=people,o=sevenSeas", //none of these should work
@ -65,9 +63,8 @@ public class LdapConnectionTests extends LdapTest {
String groupSearchBase = "o=sevenSeas";
String userTemplate = "cn={0},ou=people,o=sevenSeas";
boolean isSubTreeSearch = true;
LdapConnectionFactory ldapFac = new LdapConnectionFactory(
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, true));
String user = "Horatio Hornblower";
SecuredString userPass = SecuredStringTests.build("pass");
@ -82,9 +79,8 @@ public class LdapConnectionTests extends LdapTest {
public void testGroupLookup_OneLevel() {
String groupSearchBase = "ou=crews,ou=groups,o=sevenSeas";
String userTemplate = "cn={0},ou=people,o=sevenSeas";
boolean isSubTreeSearch = false;
LdapConnectionFactory ldapFac = new LdapConnectionFactory(
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, false));
String user = "Horatio Hornblower";
try (LdapConnection ldap = ldapFac.open(user, SecuredStringTests.build("pass"))) {

View File

@ -55,7 +55,7 @@ public class LdapGroupToRoleMapperTest extends ElasticsearchTestCase {
public void testYaml() throws IOException {
File file = this.getResource("../support/ldap/role_mapping.yml");
Settings settings = ImmutableSettings.settingsBuilder()
.put("shield.authc.ldap." + LdapGroupToRoleMapper.ROLE_MAPPING_FILE_SETTING, file.getCanonicalPath())
.put(LdapGroupToRoleMapper.ROLE_MAPPING_FILE_SETTING, file.getCanonicalPath())
.build();
AbstractGroupToRoleMapper mapper = new LdapGroupToRoleMapper(settings,
@ -71,7 +71,7 @@ public class LdapGroupToRoleMapperTest extends ElasticsearchTestCase {
@Test
public void testRelativeDN() {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.ldap." + AbstractGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.put(AbstractGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.build();
AbstractGroupToRoleMapper mapper = new LdapGroupToRoleMapper(settings,

View File

@ -1,91 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Guice;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.mock;
/**
*
*/
public class LdapModuleTests extends ElasticsearchTestCase {
private ThreadPool threadPool;
@Before
public void init() throws Exception {
threadPool = new ThreadPool("test");
}
@After
public void shutdown() {
threadPool.shutdownNow();
}
@Test
public void testEnabled() throws Exception {
assertThat(LdapModule.enabled(ImmutableSettings.EMPTY), is(false));
Settings settings = ImmutableSettings.builder()
.put("shield.authc", false)
.build();
assertThat(LdapModule.enabled(settings), is(false));
settings = ImmutableSettings.builder()
.put("shield.authc.ldap.enabled", false)
.build();
assertThat(LdapModule.enabled(settings), is(false));
settings = ImmutableSettings.builder()
.put("shield.authc.ldap.enabled", true)
.build();
assertThat(LdapModule.enabled(settings), is(true));
}
@Test
public void test() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("client.type", "node")
.put("shield.authc.ldap.url", "ldap://example.com:389")
.build();
Injector injector = Guice.createInjector(new TestModule(settings), new LdapModule(settings));
LdapRealm realm = injector.getInstance(LdapRealm.class);
assertThat(realm, notNullValue());
}
public class TestModule extends AbstractModule {
private final Settings settings;
public TestModule(Settings settings) {
this.settings = settings;
}
@Override
protected void configure() {
Environment env = new Environment(settings);
bind(Settings.class).toInstance(settings);
bind(Environment.class).toInstance(env);
bind(ThreadPool.class).toInstance(threadPool);
bind(ResourceWatcherService.class).asEagerSingleton();
bind(RestController.class).toInstance(mock(RestController.class));
bind(SSLService.class).toProvider(Providers.<SSLService>of(null));
}
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authc.support.SecuredString;
@ -26,6 +27,7 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class LdapRealmTest extends LdapTest {
public static final String VALID_USER_TEMPLATE = "cn={0},ou=people,o=sevenSeas";
public static final String VALID_USERNAME = "Thomas Masterman Hardy";
public static final String PASSWORD = "pass";
@ -49,7 +51,7 @@ public class LdapRealmTest extends LdapTest {
@Test
public void testRestHeaderRegistration() {
new LdapRealm(ImmutableSettings.EMPTY, mock(LdapConnectionFactory.class), mock(LdapGroupToRoleMapper.class), restController);
new LdapRealm.Factory(mock(Environment.class), resourceWatcherService, restController);
verify(restController).registerRelevantHeaders(UsernamePasswordToken.BASIC_AUTH_HEADER);
}
@ -61,7 +63,7 @@ public class LdapRealmTest extends LdapTest {
Settings settings = buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch);
LdapConnectionFactory ldapFactory = new LdapConnectionFactory(settings);
LdapRealm ldap = new LdapRealm(buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), restController);
LdapRealm ldap = new LdapRealm("test-ldap-realm", buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
User user = ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
assertThat( user, notNullValue());
@ -76,7 +78,7 @@ public class LdapRealmTest extends LdapTest {
LdapConnectionFactory ldapFactory = new LdapConnectionFactory(
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
LdapRealm ldap = new LdapRealm(buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), restController);
LdapRealm ldap = new LdapRealm("test-ldap-realm", buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
User user = ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
assertThat( user, notNullValue());
@ -92,7 +94,7 @@ public class LdapRealmTest extends LdapTest {
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch) );
ldapFactory = spy(ldapFactory);
LdapRealm ldap = new LdapRealm( buildCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), restController);
LdapRealm ldap = new LdapRealm("test-ldap-realm", buildCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
User user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
@ -111,7 +113,7 @@ public class LdapRealmTest extends LdapTest {
LdapGroupToRoleMapper roleMapper = buildGroupAsRoleMapper(resourceWatcherService);
ldapFactory = spy(ldapFactory);
LdapRealm ldap = new LdapRealm( buildCachingSettings(), ldapFactory, roleMapper, restController);
LdapRealm ldap = new LdapRealm("test-ldap-realm", buildCachingSettings(), ldapFactory, roleMapper);
User user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
@ -135,7 +137,7 @@ public class LdapRealmTest extends LdapTest {
buildLdapSettings(ldapUrl(), userTemplate, groupSearchBase, isSubTreeSearch));
ldapFactory = spy(ldapFactory);
LdapRealm ldap = new LdapRealm( buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), restController);
LdapRealm ldap = new LdapRealm("test-ldap-realm", buildNonCachingSettings(), ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
User user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
user = ldap.authenticate( new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.shield.authc.ldap;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
@ -23,16 +24,17 @@ import static org.hamcrest.Matchers.hasItem;
@Network
public class OpenLdapTests extends ElasticsearchTestCase {
public static final String OPEN_LDAP_URL = "ldaps://54.200.235.244:636";
public static final String PASSWORD = "NickFuryHeartsES";
@BeforeClass
public static void setTrustStore() throws URISyntaxException {
File filename = new File(LdapConnectionTests.class.getResource("../support/ldap/ldaptrust.jks").toURI()).getAbsoluteFile();
LdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
LdapSslSocketFactory.init(Providers.of(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", filename)
.put("shield.ssl.keystore.password", "changeit")
.build()));
.build())));
}
@AfterClass
@ -41,17 +43,16 @@ public class OpenLdapTests extends ElasticsearchTestCase {
}
@Test
public void test_standardLdapConnection_uid(){
public void test_standardLdapConnection_uid() {
//openldap does not use cn as naming attributes by default
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
boolean isSubTreeSearch = true;
LdapConnectionFactory connectionFactory = new LdapConnectionFactory(
LdapConnectionTests.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, isSubTreeSearch));
LdapConnectionTests.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, true));
String[] users = new String[]{"blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor"};
for(String user: users) {
String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" };
for (String user : users) {
LdapConnection ldap = connectionFactory.open(user, SecuredStringTests.build(PASSWORD));
assertThat(ldap.groups(), hasItem(containsString("Avengers")));
ldap.close();

View File

@ -68,24 +68,19 @@ public class CachingUsernamePasswordRealmTests extends ElasticsearchTestCase {
static class FailingAuthenticationRealm extends CachingUsernamePasswordRealm {
FailingAuthenticationRealm(Settings settings) {
super(settings);
super("failing", "failing-test", settings);
}
@Override
protected User doAuthenticate(UsernamePasswordToken token) {
return null;
}
@Override
public String type() {
return "failing";
}
}
static class ThrowingAuthenticationRealm extends CachingUsernamePasswordRealm {
ThrowingAuthenticationRealm(Settings settings) {
super(settings);
super("throwing", "throwing-test", settings);
}
@Override
@ -93,10 +88,6 @@ public class CachingUsernamePasswordRealmTests extends ElasticsearchTestCase {
throw new RuntimeException("whatever exception");
}
@Override
public String type() {
return "throwing";
}
}
static class AlwaysAuthenticateCachingRealm extends CachingUsernamePasswordRealm {
@ -104,7 +95,7 @@ public class CachingUsernamePasswordRealmTests extends ElasticsearchTestCase {
public final AtomicInteger INVOCATION_COUNTER = new AtomicInteger(0);
AlwaysAuthenticateCachingRealm() {
super(ImmutableSettings.EMPTY);
super("always", "always-test", ImmutableSettings.EMPTY);
}
@Override
@ -112,10 +103,5 @@ public class CachingUsernamePasswordRealmTests extends ElasticsearchTestCase {
INVOCATION_COUNTER.incrementAndGet();
return new User.Simple(token.principal(), "testRole1", "testRole2");
}
@Override
public String type() {
return "always";
}
}
}

View File

@ -202,7 +202,8 @@ public class ApacheDsEmbedded {
*
* @param workDir the directory to be used for storing the data
* @param baseDn
* @param ldifFileName @throws Exception if there were some problems while initializing the system
* @param ldifFileName
* @throws Exception if there were some problems while initializing the system
*/
private void initDirectoryService(File workDir, String baseDn, String ldifFileName) throws Exception {
// Initialize the LDAP service

View File

@ -6,10 +6,11 @@
package org.elasticsearch.shield.authc.support.ldap;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.shield.authc.ldap.LdapConnectionTests;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
@ -27,10 +28,10 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
@BeforeClass
public static void setTrustStore() throws URISyntaxException {
File filename = new File(LdapConnectionTests.class.getResource("../support/ldap/ldaptrust.jks").toURI()).getAbsoluteFile();
LdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
LdapSslSocketFactory.init(Providers.of(new SSLService(ImmutableSettings.builder()
.put("shield.ssl.keystore.path", filename)
.put("shield.ssl.keystore.password", "changeit")
.build()));
.build())));
}
@AfterClass
@ -39,19 +40,19 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
}
@Test
public void testConfigure_1ldaps(){
String[] urls = new String[]{"ldaps://example.com:636"};
public void testConfigure_1ldaps() {
String[] urls = new String[] { "ldaps://example.com:636" };
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.builder();
LdapSslSocketFactory.configureJndiSSL(urls, builder);
ImmutableMap<String, Serializable> settings = builder.build();
assertThat(settings.get(LdapSslSocketFactory.JAVA_NAMING_LDAP_FACTORY_SOCKET),
Matchers.<Serializable>equalTo("org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory"));
assertThat((String) settings.get(LdapSslSocketFactory.JAVA_NAMING_LDAP_FACTORY_SOCKET),
equalTo("org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory"));
}
@Test
public void testConfigure_2ldaps(){
String[] urls = new String[]{"ldaps://primary.example.com:636", "LDAPS://secondary.example.com:10636"};
public void testConfigure_2ldaps() {
String[] urls = new String[] { "ldaps://primary.example.com:636", "LDAPS://secondary.example.com:10636" };
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
LdapSslSocketFactory.configureJndiSSL(urls, builder);
@ -60,8 +61,8 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
}
@Test
public void testConfigure_2ldap(){
String[] urls = new String[]{"ldap://primary.example.com:392", "LDAP://secondary.example.com:10392"};
public void testConfigure_2ldap() {
String[] urls = new String[] { "ldap://primary.example.com:392", "LDAP://secondary.example.com:10392" };
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
LdapSslSocketFactory.configureJndiSSL(urls, builder);
@ -70,16 +71,16 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
}
@Test(expected = ShieldSettingsException.class)
public void testConfigure_1ldaps_1ldap(){
String[] urls = new String[]{"LDAPS://primary.example.com:636", "ldap://secondary.example.com:392"};
public void testConfigure_1ldaps_1ldap() {
String[] urls = new String[] { "LDAPS://primary.example.com:636", "ldap://secondary.example.com:392" };
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
LdapSslSocketFactory.configureJndiSSL(urls, builder);
}
@Test(expected = ShieldSettingsException.class)
public void testConfigure_1ldap_1ldaps(){
String[] urls = new String[]{"ldap://primary.example.com:392", "ldaps://secondary.example.com:636"};
public void testConfigure_1ldap_1ldaps() {
String[] urls = new String[] { "ldap://primary.example.com:392", "ldaps://secondary.example.com:636" };
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
LdapSslSocketFactory.configureJndiSSL(urls, builder);

View File

@ -24,8 +24,6 @@ import org.junit.Ignore;
@ThreadLeakFilters(defaultFilters = true, filters = { LdapTest.ApachedsThreadLeakFilter.class })
public abstract class LdapTest extends ElasticsearchTestCase {
static String SETTINGS_PREFIX = LdapRealm.class.getPackage().getName().substring("com.elasticsearch.".length()) + '.';
private static ApacheDsEmbedded ldap;
@BeforeClass
@ -50,15 +48,15 @@ public abstract class LdapTest extends ElasticsearchTestCase {
public static Settings buildLdapSettings(String[] ldapUrl, String[] userTemplate, String groupSearchBase, boolean isSubTreeSearch) {
return ImmutableSettings.builder()
.putArray(SETTINGS_PREFIX + LdapConnectionFactory.URLS_SETTING, ldapUrl)
.putArray(SETTINGS_PREFIX + LdapConnectionFactory.USER_DN_TEMPLATES_SETTING, userTemplate)
.put(SETTINGS_PREFIX + LdapConnectionFactory.GROUP_SEARCH_BASEDN_SETTING, groupSearchBase)
.put(SETTINGS_PREFIX + LdapConnectionFactory.GROUP_SEARCH_SUBTREE_SETTING, isSubTreeSearch).build();
.putArray(LdapConnectionFactory.URLS_SETTING, ldapUrl)
.putArray(LdapConnectionFactory.USER_DN_TEMPLATES_SETTING, userTemplate)
.put(LdapConnectionFactory.GROUP_SEARCH_BASEDN_SETTING, groupSearchBase)
.put(LdapConnectionFactory.GROUP_SEARCH_SUBTREE_SETTING, isSubTreeSearch).build();
}
protected Settings buildNonCachingSettings() {
return ImmutableSettings.builder()
.put("shield.authc.ldap."+LdapRealm.CACHE_TTL, -1)
.put(LdapRealm.CACHE_TTL, -1)
.build();
}
@ -69,7 +67,7 @@ public abstract class LdapTest extends ElasticsearchTestCase {
protected LdapGroupToRoleMapper buildGroupAsRoleMapper(ResourceWatcherService resourceWatcherService) {
Settings settings = ImmutableSettings.builder()
.put("shield.authc.ldap." + AbstractGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.put(AbstractGroupToRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.build();
return new LdapGroupToRoleMapper(settings, new Environment(settings), resourceWatcherService);

View File

@ -6,7 +6,6 @@
package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Before;
import org.junit.Ignore;
@ -50,68 +49,6 @@ public class SSLServiceTests extends ElasticsearchTestCase {
assertThat(ssl.createSSLEngine().getSSLParameters().getProtocols(), arrayContaining("TLSv1.2"));
}
@Test
public void testIsSSLDisabled_allDefaults(){
Settings settings = settingsBuilder().build();
assertSSLDisabled(settings);
}
@Test
public void testIsSSLEnabled_transportOffHttpOffLdapOff(){
Settings settings = settingsBuilder()
.put("shield.transport.ssl", false)
.put("shield.http.ssl", false)
.put("shield.authc.ldap.mode", "ldap")
.put("shield.authc.ldap.url", "ldap://example.com:389")
.build();
assertSSLDisabled(settings);
}
@Test
public void testIsSSLEnabled_transportOffHttpOffLdapMissingUrl() {
Settings settings = settingsBuilder()
.put("shield.transport.ssl", false)
.put("shield.http.ssl", false)
.put("shield.authc.ldap.mode", "active_dir") //SSL is on by default for a missing URL with active directory
.build();
assertSSLEnabled(settings);
}
@Test
public void testIsSSLEnabled_transportOffHttpOffLdapOn(){
Settings settings = settingsBuilder()
.put("shield.transport.ssl", false)
.put("shield.http.ssl", false)
.put("shield.authc.ldap.mode", "ldap")
.put("shield.authc.ldap.url", "ldaps://example.com:636")
.build();
assertSSLEnabled(settings);
}
@Test
public void testIsSSLEnabled_transportOffHttpOnLdapOff(){
Settings settings = settingsBuilder()
.put("shield.transport.ssl", false)
.put("shield.http.ssl", true)
.put("shield.authc.ldap.mode", "ldap")
.put("shield.authc.ldap.url", "ldap://example.com:389")
.build();
assertSSLEnabled(settings);
}
@Test
public void testIsSSLEnabled_transportOnHttpOffLdapOff(){
Settings settings = settingsBuilder()
.put("shield.transport.ssl", true)
.put("shield.http.ssl", false)
.put("shield.authc.ldap.mode", "ldap")
.put("shield.authc.ldap.url", "ldap://example.com:389")
.build();
assertSSLEnabled(settings);
}
@Test
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
File testClientStore = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks").toURI());
@ -128,12 +65,4 @@ public class SSLServiceTests extends ElasticsearchTestCase {
SSLEngine sslEngineWithTruststore = sslService.createSSLEngineWithTruststore(settingsBuilder.build());
assertThat(sslEngineWithTruststore, is(not(nullValue())));
}
private void assertSSLEnabled(Settings settings) {
assertThat(SSLService.isSSLEnabled(settings), is(true));
}
private void assertSSLDisabled(Settings settings) {
assertThat(SSLService.isSSLEnabled(settings), is(false));
}
}

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.signature.InternalSignatureService;
@ -89,8 +90,9 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
ImmutableSettings.Builder builder = ImmutableSettings.builder().put(super.node(nodeOrdinal))
.put("shield.audit.enabled", RandomizedTest.randomBoolean())
.put(InternalSignatureService.FILE_SETTING, writeFile(folder, "system_key", systemKey))
.put("shield.authc.esusers.files.users", writeFile(folder, "users", configUsers()))
.put("shield.authc.esusers.files.users_roles", writeFile(folder, "users_roles", configUsersRoles()))
.put("shield.authc.realms.esusers.type", ESUsersRealm.TYPE)
.put("shield.authc.realms.esusers.files.users", writeFile(folder, "users", configUsers()))
.put("shield.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", configUsersRoles()))
.put("shield.authz.store.files.roles", writeFile(folder, "roles.yml", configRoles()))
.put(getNodeSSLSettings());