Added security realm feature usage stats
- if active, `file` realm size - if active, `native` realm size - if active, `ldap` realm size, whether SSL is used, load balance type used, user search used - if active, `active_directory` realm size, whether SSL is used, load balance type used `size` is scale estimation based on the local cache. Scales are: `small` (under 10 users), `medium` (under 50 users), `large` (under 250 users) and `x-large` (above 250 users). Original commit: elastic/x-pack-elasticsearch@c6efb17aa4
This commit is contained in:
parent
9dbbfd09f8
commit
084179f457
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.graph;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GraphFeatureSetTests extends ESTestCase {
|
||||
|
||||
private GraphLicensee licensee;
|
||||
private NamedWriteableRegistry namedWriteableRegistry;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
licensee = mock(GraphLicensee.class);
|
||||
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
|
||||
}
|
||||
|
||||
public void testWritableRegistration() throws Exception {
|
||||
new GraphFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
|
||||
verify(namedWriteableRegistry).register(eq(GraphFeatureSet.Usage.class), eq("xpack.usage.graph"), anyObject());
|
||||
}
|
||||
|
||||
public void testAvailable() throws Exception {
|
||||
GraphFeatureSet featureSet = new GraphFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
|
||||
boolean available = randomBoolean();
|
||||
when(licensee.isAvailable()).thenReturn(available);
|
||||
assertThat(featureSet.available(), is(available));
|
||||
}
|
||||
|
||||
public void testEnabled() throws Exception {
|
||||
boolean enabled = randomBoolean();
|
||||
Settings.Builder settings = Settings.builder();
|
||||
if (enabled) {
|
||||
if (randomBoolean()) {
|
||||
settings.put("xpack.graph.enabled", enabled);
|
||||
}
|
||||
} else {
|
||||
settings.put("xpack.graph.enabled", enabled);
|
||||
}
|
||||
GraphFeatureSet featureSet = new GraphFeatureSet(settings.build(), licensee, namedWriteableRegistry);
|
||||
assertThat(featureSet.enabled(), is(enabled));
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST;
|
|||
*/
|
||||
@ESIntegTestCase.ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0)
|
||||
public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.builder()
|
||||
|
|
|
@ -46,7 +46,7 @@ public class Monitoring {
|
|||
|
||||
public Monitoring(Settings settings) {
|
||||
this.settings = settings;
|
||||
this.enabled = MonitoringSettings.ENABLED.get(settings);
|
||||
this.enabled = enabled(settings);
|
||||
this.transportClientMode = XPackPlugin.transportClientMode(settings);
|
||||
this.tribeNode = XPackPlugin.isTribeNode(settings);
|
||||
}
|
||||
|
@ -62,9 +62,9 @@ public class Monitoring {
|
|||
public Collection<Module> nodeModules() {
|
||||
List<Module> modules = new ArrayList<>();
|
||||
modules.add(new MonitoringModule(enabled, transportClientMode));
|
||||
modules.add(new ExporterModule(settings));
|
||||
if (enabled && transportClientMode == false && tribeNode == false) {
|
||||
modules.add(new CollectorModule());
|
||||
modules.add(new ExporterModule(settings));
|
||||
modules.add(new MonitoringClientModule());
|
||||
}
|
||||
return modules;
|
||||
|
@ -100,4 +100,8 @@ public class Monitoring {
|
|||
module.registerLazyInitializable(MonitoringClientProxy.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean enabled(Settings settings) {
|
||||
return MonitoringSettings.ENABLED.get(settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.marvel.agent.exporter.Exporter;
|
||||
import org.elasticsearch.marvel.agent.exporter.Exporters;
|
||||
|
@ -27,10 +29,10 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
|
|||
|
||||
private final boolean enabled;
|
||||
private final MonitoringLicensee licensee;
|
||||
private final Exporters exporters;
|
||||
private final org.elasticsearch.marvel.agent.exporter.Exporters exporters;
|
||||
|
||||
@Inject
|
||||
public MonitoringFeatureSet(Settings settings, @Nullable MonitoringLicensee licensee, Exporters exporters,
|
||||
public MonitoringFeatureSet(Settings settings, @Nullable MonitoringLicensee licensee, @Nullable Exporters exporters,
|
||||
NamedWriteableRegistry namedWriteableRegistry) {
|
||||
this.enabled = MonitoringSettings.ENABLED.get(settings);
|
||||
this.licensee = licensee;
|
||||
|
@ -60,48 +62,48 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
|
|||
|
||||
@Override
|
||||
public Usage usage() {
|
||||
return new Usage(available(), enabled(), exportersUsage(exporters));
|
||||
}
|
||||
|
||||
int enabledLocalExporters = 0;
|
||||
int enabledHttpExporters = 0;
|
||||
int enabledUnknownExporters = 0;
|
||||
static Usage.Exporters exportersUsage(Exporters exporters) {
|
||||
if (exporters == null) {
|
||||
return null;
|
||||
}
|
||||
int local = 0;
|
||||
int http = 0;
|
||||
int unknown = 0;
|
||||
for (Exporter exporter : exporters) {
|
||||
if (exporter.config().enabled()) {
|
||||
switch (exporter.type()) {
|
||||
case LocalExporter.TYPE:
|
||||
enabledLocalExporters++;
|
||||
local++;
|
||||
break;
|
||||
case HttpExporter.TYPE:
|
||||
enabledHttpExporters++;
|
||||
http++;
|
||||
break;
|
||||
default:
|
||||
enabledUnknownExporters++;
|
||||
unknown++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Usage(available(), enabled(), enabledLocalExporters, enabledHttpExporters, enabledUnknownExporters);
|
||||
return new Usage.Exporters(local, http, unknown);
|
||||
}
|
||||
|
||||
static class Usage extends XPackFeatureSet.Usage {
|
||||
|
||||
private static String WRITEABLE_NAME = writeableName(Monitoring.NAME);
|
||||
|
||||
private final int enabledLocalExporters;
|
||||
private final int enabledHttpExporters;
|
||||
private final int enabledUnknownExporters;
|
||||
private @Nullable
|
||||
Exporters exporters;
|
||||
|
||||
public Usage(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
this.enabledLocalExporters = in.readVInt();
|
||||
this.enabledHttpExporters = in.readVInt();
|
||||
this.enabledUnknownExporters = in.readVInt();
|
||||
exporters = new Exporters(in);
|
||||
}
|
||||
|
||||
public Usage(boolean available, boolean enabled, int enabledLocalExporters, int enabledHttpExporters, int enabledUnknownExporters) {
|
||||
public Usage(boolean available, boolean enabled, Exporters exporters) {
|
||||
super(Monitoring.NAME, available, enabled);
|
||||
this.enabledLocalExporters = enabledLocalExporters;
|
||||
this.enabledHttpExporters = enabledHttpExporters;
|
||||
this.enabledUnknownExporters = enabledUnknownExporters;
|
||||
this.exporters = exporters;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,6 +124,38 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
|
|||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeOptionalWriteable(exporters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.AVAILABLE, available);
|
||||
builder.field(Field.ENABLED, enabled);
|
||||
if (exporters != null) {
|
||||
builder.field(Field.ENABLED_EXPORTERS, exporters);
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
static class Exporters implements Writeable, ToXContent {
|
||||
|
||||
private final int enabledLocalExporters;
|
||||
private final int enabledHttpExporters;
|
||||
private final int enabledUnknownExporters;
|
||||
|
||||
public Exporters(StreamInput in) throws IOException {
|
||||
this(in.readVInt(), in.readVInt(), in.readVInt());
|
||||
}
|
||||
|
||||
public Exporters(int enabledLocalExporters, int enabledHttpExporters, int enabledUnknownExporters) {
|
||||
this.enabledLocalExporters = enabledLocalExporters;
|
||||
this.enabledHttpExporters = enabledHttpExporters;
|
||||
this.enabledUnknownExporters = enabledUnknownExporters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeVInt(enabledLocalExporters);
|
||||
out.writeVInt(enabledHttpExporters);
|
||||
out.writeVInt(enabledUnknownExporters);
|
||||
|
@ -130,19 +164,14 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
|
|||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.AVAILABLE, available);
|
||||
builder.field(Field.ENABLED, enabled);
|
||||
|
||||
builder.startObject(Field.ENABLED_EXPORTERS);
|
||||
builder.field(Field.LOCAL, enabledLocalExporters);
|
||||
builder.field(Field.HTTP, enabledHttpExporters);
|
||||
if (enabledUnknownExporters > 0) {
|
||||
builder.field(Field.UNKNOWN, enabledUnknownExporters);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
return builder.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
interface Field extends XPackFeatureSet.Usage.Field {
|
||||
String ENABLED_EXPORTERS = "enabled_exporters";
|
||||
|
@ -151,4 +180,5 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
|
|||
String UNKNOWN = "_unknown";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,18 +7,20 @@ package org.elasticsearch.marvel.agent.exporter;
|
|||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.MapBinder;
|
||||
import org.elasticsearch.common.inject.util.Providers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.marvel.Monitoring;
|
||||
import org.elasticsearch.marvel.agent.exporter.http.HttpExporter;
|
||||
import org.elasticsearch.marvel.agent.exporter.local.LocalExporter;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExporterModule extends AbstractModule {
|
||||
|
||||
private final Map<String, Class<? extends Exporter.Factory<? extends Exporter>>> exporterFactories = new HashMap<>();
|
||||
|
||||
private final Settings settings;
|
||||
private final Map<String, Class<? extends Exporter.Factory<? extends Exporter>>> exporterFactories = new HashMap<>();
|
||||
|
||||
public ExporterModule(Settings settings) {
|
||||
this.settings = settings;
|
||||
|
@ -28,13 +30,17 @@ public class ExporterModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
if (Monitoring.enabled(settings) && XPackPlugin.transportClientMode(settings) == false
|
||||
&& XPackPlugin.isTribeNode(settings) == false) {
|
||||
bind(Exporters.class).asEagerSingleton();
|
||||
MapBinder<String, Exporter.Factory> factoryBinder = MapBinder.newMapBinder(binder(), String.class, Exporter.Factory.class);
|
||||
for (Map.Entry<String, Class<? extends Exporter.Factory<? extends Exporter>>> entry : exporterFactories.entrySet()) {
|
||||
bind(entry.getValue()).asEagerSingleton();
|
||||
factoryBinder.addBinding(entry.getKey()).to(entry.getValue());
|
||||
}
|
||||
|
||||
} else {
|
||||
bind(Exporters.class).toProvider(Providers.of(null));
|
||||
}
|
||||
}
|
||||
|
||||
public void registerExporter(String type, Class<? extends Exporter.Factory<? extends Exporter>> factory) {
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.marvel;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.marvel.agent.exporter.Exporters;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MonitoringFeatureSetTests extends ESTestCase {
|
||||
|
||||
private MonitoringLicensee licensee;
|
||||
private NamedWriteableRegistry namedWriteableRegistry;
|
||||
private Exporters exporters;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
licensee = mock(MonitoringLicensee.class);
|
||||
exporters = mock(Exporters.class);
|
||||
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
|
||||
}
|
||||
|
||||
public void testWritableRegistration() throws Exception {
|
||||
new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
|
||||
verify(namedWriteableRegistry).register(eq(MonitoringFeatureSet.Usage.class), eq("xpack.usage.monitoring"), anyObject());
|
||||
}
|
||||
|
||||
public void testAvailable() throws Exception {
|
||||
MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
|
||||
boolean available = randomBoolean();
|
||||
when(licensee.available()).thenReturn(available);
|
||||
assertThat(featureSet.available(), is(available));
|
||||
}
|
||||
|
||||
public void testEnabled() throws Exception {
|
||||
boolean enabled = randomBoolean();
|
||||
Settings.Builder settings = Settings.builder();
|
||||
if (enabled) {
|
||||
if (randomBoolean()) {
|
||||
settings.put("xpack.monitoring.enabled", enabled);
|
||||
}
|
||||
} else {
|
||||
settings.put("xpack.monitoring.enabled", enabled);
|
||||
}
|
||||
MonitoringFeatureSet featureSet = new MonitoringFeatureSet(settings.build(), licensee, exporters, namedWriteableRegistry);
|
||||
assertThat(featureSet.enabled(), is(enabled));
|
||||
}
|
||||
}
|
|
@ -73,8 +73,8 @@ import org.elasticsearch.shield.rest.action.user.RestChangePasswordAction;
|
|||
import org.elasticsearch.shield.rest.action.user.RestDeleteUserAction;
|
||||
import org.elasticsearch.shield.rest.action.user.RestGetUsersAction;
|
||||
import org.elasticsearch.shield.rest.action.user.RestPutUserAction;
|
||||
import org.elasticsearch.shield.ssl.SSLModule;
|
||||
import org.elasticsearch.shield.ssl.SSLConfiguration;
|
||||
import org.elasticsearch.shield.ssl.SSLModule;
|
||||
import org.elasticsearch.shield.support.OptionalSettings;
|
||||
import org.elasticsearch.shield.transport.ShieldClientTransportService;
|
||||
import org.elasticsearch.shield.transport.ShieldServerTransportService;
|
||||
|
@ -125,27 +125,28 @@ public class Security {
|
|||
public Collection<Module> nodeModules() {
|
||||
List<Module> modules = new ArrayList<>();
|
||||
|
||||
// we can't load that at construction time since the license plugin might not have been loaded at that point
|
||||
// which might not be the case during Plugin class instantiation. Once nodeModules are pulled
|
||||
// everything should have been loaded
|
||||
if (enabled && transportClientMode == false) {
|
||||
securityLicenseState = new SecurityLicenseState();
|
||||
}
|
||||
|
||||
modules.add(new SecurityModule(settings, securityLicenseState));
|
||||
|
||||
if (transportClientMode) {
|
||||
if (enabled == false) {
|
||||
return modules;
|
||||
}
|
||||
|
||||
if (transportClientMode == true) {
|
||||
modules.add(new SecurityModule(settings, securityLicenseState));
|
||||
modules.add(new ShieldTransportModule(settings));
|
||||
modules.add(new SSLModule(settings));
|
||||
return modules;
|
||||
}
|
||||
|
||||
modules.add(new CryptoModule(settings));
|
||||
modules.add(new AuthenticationModule(settings));
|
||||
if (enabled == false) {
|
||||
modules.add(new SecurityModule(settings, securityLicenseState));
|
||||
return modules;
|
||||
}
|
||||
|
||||
// we can't load that at construction time since the license plugin might not have been loaded at that point
|
||||
// which might not be the case during Plugin class instantiation. Once nodeModules are pulled
|
||||
// everything should have been loaded
|
||||
securityLicenseState = new SecurityLicenseState();
|
||||
modules.add(new SecurityModule(settings, securityLicenseState));
|
||||
modules.add(new CryptoModule(settings));
|
||||
modules.add(new AuthorizationModule(settings));
|
||||
modules.add(new AuditTrailModule(settings));
|
||||
modules.add(new ShieldRestModule(settings));
|
||||
|
|
|
@ -9,12 +9,21 @@ import org.elasticsearch.common.Nullable;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.marvel.Monitoring;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.Realms;
|
||||
import org.elasticsearch.shield.authc.esnative.ReservedRealm;
|
||||
import org.elasticsearch.xpack.XPackFeatureSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -23,12 +32,14 @@ public class SecurityFeatureSet implements XPackFeatureSet {
|
|||
|
||||
private final boolean enabled;
|
||||
private final SecurityLicenseState licenseState;
|
||||
private final @Nullable Realms realms;
|
||||
|
||||
@Inject
|
||||
public SecurityFeatureSet(Settings settings, @Nullable SecurityLicenseState licenseState,
|
||||
NamedWriteableRegistry namedWriteableRegistry) {
|
||||
@Nullable Realms realms, NamedWriteableRegistry namedWriteableRegistry) {
|
||||
this.enabled = Security.enabled(settings);
|
||||
this.licenseState = licenseState;
|
||||
this.realms = realms;
|
||||
namedWriteableRegistry.register(Usage.class, Usage.WRITEABLE_NAME, Usage::new);
|
||||
}
|
||||
|
||||
|
@ -53,20 +64,39 @@ public class SecurityFeatureSet implements XPackFeatureSet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XPackFeatureSet.Usage usage() {
|
||||
return new Usage(available(), enabled());
|
||||
public Usage usage() {
|
||||
List<Map<String, Object>> enabledRealms = buildEnabledRealms(realms);
|
||||
return new Usage(available(), enabled(), enabledRealms);
|
||||
}
|
||||
|
||||
static List<Map<String, Object>> buildEnabledRealms(Realms realms) {
|
||||
if (realms == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Map<String, Object>> enabledRealms = new ArrayList<>();
|
||||
for (Realm realm : realms) {
|
||||
if (realm instanceof ReservedRealm) {
|
||||
continue; // we don't need usage of this one
|
||||
}
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
enabledRealms.add(stats);
|
||||
}
|
||||
return enabledRealms;
|
||||
}
|
||||
|
||||
static class Usage extends XPackFeatureSet.Usage {
|
||||
|
||||
private static final String WRITEABLE_NAME = writeableName(Security.NAME);
|
||||
private List<Map<String, Object>> enabledRealms;
|
||||
|
||||
public Usage(StreamInput input) throws IOException {
|
||||
super(input);
|
||||
public Usage(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
enabledRealms = in.readList(StreamInput::readMap);
|
||||
}
|
||||
|
||||
public Usage(boolean available, boolean enabled) {
|
||||
public Usage(boolean available, boolean enabled, List<Map<String, Object>> enabledRealms) {
|
||||
super(Security.NAME, available, enabled);
|
||||
this.enabledRealms = enabledRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,12 +105,24 @@ public class SecurityFeatureSet implements XPackFeatureSet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject()
|
||||
.field(Field.AVAILABLE, available)
|
||||
.field(Field.ENABLED, enabled)
|
||||
.endObject();
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeList(enabledRealms.stream().map((m) -> (Writeable) o -> o.writeMap(m)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.AVAILABLE, available);
|
||||
builder.field(Field.ENABLED, enabled);
|
||||
if (enabled) {
|
||||
builder.field(Field.ENABLED_REALMS, enabledRealms);
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
interface Field extends XPackFeatureSet.Usage.Field {
|
||||
String ENABLED_REALMS = "enabled_realms";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.shield.authc;
|
||||
|
||||
import org.elasticsearch.common.inject.multibindings.MapBinder;
|
||||
import org.elasticsearch.common.inject.util.Providers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.authc.activedirectory.ActiveDirectoryRealm;
|
||||
import org.elasticsearch.shield.authc.esnative.NativeRealm;
|
||||
|
@ -42,6 +43,11 @@ public class AuthenticationModule extends AbstractShieldModule.Node {
|
|||
|
||||
@Override
|
||||
protected void configureNode() {
|
||||
if (!shieldEnabled) {
|
||||
bind(Realms.class).toProvider(Providers.of(null));
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -9,6 +9,9 @@ import org.elasticsearch.common.logging.ESLogger;
|
|||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An authentication mechanism to which the default authentication {@link org.elasticsearch.shield.authc.AuthenticationService service}
|
||||
* delegates the authentication process. Different realms may be defined, each may be based on different
|
||||
|
@ -84,6 +87,14 @@ public abstract class Realm<T extends AuthenticationToken> implements Comparable
|
|||
*/
|
||||
public abstract User lookupUser(String username);
|
||||
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("type", type);
|
||||
stats.put("name", name());
|
||||
stats.put("order", order());
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this realm supports user lookup.
|
||||
* @return true if the realm supports user lookup
|
||||
|
|
|
@ -9,14 +9,16 @@ 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.user.User;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
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.shield.user.User;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -55,6 +57,14 @@ public class FileRealm extends CachingUsernamePasswordRealm {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> stats = super.usageStats();
|
||||
// here we can determine the size based on the in mem user store
|
||||
stats.put("size", UserbaseScale.resolve(userPasswdStore.usersCount()));
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean userLookupSupported() {
|
||||
return true;
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.shield.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.
|
||||
|
@ -29,6 +30,13 @@ public class LdapRealm extends AbstractLdapRealm {
|
|||
super(TYPE, config, ldap, roleMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> stats = super.usageStats();
|
||||
stats.put("user_search", Factory.userSearchSettings(config).isEmpty() == false);
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static class Factory extends AbstractLdapRealm.Factory<LdapRealm> {
|
||||
|
||||
private final ResourceWatcherService watcherService;
|
||||
|
@ -53,7 +61,7 @@ public class LdapRealm extends AbstractLdapRealm {
|
|||
}
|
||||
|
||||
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) throws IOException {
|
||||
Settings searchSettings = config.settings().getAsSettings("user_search");
|
||||
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. " +
|
||||
|
@ -64,5 +72,9 @@ public class LdapRealm extends AbstractLdapRealm {
|
|||
}
|
||||
return new LdapSessionFactory(config, clientSSLService).init();
|
||||
}
|
||||
|
||||
static Settings userSearchSettings(RealmConfig config) {
|
||||
return config.settings().getAsSettings("user_search");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.shield.authc.support.UsernamePasswordRealm;
|
|||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -65,6 +66,14 @@ public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm {
|
|||
return sessionFactory.supportsUnauthenticatedSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> usage = super.usageStats();
|
||||
usage.put("load_balance_type", LdapLoadBalancing.resolve(config.settings()).toString());
|
||||
usage.put("ssl", sessionFactory.sslUsed);
|
||||
return usage;
|
||||
}
|
||||
|
||||
private void logException(String action, Exception e, String principal) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} failed for user [{}]", e, action, principal);
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
@ -83,25 +82,23 @@ public enum LdapLoadBalancing {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static LdapLoadBalancing resolve(Settings settings) {
|
||||
Settings loadBalanceSettings = settings.getAsSettings(LOAD_BALANCE_SETTINGS);
|
||||
String type = loadBalanceSettings.get(LOAD_BALANCE_TYPE_SETTING, LOAD_BALANCE_TYPE_DEFAULT);
|
||||
try {
|
||||
return valueOf(type.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException ilae) {
|
||||
throw new IllegalArgumentException("unknown load balance type [" + type + "]", ilae);
|
||||
}
|
||||
}
|
||||
|
||||
public static ServerSet serverSet(String[] addresses, int[] ports, Settings settings, @Nullable SocketFactory socketFactory,
|
||||
@Nullable LDAPConnectionOptions options) {
|
||||
LdapLoadBalancing loadBalancing = resolve(settings);
|
||||
Settings loadBalanceSettings = settings.getAsSettings(LOAD_BALANCE_SETTINGS);
|
||||
String type = loadBalanceSettings.get(LOAD_BALANCE_TYPE_SETTING, LOAD_BALANCE_TYPE_DEFAULT);
|
||||
switch (type.toLowerCase(Locale.ENGLISH)) {
|
||||
case "failover":
|
||||
return FAILOVER.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
||||
case "dns_failover":
|
||||
return DNS_FAILOVER.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
||||
case "round_robin":
|
||||
return ROUND_ROBIN.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
||||
case "dns_round_robin":
|
||||
return DNS_ROUND_ROBIN.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown server set type [" + type + "]. value must be one of " +
|
||||
Arrays.toString(LdapLoadBalancing.values()));
|
||||
}
|
||||
return loadBalancing.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,9 @@ public abstract class SessionFactory {
|
|||
protected final RealmConfig config;
|
||||
protected final TimeValue timeout;
|
||||
protected final ClientSSLService sslService;
|
||||
|
||||
protected ServerSet serverSet;
|
||||
protected boolean sslUsed;
|
||||
|
||||
protected SessionFactory(RealmConfig config, ClientSSLService sslService) {
|
||||
this.config = config;
|
||||
|
@ -118,7 +120,9 @@ public abstract class SessionFactory {
|
|||
}
|
||||
|
||||
public <T extends SessionFactory> T init() {
|
||||
this.serverSet = serverSet(config.settings(), sslService, ldapServers(config.settings()));
|
||||
LDAPServers ldapServers = ldapServers(config.settings());
|
||||
this.serverSet = serverSet(config.settings(), sslService, ldapServers);
|
||||
this.sslUsed = ldapServers.ssl;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.shield.authc.RealmConfig;
|
|||
import org.elasticsearch.shield.support.Exceptions;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -164,6 +165,13 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> stats = super.usageStats();
|
||||
stats.put("size", UserbaseScale.resolve(cache.count()).toString());
|
||||
return stats;
|
||||
}
|
||||
|
||||
protected abstract User doAuthenticate(UsernamePasswordToken token);
|
||||
|
||||
protected abstract User doLookupUser(String username);
|
||||
|
|
|
@ -11,6 +11,8 @@ import org.elasticsearch.shield.authc.AuthenticationToken;
|
|||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -36,4 +38,30 @@ public abstract class UsernamePasswordRealm extends Realm<UsernamePasswordToken>
|
|||
restController.registerRelevantHeaders(UsernamePasswordToken.BASIC_AUTH_HEADER);
|
||||
}
|
||||
}
|
||||
|
||||
public enum UserbaseScale {
|
||||
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
LARGE,
|
||||
XLARGE;
|
||||
|
||||
public static UserbaseScale resolve(int count) {
|
||||
if (count < 10) {
|
||||
return SMALL;
|
||||
}
|
||||
if (count < 50) {
|
||||
return MEDIUM;
|
||||
}
|
||||
if (count < 250) {
|
||||
return LARGE;
|
||||
}
|
||||
return XLARGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this == XLARGE ? "x-large" : name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.Realms;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SecurityFeatureSetTests extends ESTestCase {
|
||||
|
||||
private SecurityLicenseState licenseState;
|
||||
private Realms realms;
|
||||
private NamedWriteableRegistry namedWriteableRegistry;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
licenseState = mock(SecurityLicenseState.class);
|
||||
realms = mock(Realms.class);
|
||||
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
|
||||
}
|
||||
|
||||
public void testWritableRegistration() throws Exception {
|
||||
new SecurityFeatureSet(Settings.EMPTY, licenseState, realms, namedWriteableRegistry);
|
||||
verify(namedWriteableRegistry).register(eq(SecurityFeatureSet.Usage.class), eq("xpack.usage.security"), anyObject());
|
||||
}
|
||||
|
||||
public void testAvailable() throws Exception {
|
||||
SecurityFeatureSet featureSet = new SecurityFeatureSet(Settings.EMPTY, licenseState, realms, namedWriteableRegistry);
|
||||
boolean available = randomBoolean();
|
||||
when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(available);
|
||||
assertThat(featureSet.available(), is(available));
|
||||
}
|
||||
|
||||
public void testEnabled() throws Exception {
|
||||
boolean enabled = randomBoolean();
|
||||
Settings.Builder settings = Settings.builder();
|
||||
if (enabled) {
|
||||
if (randomBoolean()) {
|
||||
settings.put("xpack.security.enabled", enabled);
|
||||
}
|
||||
} else {
|
||||
settings.put("xpack.security.enabled", enabled);
|
||||
}
|
||||
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, namedWriteableRegistry);
|
||||
assertThat(featureSet.enabled(), is(enabled));
|
||||
}
|
||||
|
||||
public void testUsage() throws Exception {
|
||||
|
||||
boolean available = randomBoolean();
|
||||
when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(available);
|
||||
|
||||
Settings.Builder settings = Settings.builder();
|
||||
|
||||
boolean enabled = randomBoolean();
|
||||
if (enabled) {
|
||||
if (randomBoolean()) {
|
||||
settings.put("xpack.security.enabled", enabled);
|
||||
}
|
||||
} else {
|
||||
settings.put("xpack.security.enabled", enabled);
|
||||
}
|
||||
|
||||
List<Realm> realmsList= new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Realm realm = mock(Realm.class);
|
||||
when(realm.type()).thenReturn("type" + i);
|
||||
realmsList.add(realm);
|
||||
Map<String, Object> realmUsage = new HashMap<>();
|
||||
realmUsage.put("key1", "value" + i);
|
||||
realmUsage.put("key2", i);
|
||||
realmUsage.put("key3", i % 2 == 0);
|
||||
when(realm.usageStats()).thenReturn(realmUsage);
|
||||
}
|
||||
when(realms.iterator()).thenReturn(realmsList.iterator());
|
||||
|
||||
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, namedWriteableRegistry);
|
||||
SecurityFeatureSet.Usage usage = featureSet.usage();
|
||||
assertThat(usage, is(notNullValue()));
|
||||
assertThat(usage.name(), is(Security.NAME));
|
||||
assertThat(usage.enabled(), is(enabled));
|
||||
assertThat(usage.available(), is(available));
|
||||
XContentSource source = new XContentSource(usage);
|
||||
|
||||
if (enabled) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertThat(source.getValue("enabled_realms." + i + ".key1"), is("value" + i));
|
||||
assertThat(source.getValue("enabled_realms." + i + ".key2"), is(i));
|
||||
assertThat(source.getValue("enabled_realms." + i + ".key3"), is(i % 2 == 0));
|
||||
}
|
||||
} else {
|
||||
assertThat(source.getValue("enabled_realms"), is(nullValue()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.activedirectory;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Network
|
||||
public class AbstractActiveDirectoryIntegTests extends ESTestCase {
|
||||
|
||||
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";
|
||||
|
||||
protected ClientSSLService clientSSLService;
|
||||
protected Settings globalSettings;
|
||||
protected boolean useGlobalSSL;
|
||||
|
||||
@Before
|
||||
public void initializeSslSocketFactory() throws Exception {
|
||||
useGlobalSSL = randomBoolean();
|
||||
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
|
||||
/*
|
||||
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
|
||||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
|
||||
if (useGlobalSSL) {
|
||||
builder.put("xpack.security.ssl.keystore.path", keystore)
|
||||
.put("xpack.security.ssl.keystore.password", "changeit");
|
||||
} else {
|
||||
builder.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false);
|
||||
}
|
||||
globalSettings = builder.build();
|
||||
Environment environment = new Environment(globalSettings);
|
||||
clientSSLService = new ClientSSLService(globalSettings, new Global(globalSettings));
|
||||
clientSSLService.setEnvironment(environment);
|
||||
}
|
||||
|
||||
Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope,
|
||||
boolean hostnameVerification) {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.putArray(ActiveDirectorySessionFactory.URLS_SETTING, ldapUrl)
|
||||
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
|
||||
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
|
||||
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_SCOPE_SETTING, scope)
|
||||
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification);
|
||||
if (useGlobalSSL == false) {
|
||||
builder.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
|
||||
.put("ssl.truststore.password", "changeit");
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@ import static org.mockito.Mockito.verify;
|
|||
* additional bind DN with a password in the test setup since it really is not a DN in the ldif file
|
||||
*/
|
||||
public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
protected static int numberOfLdapServers;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.activedirectory;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.shield.authc.support.DnRoleMapper;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Network
|
||||
public class ActiveDirectoryRealmUsageTests extends AbstractActiveDirectoryIntegTests {
|
||||
|
||||
public void testUsageStats() throws Exception {
|
||||
String loadBalanceType = randomFrom("failover", "round_robin");
|
||||
Settings settings = Settings.builder()
|
||||
.put(buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Bruce Banner, CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
|
||||
LdapSearchScope.BASE, false))
|
||||
.put("load_balance.type", loadBalanceType)
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, mock(DnRoleMapper.class));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
assertThat(stats, is(notNullValue()));
|
||||
assertThat(stats, hasEntry("type", "active_directory"));
|
||||
assertThat(stats, hasEntry("name", "ad-test"));
|
||||
assertThat(stats, hasEntry("order", realm.order()));
|
||||
assertThat(stats, hasEntry("size", "small"));
|
||||
assertThat(stats, hasEntry("ssl", true));
|
||||
assertThat(stats, hasEntry("load_balance_type", loadBalanceType));
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ package org.elasticsearch.shield.authc.activedirectory;
|
|||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.authc.ldap.LdapSessionFactory;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||
|
@ -15,14 +14,9 @@ import org.elasticsearch.shield.authc.ldap.support.LdapSession;
|
|||
import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
|
||||
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.test.ShieldTestsUtils.assertAuthenticationException;
|
||||
|
@ -32,36 +26,7 @@ import static org.hamcrest.Matchers.hasItem;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@Network
|
||||
public class ActiveDirectorySessionFactoryTests extends ESTestCase {
|
||||
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";
|
||||
|
||||
private ClientSSLService clientSSLService;
|
||||
private Settings globalSettings;
|
||||
private boolean useGlobalSSL;
|
||||
|
||||
@Before
|
||||
public void initializeSslSocketFactory() throws Exception {
|
||||
useGlobalSSL = randomBoolean();
|
||||
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
|
||||
/*
|
||||
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
|
||||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
|
||||
if (useGlobalSSL) {
|
||||
builder.put("xpack.security.ssl.keystore.path", keystore)
|
||||
.put("xpack.security.ssl.keystore.password", "changeit");
|
||||
} else {
|
||||
builder.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false);
|
||||
}
|
||||
globalSettings = builder.build();
|
||||
Environment environment = new Environment(globalSettings);
|
||||
clientSSLService = new ClientSSLService(globalSettings, new Global(globalSettings));
|
||||
clientSSLService.setEnvironment(environment);
|
||||
}
|
||||
public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryIntegTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAdAuth() throws Exception {
|
||||
|
@ -333,19 +298,4 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
|
|||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope,
|
||||
boolean hostnameVerification) {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.putArray(ActiveDirectorySessionFactory.URLS_SETTING, ldapUrl)
|
||||
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
|
||||
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
|
||||
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_SCOPE_SETTING, scope)
|
||||
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification);
|
||||
if (useGlobalSSL == false) {
|
||||
builder.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
|
||||
.put("ssl.truststore.password", "changeit");
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,23 +7,25 @@ package org.elasticsearch.shield.authc.file;
|
|||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.authc.support.Hasher;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordRealm;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -161,6 +163,27 @@ public class FileRealmTests extends ESTestCase {
|
|||
assertThat(user5, sameInstance(user6));
|
||||
}
|
||||
|
||||
public void testUsageStats() throws Exception {
|
||||
int userCount = randomIntBetween(0, 1000);
|
||||
when(userPasswdStore.usersCount()).thenReturn(userCount);
|
||||
|
||||
Settings.Builder settings = Settings.builder();
|
||||
|
||||
int order = randomIntBetween(0, 10);
|
||||
settings.put("order", order);
|
||||
|
||||
RealmConfig config = new RealmConfig("file-realm", settings.build(), globalSettings);
|
||||
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
||||
|
||||
Map<String, Object> usage = realm.usageStats();
|
||||
assertThat(usage, is(notNullValue()));
|
||||
assertThat(usage, hasEntry("type", "file"));
|
||||
assertThat(usage, hasEntry("name", "file-realm"));
|
||||
assertThat(usage, hasEntry("order", order));
|
||||
assertThat(usage, hasEntry("size", UsernamePasswordRealm.UserbaseScale.resolve(userCount)));
|
||||
|
||||
}
|
||||
|
||||
static class UserPasswdStore extends FileUserPasswdStore {
|
||||
public UserPasswdStore(RealmConfig config) {
|
||||
super(config, mock(ResourceWatcherService.class));
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.shield.authc.ldap;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
|
||||
|
@ -15,16 +14,20 @@ import org.elasticsearch.shield.authc.support.DnRoleMapper;
|
|||
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.shield.authc.ldap.LdapSessionFactory.USER_DN_TEMPLATES_SETTING;
|
||||
import static org.elasticsearch.shield.authc.ldap.support.SessionFactory.HOSTNAME_VERIFICATION_SETTING;
|
||||
import static org.elasticsearch.shield.authc.ldap.support.SessionFactory.URLS_SETTING;
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -35,6 +38,7 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class LdapRealmTests extends LdapTestCase {
|
||||
|
||||
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";
|
||||
|
@ -217,4 +221,37 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
assertThat(user, notNullValue());
|
||||
assertThat(user.roles(), arrayContaining("avenger"));
|
||||
}
|
||||
|
||||
public void testUsageStats() throws Exception {
|
||||
String groupSearchBase = "o=sevenSeas";
|
||||
Settings.Builder settings = Settings.builder()
|
||||
.putArray(URLS_SETTING, ldapUrls())
|
||||
.put("bind_dn", "cn=Thomas Masterman Hardy,ou=people,o=sevenSeas")
|
||||
.put("bind_password", PASSWORD)
|
||||
.put("group_search.base_dn", groupSearchBase)
|
||||
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||
.put(HOSTNAME_VERIFICATION_SETTING, false);
|
||||
|
||||
int order = randomIntBetween(0, 10);
|
||||
settings.put("order", order);
|
||||
|
||||
boolean userSearch = randomBoolean();
|
||||
if (userSearch) {
|
||||
settings.put("user_search.base_dn", "");
|
||||
}
|
||||
|
||||
RealmConfig config = new RealmConfig("ldap-realm", settings.build(), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapRealm realm = new LdapRealm(config, ldapFactory, new DnRoleMapper(LdapRealm.TYPE, config, resourceWatcherService, null));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
assertThat(stats, is(notNullValue()));
|
||||
assertThat(stats, hasEntry("type", "ldap"));
|
||||
assertThat(stats, hasEntry("name", "ldap-realm"));
|
||||
assertThat(stats, hasEntry("order", realm.order()));
|
||||
assertThat(stats, hasEntry("size", "small"));
|
||||
assertThat(stats, hasEntry("ssl", false));
|
||||
assertThat(stats, hasEntry("user_search", userSearch));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ import static org.hamcrest.Matchers.nullValue;
|
|||
LdapUserSearchSessionFactoryTests.BackgroundConnectThreadLeakFilter.class
|
||||
})
|
||||
public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||
|
||||
private ClientSSLService clientSSLService;
|
||||
private Settings globalSettings;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
|
|||
import org.elasticsearch.shield.authc.ldap.support.LdapSession;
|
||||
import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
|
||||
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
|
||||
import org.elasticsearch.shield.authc.support.DnRoleMapper;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
|
||||
|
@ -22,9 +23,14 @@ import org.junit.Before;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Network
|
||||
public class OpenLdapTests extends ESTestCase {
|
||||
|
@ -91,6 +97,36 @@ public class OpenLdapTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testUsageStats() throws Exception {
|
||||
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";
|
||||
Settings.Builder settings = Settings.builder()
|
||||
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
|
||||
.put("group_search.filter", "(&(objectclass=posixGroup)(memberUID={0}))")
|
||||
.put("group_search.user_attribute", "uid");
|
||||
|
||||
boolean userSearch = randomBoolean();
|
||||
if (userSearch) {
|
||||
settings.put("user_search.base_dn", "");
|
||||
}
|
||||
|
||||
String loadBalanceType = randomFrom("failover", "round_robin");
|
||||
settings.put("load_balance.type", loadBalanceType);
|
||||
|
||||
RealmConfig config = new RealmConfig("oldap-test", settings.build(), globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapRealm realm = new LdapRealm(config, sessionFactory, mock(DnRoleMapper.class));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
assertThat(stats, is(notNullValue()));
|
||||
assertThat(stats, hasEntry("size", "small"));
|
||||
assertThat(stats, hasEntry("ssl", true));
|
||||
assertThat(stats, hasEntry("user_search", userSearch));
|
||||
assertThat(stats, hasEntry("load_balance_type", loadBalanceType));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void testCustomFilter() throws Exception {
|
||||
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";
|
||||
|
|
|
@ -12,6 +12,7 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class LDAPServersTests extends ESTestCase {
|
||||
|
||||
public void testConfigure1ldaps() {
|
||||
String[] urls = new String[] { "ldaps://example.com:636" };
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public class LdapLoadBalancingTests extends ESTestCase {
|
|||
LdapLoadBalancing.serverSet(null, null, settings, null, null);
|
||||
fail("using type [" + badType + "] should have thrown an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), containsString("unknown server set type"));
|
||||
assertThat(e.getMessage(), containsString("unknown load balance type"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ package org.elasticsearch.shield.authc.support;
|
|||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.user.User;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
|
@ -25,6 +25,7 @@ import static org.hamcrest.Matchers.nullValue;
|
|||
import static org.hamcrest.Matchers.sameInstance;
|
||||
|
||||
public class CachingUsernamePasswordRealmTests extends ESTestCase {
|
||||
|
||||
private Settings globalSettings;
|
||||
|
||||
@Before
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.test.ESTestCase;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class UsernamePasswordRealmTests extends ESTestCase {
|
||||
|
||||
public void testUserbaseScaelResolve() throws Exception {
|
||||
int count = randomIntBetween(0, 1000);
|
||||
UsernamePasswordRealm.UserbaseScale scale = UsernamePasswordRealm.UserbaseScale.resolve(count);
|
||||
if (count < 10) {
|
||||
assertThat(scale, is(UsernamePasswordRealm.UserbaseScale.SMALL));
|
||||
} else if (count < 50) {
|
||||
assertThat(scale, is(UsernamePasswordRealm.UserbaseScale.MEDIUM));
|
||||
} else if (count < 250) {
|
||||
assertThat(scale, is(UsernamePasswordRealm.UserbaseScale.LARGE));
|
||||
} else {
|
||||
assertThat(scale, is(UsernamePasswordRealm.UserbaseScale.XLARGE));
|
||||
}
|
||||
}
|
||||
|
||||
public void testUserbaseScaleToString() throws Exception {
|
||||
UsernamePasswordRealm.UserbaseScale scale = randomFrom(UsernamePasswordRealm.UserbaseScale.values());
|
||||
String value = scale.toString();
|
||||
if (scale == UsernamePasswordRealm.UserbaseScale.XLARGE) {
|
||||
assertThat(value , is("x-large"));
|
||||
} else {
|
||||
assertThat(value , is(scale.name().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,6 +75,7 @@ cluster:admin/script/delete
|
|||
cluster:admin/script/put
|
||||
indices:data/write/update
|
||||
cluster:monitor/xpack/info
|
||||
cluster:monitor/xpack/usage
|
||||
cluster:monitor/xpack/license/get
|
||||
cluster:admin/xpack/license/delete
|
||||
cluster:admin/xpack/license/put
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"xpack.info": {
|
||||
"documentation": "Retrieve information about xpack features usage",
|
||||
"methods": [ "GET" ],
|
||||
"url": {
|
||||
"path": "/_xpack/usage",
|
||||
"paths": [ "/_xpack/usage" ],
|
||||
"parts": {},
|
||||
"params": {
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ import java.io.IOException;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
* Encapsulates the xcontent source
|
||||
*/
|
||||
|
@ -48,6 +50,10 @@ public class XContentSource implements ToXContent {
|
|||
this(builder.bytes(), builder.contentType());
|
||||
}
|
||||
|
||||
public XContentSource(ToXContent content) throws IOException {
|
||||
this(content.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The bytes reference of the source
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.watcher;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class WatcherFeatureSetTests extends ESTestCase {
|
||||
|
||||
private WatcherLicensee licensee;
|
||||
private NamedWriteableRegistry namedWriteableRegistry;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
licensee = mock(WatcherLicensee.class);
|
||||
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
|
||||
}
|
||||
|
||||
public void testWritableRegistration() throws Exception {
|
||||
new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
|
||||
verify(namedWriteableRegistry).register(eq(WatcherFeatureSet.Usage.class), eq("xpack.usage.watcher"), anyObject());
|
||||
}
|
||||
|
||||
public void testAvailable() throws Exception {
|
||||
WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
|
||||
boolean available = randomBoolean();
|
||||
when(licensee.available()).thenReturn(available);
|
||||
assertThat(featureSet.available(), is(available));
|
||||
}
|
||||
|
||||
public void testEnabled() throws Exception {
|
||||
boolean enabled = randomBoolean();
|
||||
Settings.Builder settings = Settings.builder();
|
||||
if (enabled) {
|
||||
if (randomBoolean()) {
|
||||
settings.put("xpack.watcher.enabled", enabled);
|
||||
}
|
||||
} else {
|
||||
settings.put("xpack.watcher.enabled", enabled);
|
||||
}
|
||||
WatcherFeatureSet featureSet = new WatcherFeatureSet(settings.build(), licensee, namedWriteableRegistry);
|
||||
assertThat(featureSet.enabled(), is(enabled));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue