Remove guice from realms construction

This change makes the internal realms factories, as well as those added
by extensions, constructed directly instead of via guice. Adding realms
in extensions is now pull based. Finally, all of the generics for realms
and realm factories have been removed.

Original commit: elastic/x-pack-elasticsearch@f0de9d2340
This commit is contained in:
Ryan Ernst 2016-07-15 13:01:02 -07:00
parent c827a4be79
commit 9df9957307
24 changed files with 205 additions and 439 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).init(),
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).init();
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,15 @@
*/
package org.elasticsearch.xpack.security.authc.ldap;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.Inject;
import java.util.Map;
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.io.IOException;
import java.util.Map;
/**
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
@ -26,55 +22,36 @@ 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);
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).init();
}
return new LdapSessionFactory(config, clientSSLService).init();
}
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 (IOException e) {
throw new ElasticsearchException("failed to create realm [{}/{}]", e, LdapRealm.TYPE, config.name());
}
}
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) throws IOException {
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).init();
}
return new LdapSessionFactory(config, clientSSLService).init();
}
static Settings userSearchSettings(RealmConfig config) {
return config.settings().getAsSettings("user_search");
}
}
}

View File

@ -5,18 +5,16 @@
*/
package org.elasticsearch.xpack.security.authc.ldap.support;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.xpack.security.user.User;
import java.util.List;
import java.util.Map;
import java.util.Set;
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;
import org.elasticsearch.xpack.security.user.User;
/**
* Supporting class for LDAP realms
@ -100,21 +98,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,33 @@
/*
* 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 org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
public class SecurityTests extends ESTestCase {
private Environment env;
@Before
public void setupEnv() {
Settings settings = Settings.builder()
.put("path.home", createTempDir()).build();
env = new Environment(settings);
}
public void testCustomRealmExtension() throws Exception {
Security security = new Security(Settings.EMPTY, env);
//security.createComponents(null, null, null, )
}
public void testCustomRealmExtensionConflict() throws Exception {
}
}

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

@ -192,8 +192,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();
}
}