Merge pull request elastic/elasticsearch#2844 from rjernst/deguice8

Remove guice from realms construction

Original commit: elastic/x-pack-elasticsearch@8bfeef931c
This commit is contained in:
Ryan Ernst 2016-07-18 13:55:12 -07:00 committed by GitHub
commit c3a3898da9
24 changed files with 250 additions and 435 deletions

View File

@ -7,16 +7,27 @@ package org.elasticsearch.example;
import org.elasticsearch.example.realm.CustomAuthenticationFailureHandler;
import org.elasticsearch.example.realm.CustomRealm;
import org.elasticsearch.example.realm.CustomRealmFactory;
import org.elasticsearch.xpack.security.authc.AuthenticationModule;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.authc.Realm;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
public class ExampleRealmExtension extends XPackExtension {
static {
// check that the extension's policy works.
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.getSecurityManager().checkPrintJobAccess();
return null;
});
}
@Override
public String name() {
return "custom realm example";
@ -28,13 +39,12 @@ public class ExampleRealmExtension extends XPackExtension {
}
public void onModule(AuthenticationModule authenticationModule) {
authenticationModule.addCustomRealm(CustomRealm.TYPE, CustomRealmFactory.class);
authenticationModule.setAuthenticationFailureHandler(CustomAuthenticationFailureHandler.class);
// check that the extension's policy works.
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.getSecurityManager().checkPrintJobAccess();
return null;
});
}
@Override
public Map<String, Realm.Factory> getRealms() {
return Collections.singletonMap(CustomRealm.TYPE, CustomRealm::new);
}
@Override

View File

@ -13,7 +13,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
public class CustomRealm extends Realm<UsernamePasswordToken> {
public class CustomRealm extends Realm {
public static final String TYPE = "custom";
@ -46,7 +46,8 @@ public class CustomRealm extends Realm<UsernamePasswordToken> {
}
@Override
public User authenticate(UsernamePasswordToken token) {
public User authenticate(AuthenticationToken authToken) {
UsernamePasswordToken token = (UsernamePasswordToken)authToken;
final String actualUser = token.principal();
if (KNOWN_USER.equals(actualUser) && SecuredString.constantTimeEquals(token.credentials(), KNOWN_PW)) {
return new User(actualUser, ROLES);

View File

@ -1,29 +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.example.realm;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmConfig;
public class CustomRealmFactory extends Realm.Factory<CustomRealm> {
@Inject
public CustomRealmFactory() {
super(CustomRealm.TYPE, false);
}
@Override
public CustomRealm create(RealmConfig config) {
return new CustomRealm(config);
}
@Override
public CustomRealm createDefault(String name) {
return null;
}
}

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -36,8 +37,10 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.action.SecurityActionModule;
import org.elasticsearch.xpack.security.action.filter.SecurityActionFilter;
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheAction;
@ -63,11 +66,20 @@ import org.elasticsearch.xpack.security.action.user.TransportPutUserAction;
import org.elasticsearch.xpack.security.audit.AuditTrailModule;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.audit.index.IndexNameResolver;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.AuthenticationModule;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.authz.AuthorizationModule;
@ -197,11 +209,11 @@ public class Security implements ActionPlugin {
}
List<Class<? extends LifecycleComponent>> list = new ArrayList<>();
list.add(FileRolesStore.class);
list.add(Realms.class);
return list;
}
public Collection<Object> createComponents(ResourceWatcherService resourceWatcherService) {
public Collection<Object> createComponents(InternalClient client, ThreadPool threadPool,
ResourceWatcherService resourceWatcherService, List<XPackExtension> extensions) {
if (enabled == false) {
return Collections.emptyList();
}
@ -210,7 +222,27 @@ public class Security implements ActionPlugin {
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, globalSslConfig, resourceWatcherService);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, globalSslConfig, resourceWatcherService);
return Arrays.asList(clientSSLService, serverSSLService);
// realms construction
final NativeUsersStore nativeUsersStore = new NativeUsersStore(settings, client, threadPool);
final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore);
Map<String, Realm.Factory> realmFactories = new HashMap<>();
realmFactories.put(FileRealm.TYPE, config -> new FileRealm(config, resourceWatcherService));
realmFactories.put(NativeRealm.TYPE, config -> new NativeRealm(config, nativeUsersStore));
realmFactories.put(ActiveDirectoryRealm.TYPE,
config -> new ActiveDirectoryRealm(config, resourceWatcherService, clientSSLService));
realmFactories.put(LdapRealm.TYPE, config -> new LdapRealm(config, resourceWatcherService, clientSSLService));
realmFactories.put(PkiRealm.TYPE, config -> new PkiRealm(config, resourceWatcherService));
for (XPackExtension extension : extensions) {
Map<String, Realm.Factory> newRealms = extension.getRealms();
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
if (realmFactories.put(entry.getKey(), entry.getValue()) != null) {
throw new IllegalArgumentException("Realm type [" + entry.getKey() + "] is already registered");
}
}
}
final Realms realms = new Realms(settings, env, realmFactories, securityLicenseState, reservedRealm);
return Arrays.asList(clientSSLService, serverSSLService, nativeUsersStore, realms);
}
public Settings additionalSettings() {

View File

@ -5,36 +5,16 @@
*/
package org.elasticsearch.xpack.security.authc;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.support.AbstractSecurityModule;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
*
*/
public class AuthenticationModule extends AbstractSecurityModule.Node {
static final List<String> INTERNAL_REALM_TYPES =
Arrays.asList(ReservedRealm.TYPE, NativeRealm.TYPE, FileRealm.TYPE, ActiveDirectoryRealm.TYPE, LdapRealm.TYPE, PkiRealm.TYPE);
private final Map<String, Class<? extends Realm.Factory<? extends Realm<? extends AuthenticationToken>>>> customRealms =
new HashMap<>();
private Class<? extends AuthenticationFailureHandler> authcFailureHandler = null;
public AuthenticationModule(Settings settings) {
@ -49,45 +29,14 @@ public class AuthenticationModule extends AbstractSecurityModule.Node {
}
AnonymousUser.initialize(settings);
MapBinder<String, Realm.Factory> mapBinder = MapBinder.newMapBinder(binder(), String.class, Realm.Factory.class);
mapBinder.addBinding(FileRealm.TYPE).to(FileRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(NativeRealm.TYPE).to(NativeRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(ActiveDirectoryRealm.TYPE).to(ActiveDirectoryRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(LdapRealm.TYPE).to(LdapRealm.Factory.class).asEagerSingleton();
mapBinder.addBinding(PkiRealm.TYPE).to(PkiRealm.Factory.class).asEagerSingleton();
for (Entry<String, Class<? extends Realm.Factory<? extends Realm<? extends AuthenticationToken>>>> entry :
customRealms.entrySet()) {
mapBinder.addBinding(entry.getKey()).to(entry.getValue()).asEagerSingleton();
}
bind(ReservedRealm.class).asEagerSingleton();
bind(Realms.class).asEagerSingleton();
if (authcFailureHandler == null) {
bind(AuthenticationFailureHandler.class).to(DefaultAuthenticationFailureHandler.class).asEagerSingleton();
} else {
bind(AuthenticationFailureHandler.class).to(authcFailureHandler).asEagerSingleton();
}
bind(NativeUsersStore.class).asEagerSingleton();
bind(AuthenticationService.class).to(InternalAuthenticationService.class).asEagerSingleton();
}
/**
* Registers a custom realm type and factory for use as a authentication realm
*
* @param type the type of the realm that identifies it. Must not be null, empty, or the same value as one of the built-in realms
* @param clazz the factory class that is used to create this type of realm. Must not be null
*/
public void addCustomRealm(String type, Class<? extends Realm.Factory<? extends Realm<? extends AuthenticationToken>>> clazz) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type must not be null or empty");
} else if (clazz == null) {
throw new IllegalArgumentException("realm factory class cannot be null");
} else if (INTERNAL_REALM_TYPES.contains(type)) {
throw new IllegalArgumentException("cannot redefine the type [" + type + "] with custom realm [" + clazz.getName() + "]");
}
customRealms.put(type, clazz);
}
/**
* Sets the {@link AuthenticationFailureHandler} to the specified implementation
*/

View File

@ -17,7 +17,7 @@ import java.util.Map;
* service } 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 abstract class Realm<T extends AuthenticationToken> implements Comparable<Realm> {
public abstract class Realm implements Comparable<Realm> {
protected final ESLogger logger;
protected final String type;
@ -67,7 +67,7 @@ public abstract class Realm<T extends AuthenticationToken> implements Comparable
* @param context The context that will provide information about the incoming request
* @return The authentication token or {@code null} if not found
*/
public abstract T token(ThreadContext context);
public abstract AuthenticationToken token(ThreadContext context);
/**
* Authenticates the given token. A successful authentication will return the User associated
@ -76,7 +76,7 @@ public abstract class Realm<T extends AuthenticationToken> implements Comparable
* @param token The authentication token
* @return The authenticated user or {@code null} if authentication failed.
*/
public abstract User authenticate(T token);
public abstract User authenticate(AuthenticationToken token);
/**
* Looks up the user identified the String identifier. A successful lookup will return the {@link User} identified
@ -107,44 +107,14 @@ public abstract class Realm<T extends AuthenticationToken> implements Comparable
}
/**
* A factory for a specific realm type. Knows how to create a new realm given the appropriate
* settings. The factory will be called when creating a realm during the parsing of realms defined in the
* elasticsearch.yml file
* A factory interface to construct a security realm.
*/
public abstract static 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;
}
public interface Factory {
/**
* @return The type of the ream this factory creates
* Constructs a realm which will be used for authentication.
* @param config The configuration for the realm
*/
public String type() {
return type;
}
public boolean internal() {
return internal;
}
/**
* Creates a new realm based on the given settigns.
*
* @param config The configuration for the realm
* @return The new realm (this method never returns {@code null}).
*/
public abstract R create(RealmConfig config);
/**
* 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);
Realm create(RealmConfig config);
}
}

View File

@ -13,10 +13,13 @@ import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import java.util.ArrayList;
import java.util.Arrays;
@ -34,6 +37,9 @@ import static org.elasticsearch.xpack.security.Security.setting;
*/
public class Realms extends AbstractLifecycleComponent implements Iterable<Realm> {
static final List<String> INTERNAL_REALM_TYPES =
Arrays.asList(ReservedRealm.TYPE, NativeRealm.TYPE, FileRealm.TYPE, ActiveDirectoryRealm.TYPE, LdapRealm.TYPE, PkiRealm.TYPE);
public static final Setting<Settings> REALMS_GROUPS_SETTINGS = Setting.groupSetting(setting("authc.realms."), Property.NodeScope);
private final Environment env;
@ -47,7 +53,6 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
// a list of realms that are considered native, that is they only interact with x-pack and no 3rd party auth sources
protected List<Realm> nativeRealmsOnly = Collections.emptyList();
@Inject
public Realms(Settings settings, Environment env, Map<String, Realm.Factory> factories, SecurityLicenseState securityLicenseState,
ReservedRealm reservedRealm) {
super(settings);
@ -67,7 +72,7 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
List<Realm> nativeRealms = new ArrayList<>();
for (Realm realm : realms) {
// don't add the reserved realm here otherwise we end up with only this realm...
if (AuthenticationModule.INTERNAL_REALM_TYPES.contains(realm.type()) && ReservedRealm.TYPE.equals(realm.type()) == false) {
if (INTERNAL_REALM_TYPES.contains(realm.type()) && ReservedRealm.TYPE.equals(realm.type()) == false) {
internalRealms.add(realm);
}
@ -151,7 +156,7 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
}
continue;
}
if (factory.internal()) {
if (FileRealm.TYPE.equals(type) || NativeRealm.TYPE.equals(type)) {
// 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)) {
@ -202,11 +207,12 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
private void addNativeRealms(List<Realm> realms) {
Realm.Factory fileRealm = factories.get(FileRealm.TYPE);
if (fileRealm != null) {
realms.add(fileRealm.createDefault("default_" + FileRealm.TYPE));
realms.add(fileRealm.create(new RealmConfig("default_" + FileRealm.TYPE, Settings.EMPTY, settings, env)));
}
Realm.Factory indexRealmFactory = factories.get(NativeRealm.TYPE);
if (indexRealmFactory != null) {
realms.add(indexRealmFactory.createDefault("default_" + NativeRealm.TYPE));
realms.add(indexRealmFactory.create(new RealmConfig("default_" + NativeRealm.TYPE, Settings.EMPTY, settings, env)));
}
}

View File

@ -5,13 +5,12 @@
*/
package org.elasticsearch.xpack.security.authc.activedirectory;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.AbstractLdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.watcher.ResourceWatcherService;
/**
*
@ -20,30 +19,13 @@ public class ActiveDirectoryRealm extends AbstractLdapRealm {
public static final String TYPE = "active_directory";
public ActiveDirectoryRealm(RealmConfig config,
ActiveDirectorySessionFactory connectionFactory,
DnRoleMapper roleMapper) {
super(TYPE, config, connectionFactory, roleMapper);
public ActiveDirectoryRealm(RealmConfig config, ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
this(config, new ActiveDirectorySessionFactory(config, clientSSLService),
new DnRoleMapper(TYPE, config, watcherService, null));
}
public static class Factory extends AbstractLdapRealm.Factory<ActiveDirectoryRealm> {
private final ResourceWatcherService watcherService;
private final ClientSSLService clientSSLService;
@Inject
public Factory(ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
super(ActiveDirectoryRealm.TYPE);
this.watcherService = watcherService;
this.clientSSLService = clientSSLService;
}
@Override
public ActiveDirectoryRealm create(RealmConfig config) {
ActiveDirectorySessionFactory connectionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
DnRoleMapper roleMapper = new DnRoleMapper(TYPE, config, watcherService, null);
return new ActiveDirectoryRealm(config, connectionFactory, roleMapper);
}
// pkg private for tests
ActiveDirectoryRealm(RealmConfig config, SessionFactory sessionFactory, DnRoleMapper roleMapper) {
super(TYPE, config, sessionFactory, roleMapper);
}
}

View File

@ -5,19 +5,12 @@
*/
package org.elasticsearch.xpack.security.authc.esnative;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authc.Realm;
import java.util.List;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import java.util.List;
import org.elasticsearch.xpack.security.user.User;
/**
* User/password realm that is backed by an Elasticsearch index
@ -58,30 +51,4 @@ public class NativeRealm extends CachingUsernamePasswordRealm {
}
}
}
public static class Factory extends Realm.Factory<NativeRealm> {
private final Settings settings;
private final Environment env;
private final NativeUsersStore userStore;
@Inject
public Factory(Settings settings, Environment env, NativeUsersStore userStore) {
super(TYPE, true);
this.settings = settings;
this.env = env;
this.userStore = userStore;
}
@Override
public NativeRealm create(RealmConfig config) {
return new NativeRealm(config, userStore);
}
@Override
public NativeRealm createDefault(String name) {
RealmConfig config = new RealmConfig(name, Settings.EMPTY, settings, env);
return create(config);
}
}
}

View File

@ -123,7 +123,6 @@ public class NativeUsersStore extends AbstractComponent implements ClusterStateL
private volatile boolean securityIndexExists = false;
@Inject
public NativeUsersStore(Settings settings, InternalClient client, ThreadPool threadPool) {
super(settings);
this.client = client;

View File

@ -35,7 +35,6 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
private final NativeUsersStore nativeUsersStore;
@Inject
public ReservedRealm(Environment env, Settings settings, NativeUsersStore nativeUsersStore) {
super(TYPE, new RealmConfig(TYPE, Settings.EMPTY, settings, env));
this.nativeUsersStore = nativeUsersStore;

View File

@ -5,19 +5,14 @@
*/
package org.elasticsearch.xpack.security.authc.file;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import java.util.Map;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.watcher.ResourceWatcherService;
import java.util.Map;
/**
*
@ -29,7 +24,12 @@ public class FileRealm extends CachingUsernamePasswordRealm {
final FileUserPasswdStore userPasswdStore;
final FileUserRolesStore userRolesStore;
public FileRealm(RealmConfig config, FileUserPasswdStore userPasswdStore, FileUserRolesStore userRolesStore) {
public FileRealm(RealmConfig config, ResourceWatcherService watcherService) {
this(config, new FileUserPasswdStore(config, watcherService), new FileUserRolesStore(config, watcherService));
}
// pkg private for testing
FileRealm(RealmConfig config, FileUserPasswdStore userPasswdStore, FileUserRolesStore userRolesStore) {
super(TYPE, config);
Listener listener = new Listener();
this.userPasswdStore = userPasswdStore;
@ -76,31 +76,4 @@ public class FileRealm extends CachingUsernamePasswordRealm {
expireAll();
}
}
public static class Factory extends UsernamePasswordRealm.Factory<FileRealm> {
private final Settings settings;
private final Environment env;
private final ResourceWatcherService watcherService;
@Inject
public Factory(Settings settings, Environment env, ResourceWatcherService watcherService) {
super(TYPE, true);
this.settings = settings;
this.env = env;
this.watcherService = watcherService;
}
@Override
public FileRealm create(RealmConfig config) {
return new FileRealm(config, new FileUserPasswdStore(config, watcherService),
new FileUserRolesStore(config, watcherService));
}
@Override
public FileRealm createDefault(String name) {
RealmConfig config = new RealmConfig(name, Settings.EMPTY, settings, env);
return create(config);
}
}
}

View File

@ -5,19 +5,18 @@
*/
package org.elasticsearch.xpack.security.authc.ldap;
import java.util.Map;
import com.unboundid.ldap.sdk.LDAPException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.AbstractLdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.watcher.ResourceWatcherService;
import java.util.Map;
/**
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
@ -26,55 +25,40 @@ public class LdapRealm extends AbstractLdapRealm {
public static final String TYPE = "ldap";
public LdapRealm(RealmConfig config, SessionFactory ldap, DnRoleMapper roleMapper) {
super(TYPE, config, ldap, roleMapper);
public LdapRealm(RealmConfig config, ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
this(config, sessionFactory(config, clientSSLService), new DnRoleMapper(TYPE, config, watcherService, null));
}
// pkg private for testing
LdapRealm(RealmConfig config, SessionFactory sessionFactory, DnRoleMapper roleMapper) {
super(TYPE, config, sessionFactory, roleMapper);
}
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) {
Settings searchSettings = userSearchSettings(config);
try {
if (!searchSettings.names().isEmpty()) {
if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
"authentication section of the X-Pack guide.");
}
return new LdapUserSearchSessionFactory(config, clientSSLService);
}
return new LdapSessionFactory(config, clientSSLService);
} catch (LDAPException e) {
throw new ElasticsearchException("failed to create realm [{}/{}]", e, LdapRealm.TYPE, config.name());
}
}
static Settings userSearchSettings(RealmConfig config) {
return config.settings().getAsSettings("user_search");
}
@Override
public Map<String, Object> usageStats() {
Map<String, Object> stats = super.usageStats();
stats.put("user_search", Factory.userSearchSettings(config).isEmpty() == false);
stats.put("user_search", userSearchSettings(config).isEmpty() == false);
return stats;
}
public static class Factory extends AbstractLdapRealm.Factory<LdapRealm> {
private final ResourceWatcherService watcherService;
private final ClientSSLService clientSSLService;
@Inject
public Factory(ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
super(TYPE);
this.watcherService = watcherService;
this.clientSSLService = clientSSLService;
}
@Override
public LdapRealm create(RealmConfig config) {
try {
SessionFactory sessionFactory = sessionFactory(config, clientSSLService);
DnRoleMapper roleMapper = new DnRoleMapper(TYPE, config, watcherService, null);
return new LdapRealm(config, sessionFactory, roleMapper);
} catch (LDAPException e) {
throw new ElasticsearchException("failed to create realm [{}/{}]", e, LdapRealm.TYPE, config.name());
}
}
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) throws LDAPException {
Settings searchSettings = userSearchSettings(config);
if (!searchSettings.names().isEmpty()) {
if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
"authentication section of the X-Pack guide.");
}
return new LdapUserSearchSessionFactory(config, clientSSLService);
}
return new LdapSessionFactory(config, clientSSLService);
}
static Settings userSearchSettings(RealmConfig config) {
return config.settings().getAsSettings("user_search");
}
}
}

View File

@ -5,20 +5,18 @@
*/
package org.elasticsearch.xpack.security.authc.ldap.support;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.unboundid.ldap.sdk.LDAPException;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Supporting class for LDAP realms
*/
@ -101,21 +99,4 @@ public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm {
expireAll();
}
}
public abstract static class Factory<R extends AbstractLdapRealm> extends UsernamePasswordRealm.Factory<R> {
public Factory(String type) {
super(type, false);
}
/**
* 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

@ -38,7 +38,7 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PkiRealm extends Realm<X509AuthenticationToken> {
public class PkiRealm extends Realm {
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
public static final String TYPE = "pki";
@ -51,7 +51,13 @@ public class PkiRealm extends Realm<X509AuthenticationToken> {
private final Pattern principalPattern;
private final DnRoleMapper roleMapper;
public PkiRealm(RealmConfig config, DnRoleMapper roleMapper) {
public PkiRealm(RealmConfig config, ResourceWatcherService watcherService) {
this(config, new DnRoleMapper(TYPE, config, watcherService, null));
}
// pkg private for testing
PkiRealm(RealmConfig config, DnRoleMapper roleMapper) {
super(TYPE, config);
this.trustManagers = trustManagers(config.settings(), config.env());
this.principalPattern = Pattern.compile(config.settings().get("username_pattern", DEFAULT_USERNAME_PATTERN),
@ -71,7 +77,8 @@ public class PkiRealm extends Realm<X509AuthenticationToken> {
}
@Override
public User authenticate(X509AuthenticationToken token) {
public User authenticate(AuthenticationToken authToken) {
X509AuthenticationToken token = (X509AuthenticationToken)authToken;
if (!isCertificateChainTrusted(trustManagers, token, logger)) {
return null;
}
@ -222,26 +229,4 @@ public class PkiRealm extends Realm<X509AuthenticationToken> {
logger.error("PKI realm [{}] is enabled but cannot be used as neither HTTP or Transport have both SSL and client authentication " +
"enabled", config.name());
}
public static class Factory extends Realm.Factory<PkiRealm> {
private final ResourceWatcherService watcherService;
@Inject
public Factory(ResourceWatcherService watcherService) {
super(TYPE, false);
this.watcherService = watcherService;
}
@Override
public PkiRealm create(RealmConfig config) {
DnRoleMapper roleMapper = new DnRoleMapper(TYPE, config, watcherService, null);
return new PkiRealm(config, roleMapper);
}
@Override
public PkiRealm createDefault(String name) {
return null;
}
}
}

View File

@ -10,6 +10,8 @@ import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.cache.CacheLoader;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.support.Exceptions;
import org.elasticsearch.xpack.security.user.User;
@ -63,11 +65,12 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
* against a hash also stored in the cache. Otherwise the subclass authenticates the user via
* doAuthenticate
*
* @param token The authentication token
* @param authToken The authentication token
* @return an authenticated user with roles
*/
@Override
public final User authenticate(final UsernamePasswordToken token) {
public final User authenticate(AuthenticationToken authToken) {
UsernamePasswordToken token = (UsernamePasswordToken)authToken;
if (cache == null) {
return doAuthenticate(token);
}

View File

@ -16,7 +16,7 @@ import java.util.Locale;
/**
*
*/
public abstract class UsernamePasswordRealm extends Realm<UsernamePasswordToken> {
public abstract class UsernamePasswordRealm extends Realm {
public UsernamePasswordRealm(String type, RealmConfig config) {
super(type, config);
@ -31,13 +31,6 @@ public abstract class UsernamePasswordRealm extends Realm<UsernamePasswordToken>
return token instanceof UsernamePasswordToken;
}
public abstract static class Factory<R extends UsernamePasswordRealm> extends Realm.Factory<R> {
protected Factory(String type, boolean internal) {
super(type, internal);
}
}
public enum UserbaseSize {
TINY,

View File

@ -0,0 +1,75 @@
/*
* 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.xpack.security;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import static org.mockito.Mockito.mock;
public class SecurityTests extends ESTestCase {
public static class DummyExtension extends XPackExtension {
private String realmType;
DummyExtension(String realmType) {
this.realmType = realmType;
}
@Override
public String name() {
return "dummy";
}
@Override
public String description() {
return "dummy";
}
@Override
public Map<String, Realm.Factory> getRealms() {
return Collections.singletonMap(realmType, config -> null);
}
}
private Collection<Object> createComponents(Settings testSettings, XPackExtension... extensions) throws IOException {
Settings settings = Settings.builder().put(testSettings)
.put("path.home", createTempDir()).build();
Environment env = new Environment(settings);
Security security = new Security(settings, env);
ThreadPool threadPool = mock(ThreadPool.class);
return security.createComponents(null, threadPool, null, Arrays.asList(extensions));
}
private <T> T findComponent(Class<T> type, Collection<Object> components) {
for (Object obj : components) {
if (type.isInstance(obj)) {
return type.cast(obj);
}
}
throw new AssertionError("Could not find component of type " + type + " in components");
}
public void testCustomRealmExtension() throws Exception {
Collection<Object> components = createComponents(Settings.EMPTY, new DummyExtension("myrealm"));
Realms realms = findComponent(Realms.class, components);
assertNotNull(realms.realmFactory("myrealm"));
}
public void testCustomRealmExtensionConflict() throws Exception {
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> createComponents(Settings.EMPTY, new DummyExtension(FileRealm.TYPE)));
assertEquals("Realm type [" + FileRealm.TYPE + "] is already registered", e.getMessage());
}
}

View File

@ -1,57 +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.xpack.security.authc;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.containsString;
/**
* Unit tests for the AuthenticationModule
*/
public class AuthenticationModuleTests extends ESTestCase {
public void testAddingReservedRealmType() {
Settings settings = Settings.EMPTY;
AuthenticationModule module = new AuthenticationModule(settings);
try {
module.addCustomRealm(randomFrom(PkiRealm.TYPE, LdapRealm.TYPE, ActiveDirectoryRealm.TYPE, FileRealm.TYPE),
randomFrom(PkiRealm.Factory.class, LdapRealm.Factory.class, ActiveDirectoryRealm.Factory.class,
FileRealm.Factory.class));
fail("overriding a built in realm type is not allowed!");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot redefine"));
}
}
public void testAddingNullOrEmptyType() {
Settings settings = Settings.EMPTY;
AuthenticationModule module = new AuthenticationModule(settings);
try {
module.addCustomRealm(randomBoolean() ? null : "",
randomFrom(PkiRealm.Factory.class, LdapRealm.Factory.class, ActiveDirectoryRealm.Factory.class,
FileRealm.Factory.class));
fail("type must not be null");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("null or empty"));
}
}
public void testAddingNullFactory() {
Settings settings = Settings.EMPTY;
AuthenticationModule module = new AuthenticationModule(settings);
try {
module.addCustomRealm(randomAsciiOfLength(7), null);
fail("factory must not be null");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("null"));
}
}
}

View File

@ -33,9 +33,6 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
*
*/
public class RealmsTests extends ESTestCase {
private Map<String, Realm.Factory> factories;
private SecurityLicenseState securityLicenseState;
@ -44,11 +41,11 @@ public class RealmsTests extends ESTestCase {
@Before
public void init() throws Exception {
factories = new HashMap<>();
factories.put(FileRealm.TYPE, new DummyRealm.Factory(FileRealm.TYPE, true));
factories.put(NativeRealm.TYPE, new DummyRealm.Factory(NativeRealm.TYPE, true));
factories.put(FileRealm.TYPE, config -> new DummyRealm(FileRealm.TYPE, config));
factories.put(NativeRealm.TYPE, config -> new DummyRealm(NativeRealm.TYPE, config));
for (int i = 0; i < randomIntBetween(1, 5); i++) {
DummyRealm.Factory factory = new DummyRealm.Factory("type_" + i, rarely());
factories.put("type_" + i, factory);
String name = "type_" + i;
factories.put(name, config -> new DummyRealm(name, config));
}
securityLicenseState = mock(SecurityLicenseState.class);
reservedRealm = mock(ReservedRealm.class);
@ -195,7 +192,7 @@ public class RealmsTests extends ESTestCase {
}
public void testUnlicensedWithInternalRealms() throws Exception {
factories.put(LdapRealm.TYPE, new DummyRealm.Factory(LdapRealm.TYPE, false));
factories.put(LdapRealm.TYPE, config -> new DummyRealm(LdapRealm.TYPE, config));
assertThat(factories.get("type_0"), notNullValue());
Settings.Builder builder = Settings.builder()
.put("path.home", createTempDir())
@ -252,7 +249,7 @@ public class RealmsTests extends ESTestCase {
}
public void testUnlicensedWithNativeRealms() throws Exception {
factories.put(LdapRealm.TYPE, new DummyRealm.Factory(LdapRealm.TYPE, false));
factories.put(LdapRealm.TYPE, config -> new DummyRealm(LdapRealm.TYPE, config));
final String type = randomFrom(FileRealm.TYPE, NativeRealm.TYPE);
Settings.Builder builder = Settings.builder()
.put("path.home", createTempDir())
@ -385,26 +382,5 @@ public class RealmsTests extends ESTestCase {
public boolean userLookupSupported() {
return false;
}
static class Factory extends Realm.Factory<DummyRealm> {
public Factory(String type, boolean internal) {
super(type, internal);
}
@Override
public DummyRealm create(RealmConfig config) {
return new DummyRealm(type(), config);
}
@Override
public DummyRealm createDefault(String name) {
if (type().equals(NativeRealm.TYPE) || type().equals(FileRealm.TYPE)) {
return new DummyRealm(type(), new RealmConfig(name, Settings.EMPTY,
Settings.builder().put("path.home", createTempDir()).build()));
}
return null;
}
}
}
}

View File

@ -164,7 +164,7 @@ public class LdapRealmTests extends LdapTestCase {
.put(HOSTNAME_VERIFICATION_SETTING, false)
.build();
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
SessionFactory sessionFactory = LdapRealm.Factory.sessionFactory(config, null);
SessionFactory sessionFactory = LdapRealm.sessionFactory(config, null);
assertThat(sessionFactory, is(instanceOf(LdapSessionFactory.class)));
}
@ -180,7 +180,7 @@ public class LdapRealmTests extends LdapTestCase {
.put(HOSTNAME_VERIFICATION_SETTING, false)
.build();
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings);
SessionFactory sessionFactory = LdapRealm.Factory.sessionFactory(config, null);
SessionFactory sessionFactory = LdapRealm.sessionFactory(config, null);
try {
assertThat(sessionFactory, is(instanceOf(LdapUserSearchSessionFactory.class)));
} finally {
@ -199,7 +199,7 @@ public class LdapRealmTests extends LdapTestCase {
.build();
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings);
try {
LdapRealm.Factory.sessionFactory(config, null);
LdapRealm.sessionFactory(config, null);
fail("an exception should have been thrown because both user template and user search settings were specified");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("settings were found for both user search and user template"));

View File

@ -142,7 +142,7 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
}
public void testAuthenticateContract() throws Exception {
Realm<UsernamePasswordToken> realm = new FailingAuthenticationRealm(Settings.EMPTY, globalSettings);
Realm realm = new FailingAuthenticationRealm(Settings.EMPTY, globalSettings);
User user = realm.authenticate(new UsernamePasswordToken("user", SecuredStringTests.build("pass")));
assertThat(user , nullValue());
@ -152,7 +152,7 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
}
public void testLookupContract() throws Exception {
Realm<UsernamePasswordToken> realm = new FailingAuthenticationRealm(Settings.EMPTY, globalSettings);
Realm realm = new FailingAuthenticationRealm(Settings.EMPTY, globalSettings);
User user = realm.lookupUser("user");
assertThat(user , nullValue());

View File

@ -191,8 +191,8 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin {
components.add(internalClient);
components.addAll(licensing.createComponents(clusterService, getClock(), env, resourceWatcherService,
security.getSecurityLicenseState()));
components.addAll(security.createComponents(resourceWatcherService));
security.getSecurityLicenseState()));
components.addAll(security.createComponents(internalClient, threadPool, resourceWatcherService, extensionsService.getExtensions()));
// watcher http stuff
Map<String, HttpAuthFactory> httpAuthFactories = new HashMap<>();

View File

@ -7,8 +7,12 @@ package org.elasticsearch.xpack.extensions;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.AuthenticationModule;
import org.elasticsearch.xpack.security.authc.Realm;
/**
@ -36,4 +40,16 @@ public abstract class XPackExtension {
public Collection<String> getRestHeaders() {
return Collections.emptyList();
}
/**
* Returns authentication realm implementations added by this extension.
*
* The key of the returned {@link Map} is the type name of the realm, and the value
* is a {@link org.elasticsearch.xpack.security.authc.Realm.Factory} which will construct
* that realm for use in authentication when that realm type is configured.
*/
public Map<String, Realm.Factory> getRealms() {
return Collections.emptyMap();
}
}