Add license feature usage api (#59342) (#59571)

This commit adds a new api to track when gold+ features are used within
x-pack. The tracking is done internally whenever a feature is checked
against the current license. The output of the api is a list of each
used feature, which includes the name, license level, and last time it
was used. In addition to a unit test for the tracking, a rest test is
added which ensures starting up a default configured node does not
result in any features registering as used.

There are a couple features which currently do not work well with the
tracking, as they are checked in a manner that makes them look always
used. Those features will be fixed in followups, and in this PR they are
omitted from the feature usage output.
This commit is contained in:
Ryan Ernst 2020-07-14 14:34:59 -07:00 committed by GitHub
parent e5baacbe2e
commit 3b688bfee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 538 additions and 123 deletions

View File

@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common;
import java.util.function.Supplier;
public class MemoizedSupplier<T> implements Supplier<T> {
private Supplier<T> supplier;
private T value;
public MemoizedSupplier(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public T get() {
if (supplier != null) {
value = supplier.get();
supplier = null;
}
return value;
}
}

View File

@ -7,6 +7,7 @@ import java.nio.file.Paths
apply plugin: 'elasticsearch.esplugin' apply plugin: 'elasticsearch.esplugin'
apply plugin: 'elasticsearch.publish' apply plugin: 'elasticsearch.publish'
apply plugin: 'elasticsearch.internal-cluster-test' apply plugin: 'elasticsearch.internal-cluster-test'
apply plugin: 'elasticsearch.yaml-rest-test'
archivesBaseName = 'x-pack-core' archivesBaseName = 'x-pack-core'
@ -57,6 +58,8 @@ dependencies {
transitive = false transitive = false
} }
yamlRestTestImplementation project(':x-pack:plugin:core')
} }
ext.expansions = [ ext.expansions = [
@ -143,7 +146,18 @@ thirdPartyAudit.ignoreMissingClasses(
'javax.servlet.ServletContextListener' 'javax.servlet.ServletContextListener'
) )
// xpack modules are installed in real clusters as the meta plugin, so restResources {
// installing them as individual plugins for integ tests doesn't make sense, restApi {
// so we disable integ tests includeCore '*'
integTest.enabled = false }
}
testClusters.yamlRestTest {
testDistribution = 'default'
setting 'xpack.security.enabled', 'true'
setting 'xpack.license.self_generated.type', 'trial'
keystore 'bootstrap.password', 'x-pack-test-password'
user username: "x_pack_rest_user", password: "x-pack-test-password"
}
testingConventions.enabled = false

View File

@ -0,0 +1,27 @@
/*
* 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.license;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;
import java.io.IOException;
public class GetFeatureUsageRequest extends ActionRequest {
public GetFeatureUsageRequest() {}
public GetFeatureUsageRequest(StreamInput in) throws IOException {
super(in);
}
@Override
public ActionRequestValidationException validate() {
return null;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.license;
import org.elasticsearch.action.ActionResponse;
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.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
public class GetFeatureUsageResponse extends ActionResponse implements ToXContentObject {
public static class FeatureUsageInfo implements Writeable {
public final String name;
public final ZonedDateTime lastUsedTime;
public final String licenseLevel;
public FeatureUsageInfo(String name, ZonedDateTime lastUsedTime, String licenseLevel) {
this.name = name;
this.lastUsedTime = lastUsedTime;
this.licenseLevel = licenseLevel;
}
public FeatureUsageInfo(StreamInput in) throws IOException {
this.name = in.readString();
this.lastUsedTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(in.readLong()), ZoneOffset.UTC);
this.licenseLevel = in.readString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeLong(lastUsedTime.toEpochSecond());
out.writeString(licenseLevel);
}
}
private List<FeatureUsageInfo> features;
public GetFeatureUsageResponse(List<FeatureUsageInfo> features) {
this.features = Collections.unmodifiableList(features);
}
public GetFeatureUsageResponse(StreamInput in) throws IOException {
this.features = in.readList(FeatureUsageInfo::new);
}
public List<FeatureUsageInfo> getFeatures() {
return features;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeList(features);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startArray("features");
for (FeatureUsageInfo feature : features) {
builder.startObject();
builder.field("name", feature.name);
builder.field("last_used", feature.lastUsedTime.toString());
builder.field("license_level", feature.licenseLevel);
builder.endObject();
}
builder.endArray();
builder.endObject();
return builder;
}
}

View File

@ -66,7 +66,8 @@ public class Licensing implements ActionPlugin {
new ActionHandler<>(PostStartTrialAction.INSTANCE, TransportPostStartTrialAction.class), new ActionHandler<>(PostStartTrialAction.INSTANCE, TransportPostStartTrialAction.class),
new ActionHandler<>(GetTrialStatusAction.INSTANCE, TransportGetTrialStatusAction.class), new ActionHandler<>(GetTrialStatusAction.INSTANCE, TransportGetTrialStatusAction.class),
new ActionHandler<>(PostStartBasicAction.INSTANCE, TransportPostStartBasicAction.class), new ActionHandler<>(PostStartBasicAction.INSTANCE, TransportPostStartBasicAction.class),
new ActionHandler<>(GetBasicStatusAction.INSTANCE, TransportGetBasicStatusAction.class)); new ActionHandler<>(GetBasicStatusAction.INSTANCE, TransportGetBasicStatusAction.class),
new ActionHandler<>(TransportGetFeatureUsageAction.TYPE, TransportGetFeatureUsageAction.class));
} }
@Override @Override
@ -81,6 +82,7 @@ public class Licensing implements ActionPlugin {
handlers.add(new RestGetBasicStatus()); handlers.add(new RestGetBasicStatus());
handlers.add(new RestPostStartTrialLicense()); handlers.add(new RestPostStartTrialLicense());
handlers.add(new RestPostStartBasicLicense()); handlers.add(new RestPostStartBasicLicense());
handlers.add(new RestGetFeatureUsageAction());
return handlers; return handlers;
} }

View File

@ -0,0 +1,37 @@
/*
* 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.license;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.rest.RestRequest.Method.GET;
public class RestGetFeatureUsageAction extends BaseRestHandler {
@Override
public String getName() {
return "get_feature_usage";
}
@Override
public List<Route> routes() {
return Collections.singletonList(new Route(GET, "/_license/feature_usage"));
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
return channel -> client.execute(TransportGetFeatureUsageAction.TYPE, new GetFeatureUsageRequest(),
new RestToXContentListener<>(channel));
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.license;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.transport.TransportService;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class TransportGetFeatureUsageAction extends HandledTransportAction<GetFeatureUsageRequest, GetFeatureUsageResponse> {
public static final ActionType<GetFeatureUsageResponse> TYPE =
new ActionType<>("cluster:admin/xpack/license/feature_usage", GetFeatureUsageResponse::new);
private final XPackLicenseState licenseState;
@Inject
public TransportGetFeatureUsageAction(TransportService transportService, ActionFilters actionFilters,
XPackLicenseState licenseState) {
super(TYPE.name(), transportService, actionFilters, GetFeatureUsageRequest::new);
this.licenseState = licenseState;
}
@Override
protected void doExecute(Task task, GetFeatureUsageRequest request, ActionListener<GetFeatureUsageResponse> listener) {
Map<XPackLicenseState.Feature, Long> featureUsage = licenseState.getLastUsed();
List<GetFeatureUsageResponse.FeatureUsageInfo> usageInfos = new ArrayList<>();
for (Map.Entry<XPackLicenseState.Feature, Long> entry : featureUsage.entrySet()) {
XPackLicenseState.Feature feature = entry.getKey();
String name = feature.name().toLowerCase(Locale.ROOT);
ZonedDateTime lastUsedTime = Instant.ofEpochMilli(entry.getValue()).atZone(ZoneOffset.UTC);
String licenseLevel = feature.minimumOperationMode.name().toLowerCase(Locale.ROOT);
usageInfos.add(new GetFeatureUsageResponse.FeatureUsageInfo(name, lastUsedTime, licenseLevel));
}
listener.onResponse(new GetFeatureUsageResponse(usageInfos));
}
}

View File

@ -16,14 +16,19 @@ import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.monitoring.MonitoringField; import org.elasticsearch.xpack.core.monitoring.MonitoringField;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.LongSupplier;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
/** /**
* A holder for the current state of the license for all xpack features. * A holder for the current state of the license for all xpack features.
@ -106,6 +111,14 @@ public class XPackLicenseState {
} }
} }
// temporarily non tracked feeatures which need rework in how they are checked
// so they are not tracked as always used
private static final Set<Feature> NON_TRACKED_FEATURES = org.elasticsearch.common.collect.Set.of(
Feature.SECURITY_IP_FILTERING,
Feature.SECURITY_ALL_REALMS,
Feature.SECURITY_STANDARD_REALMS
);
/** Messages for each feature which are printed when the license expires. */ /** Messages for each feature which are printed when the license expires. */
static final Map<String, String[]> EXPIRATION_MESSAGES; static final Map<String, String[]> EXPIRATION_MESSAGES;
static { static {
@ -398,6 +411,8 @@ public class XPackLicenseState {
private final List<LicenseStateListener> listeners; private final List<LicenseStateListener> listeners;
private final boolean isSecurityEnabled; private final boolean isSecurityEnabled;
private final boolean isSecurityExplicitlyEnabled; private final boolean isSecurityExplicitlyEnabled;
private final Map<Feature, LongAccumulator> lastUsed;
private final LongSupplier epochMillisProvider;
// Since Status is the only field that can be updated, we do not need to synchronize access to // Since Status is the only field that can be updated, we do not need to synchronize access to
// XPackLicenseState. However, if status is read multiple times in a method, it can change in between // XPackLicenseState. However, if status is read multiple times in a method, it can change in between
@ -405,18 +420,31 @@ public class XPackLicenseState {
// is only read once. // is only read once.
private volatile Status status = new Status(OperationMode.TRIAL, true); private volatile Status status = new Status(OperationMode.TRIAL, true);
public XPackLicenseState(Settings settings) { public XPackLicenseState(Settings settings, LongSupplier epochMillisProvider) {
this.listeners = new CopyOnWriteArrayList<>(); this.listeners = new CopyOnWriteArrayList<>();
this.isSecurityEnabled = XPackSettings.SECURITY_ENABLED.get(settings); this.isSecurityEnabled = XPackSettings.SECURITY_ENABLED.get(settings);
this.isSecurityExplicitlyEnabled = isSecurityEnabled && isSecurityExplicitlyEnabled(settings); this.isSecurityExplicitlyEnabled = isSecurityEnabled && isSecurityExplicitlyEnabled(settings);
// prepopulate feature last used map with entries for non basic features, which are the ones we
// care to actually keep track of
Map<Feature, LongAccumulator> lastUsed = new EnumMap<>(Feature.class);
for (Feature feature : Feature.values()) {
if (feature.minimumOperationMode.compareTo(OperationMode.BASIC) > 0 && NON_TRACKED_FEATURES.contains(feature) == false) {
lastUsed.put(feature, new LongAccumulator(Long::max, 0));
}
}
this.lastUsed = lastUsed;
this.epochMillisProvider = epochMillisProvider;
} }
private XPackLicenseState(List<LicenseStateListener> listeners, boolean isSecurityEnabled, boolean isSecurityExplicitlyEnabled, private XPackLicenseState(List<LicenseStateListener> listeners, boolean isSecurityEnabled, boolean isSecurityExplicitlyEnabled,
Status status) { Status status, Map<Feature, LongAccumulator> lastUsed, LongSupplier epochMillisProvider) {
this.listeners = listeners; this.listeners = listeners;
this.isSecurityEnabled = isSecurityEnabled; this.isSecurityEnabled = isSecurityEnabled;
this.isSecurityExplicitlyEnabled = isSecurityExplicitlyEnabled; this.isSecurityExplicitlyEnabled = isSecurityExplicitlyEnabled;
this.status = status; this.status = status;
this.lastUsed = lastUsed;
this.epochMillisProvider = epochMillisProvider;
} }
private static boolean isSecurityExplicitlyEnabled(Settings settings) { private static boolean isSecurityExplicitlyEnabled(Settings settings) {
@ -480,8 +508,12 @@ public class XPackLicenseState {
* Checks whether the given feature is allowed, tracking the last usage time. * Checks whether the given feature is allowed, tracking the last usage time.
*/ */
public boolean checkFeature(Feature feature) { public boolean checkFeature(Feature feature) {
// TODO: usage tracking is not yet implemented boolean allowed = isAllowed(feature);
return isAllowed(feature); LongAccumulator maxEpochAccumulator = lastUsed.get(feature);
if (maxEpochAccumulator != null) {
maxEpochAccumulator.accumulate(epochMillisProvider.getAsLong());
}
return allowed;
} }
/** /**
@ -493,6 +525,17 @@ public class XPackLicenseState {
return isAllowedByLicense(feature.minimumOperationMode, feature.needsActive); return isAllowedByLicense(feature.minimumOperationMode, feature.needsActive);
} }
/**
* Returns a mapping of gold+ features to the last time that feature was used.
*
* Note that if a feature has not been used, it will not appear in the map.
*/
public Map<Feature, Long> getLastUsed() {
return lastUsed.entrySet().stream()
.filter(e -> e.getValue().get() != 0) // feature was never used
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get()));
}
public static boolean isMachineLearningAllowedForOperationMode(final OperationMode operationMode) { public static boolean isMachineLearningAllowedForOperationMode(final OperationMode operationMode) {
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM); return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
} }
@ -566,7 +609,7 @@ public class XPackLicenseState {
*/ */
public XPackLicenseState copyCurrentLicenseState() { public XPackLicenseState copyCurrentLicenseState() {
return executeAgainstStatus(status -> return executeAgainstStatus(status ->
new XPackLicenseState(listeners, isSecurityEnabled, isSecurityExplicitlyEnabled, status)); new XPackLicenseState(listeners, isSecurityEnabled, isSecurityExplicitlyEnabled, status, lastUsed, epochMillisProvider));
} }
/** /**

View File

@ -85,6 +85,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.LongSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -136,6 +137,7 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
private static final SetOnce<XPackLicenseState> licenseState = new SetOnce<>(); private static final SetOnce<XPackLicenseState> licenseState = new SetOnce<>();
private static final SetOnce<SSLService> sslService = new SetOnce<>(); private static final SetOnce<SSLService> sslService = new SetOnce<>();
private static final SetOnce<LicenseService> licenseService = new SetOnce<>(); private static final SetOnce<LicenseService> licenseService = new SetOnce<>();
private static final SetOnce<LongSupplier> epochMillisSupplier = new SetOnce<>();
public XPackPlugin( public XPackPlugin(
final Settings settings, final Settings settings,
@ -146,7 +148,7 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
this.settings = settings; this.settings = settings;
this.transportClientMode = transportClientMode(settings); this.transportClientMode = transportClientMode(settings);
setLicenseState(new XPackLicenseState(settings)); setLicenseState(new XPackLicenseState(settings, () -> getEpochMillisSupplier().getAsLong()));
this.licensing = new Licensing(settings); this.licensing = new Licensing(settings);
} }
@ -159,9 +161,13 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
protected SSLService getSslService() { return getSharedSslService(); } protected SSLService getSslService() { return getSharedSslService(); }
protected LicenseService getLicenseService() { return getSharedLicenseService(); } protected LicenseService getLicenseService() { return getSharedLicenseService(); }
protected XPackLicenseState getLicenseState() { return getSharedLicenseState(); } protected XPackLicenseState getLicenseState() { return getSharedLicenseState(); }
protected LongSupplier getEpochMillisSupplier() { return getSharedEpochMillisSupplier(); }
protected void setSslService(SSLService sslService) { XPackPlugin.sslService.set(sslService); } protected void setSslService(SSLService sslService) { XPackPlugin.sslService.set(sslService); }
protected void setLicenseService(LicenseService licenseService) { XPackPlugin.licenseService.set(licenseService); } protected void setLicenseService(LicenseService licenseService) { XPackPlugin.licenseService.set(licenseService); }
protected void setLicenseState(XPackLicenseState licenseState) { XPackPlugin.licenseState.set(licenseState); } protected void setLicenseState(XPackLicenseState licenseState) { XPackPlugin.licenseState.set(licenseState); }
protected void setEpochMillisSupplier(LongSupplier epochMillisSupplier) {
XPackPlugin.epochMillisSupplier.set(epochMillisSupplier);
}
public static SSLService getSharedSslService() { public static SSLService getSharedSslService() {
final SSLService ssl = XPackPlugin.sslService.get(); final SSLService ssl = XPackPlugin.sslService.get();
@ -172,6 +178,7 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
} }
public static LicenseService getSharedLicenseService() { return licenseService.get(); } public static LicenseService getSharedLicenseService() { return licenseService.get(); }
public static XPackLicenseState getSharedLicenseState() { return licenseState.get(); } public static XPackLicenseState getSharedLicenseState() { return licenseState.get(); }
public static LongSupplier getSharedEpochMillisSupplier() { return epochMillisSupplier.get(); }
/** /**
* Checks if the cluster state allows this node to add x-pack metadata to the cluster state, * Checks if the cluster state allows this node to add x-pack metadata to the cluster state,
@ -268,6 +275,8 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
setLicenseService(new LicenseService(settings, clusterService, getClock(), setLicenseService(new LicenseService(settings, clusterService, getClock(),
environment, resourceWatcherService, getLicenseState())); environment, resourceWatcherService, getLicenseState()));
setEpochMillisSupplier(threadPool::absoluteTimeInMillis);
// It is useful to override these as they are what guice is injecting into actions // It is useful to override these as they are what guice is injecting into actions
components.add(sslService); components.add(sslService);
components.add(getLicenseService()); components.add(getLicenseService());

View File

@ -28,7 +28,7 @@ public class LicenseFIPSTests extends AbstractLicenseServiceTestCase {
.put("xpack.security.transport.ssl.enabled", true) .put("xpack.security.transport.ssl.enabled", true)
.put("xpack.security.fips_mode.enabled", randomBoolean()) .put("xpack.security.fips_mode.enabled", randomBoolean())
.build(); .build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();
@ -52,7 +52,7 @@ public class LicenseFIPSTests extends AbstractLicenseServiceTestCase {
.put("xpack.security.transport.ssl.enabled", true) .put("xpack.security.transport.ssl.enabled", true)
.put("xpack.security.fips_mode.enabled", true) .put("xpack.security.fips_mode.enabled", true)
.build(); .build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();
@ -67,7 +67,7 @@ public class LicenseFIPSTests extends AbstractLicenseServiceTestCase {
.put("xpack.security.transport.ssl.enabled", true) .put("xpack.security.transport.ssl.enabled", true)
.put("xpack.security.fips_mode.enabled", false) .put("xpack.security.fips_mode.enabled", false)
.build(); .build();
licenseState = new XPackLicenseState(settings); licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();

View File

@ -33,7 +33,7 @@ public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
request.acknowledge(true); request.acknowledge(true);
request.license(newLicense); request.license(newLicense);
Settings settings = Settings.builder().put("xpack.security.enabled", true).build(); Settings settings = Settings.builder().put("xpack.security.enabled", true).build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
inetAddress = InetAddress.getLoopbackAddress(); inetAddress = InetAddress.getLoopbackAddress();
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
@ -48,7 +48,7 @@ public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
.put("discovery.type", "single-node") .put("discovery.type", "single-node")
.build(); .build();
licenseService.stop(); licenseService.stop();
licenseState = new XPackLicenseState(settings); licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();
licenseService.registerLicense(request, responseFuture); licenseService.registerLicense(request, responseFuture);
@ -62,7 +62,7 @@ public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
request.acknowledge(true); request.acknowledge(true);
request.license(newLicense); request.license(newLicense);
Settings settings = Settings.builder().put("xpack.security.enabled", true).build(); Settings settings = Settings.builder().put("xpack.security.enabled", true).build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
inetAddress = TransportAddress.META_ADDRESS; inetAddress = TransportAddress.META_ADDRESS;
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
@ -74,7 +74,7 @@ public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
settings = Settings.builder().put("xpack.security.enabled", false).build(); settings = Settings.builder().put("xpack.security.enabled", false).build();
licenseService.stop(); licenseService.stop();
licenseState = new XPackLicenseState(settings); licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();
licenseService.registerLicense(request, responseFuture); licenseService.registerLicense(request, responseFuture);
@ -85,7 +85,7 @@ public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
.put("xpack.security.transport.ssl.enabled", true) .put("xpack.security.transport.ssl.enabled", true)
.build(); .build();
licenseService.stop(); licenseService.stop();
licenseState = new XPackLicenseState(settings); licenseState = new XPackLicenseState(settings, () -> 0);
setInitialState(null, licenseState, settings); setInitialState(null, licenseState, settings);
licenseService.start(); licenseService.start();
licenseService.registerLicense(request, responseFuture); licenseService.registerLicense(request, responseFuture);

View File

@ -362,7 +362,7 @@ public class TestUtils {
public final List<Version> trialVersionUpdates = new ArrayList<>(); public final List<Version> trialVersionUpdates = new ArrayList<>();
public AssertingLicenseState() { public AssertingLicenseState() {
super(Settings.EMPTY); super(Settings.EMPTY, () -> 0);
} }
@Override @Override
@ -383,7 +383,7 @@ public class TestUtils {
} }
public UpdatableLicenseState(Settings settings) { public UpdatableLicenseState(Settings settings) {
super(settings); super(settings, () -> 0);
} }
@Override @Override
@ -393,7 +393,7 @@ public class TestUtils {
} }
public static XPackLicenseState newTestLicenseState() { public static XPackLicenseState newTestLicenseState() {
return new XPackLicenseState(Settings.EMPTY); return new XPackLicenseState(Settings.EMPTY, () -> 0);
} }
public static void putLicense(Metadata.Builder builder, License license) { public static void putLicense(Metadata.Builder builder, License license) {

View File

@ -15,6 +15,7 @@ import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.XPackSettings;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -25,6 +26,9 @@ import static org.elasticsearch.license.License.OperationMode.PLATINUM;
import static org.elasticsearch.license.License.OperationMode.STANDARD; import static org.elasticsearch.license.License.OperationMode.STANDARD;
import static org.elasticsearch.license.License.OperationMode.TRIAL; import static org.elasticsearch.license.License.OperationMode.TRIAL;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.collection.IsMapContaining.hasEntry;
import static org.hamcrest.collection.IsMapContaining.hasKey;
import static org.hamcrest.core.IsNot.not;
/** /**
* Unit tests for the {@link XPackLicenseState} * Unit tests for the {@link XPackLicenseState}
@ -77,7 +81,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityDefaults() { public void testSecurityDefaults() {
Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build(); Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
assertThat(licenseState.checkFeature(Feature.SECURITY_IP_FILTERING), is(true)); assertThat(licenseState.checkFeature(Feature.SECURITY_IP_FILTERING), is(true));
assertThat(licenseState.checkFeature(Feature.SECURITY_AUDITING), is(true)); assertThat(licenseState.checkFeature(Feature.SECURITY_AUDITING), is(true));
@ -92,8 +96,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testTransportSslDoesNotAutomaticallyEnableSecurityOnTrialLicense() { public void testTransportSslDoesNotAutomaticallyEnableSecurityOnTrialLicense() {
Settings settings = Settings.builder().put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true).build(); Settings settings = Settings.builder().put(XPackSettings.TRANSPORT_SSL_ENABLED.getKey(), true).build();
final XPackLicenseState licenseState; final XPackLicenseState licenseState= new XPackLicenseState(settings, () -> 0);
licenseState = new XPackLicenseState(settings);
assertSecurityNotAllowed(licenseState); assertSecurityNotAllowed(licenseState);
} }
@ -116,7 +119,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityBasicWithExplicitSecurityEnabled() { public void testSecurityBasicWithExplicitSecurityEnabled() {
final Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build(); final Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(BASIC, true, null); licenseState.update(BASIC, true, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -148,7 +151,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityEnabledBasicExpired() { public void testSecurityEnabledBasicExpired() {
Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build(); Settings settings = Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build();
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(BASIC, false, null); licenseState.update(BASIC, false, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -164,7 +167,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityStandard() { public void testSecurityStandard() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(STANDARD, true, null); licenseState.update(STANDARD, true, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -178,7 +181,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityStandardExpired() { public void testSecurityStandardExpired() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(STANDARD, false, null); licenseState.update(STANDARD, false, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -192,7 +195,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityGold() { public void testSecurityGold() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(GOLD, true, null); licenseState.update(GOLD, true, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -209,7 +212,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityGoldExpired() { public void testSecurityGoldExpired() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(GOLD, false, null); licenseState.update(GOLD, false, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -226,7 +229,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityPlatinum() { public void testSecurityPlatinum() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(PLATINUM, true, null); licenseState.update(PLATINUM, true, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -243,7 +246,7 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testSecurityPlatinumExpired() { public void testSecurityPlatinumExpired() {
Settings settings = randomFrom(Settings.EMPTY, Settings settings = randomFrom(Settings.EMPTY,
Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build()); Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), true).build());
XPackLicenseState licenseState = new XPackLicenseState(settings); XPackLicenseState licenseState = new XPackLicenseState(settings, () -> 0);
licenseState.update(PLATINUM, false, null); licenseState.update(PLATINUM, false, null);
assertThat(licenseState.isSecurityEnabled(), is(true)); assertThat(licenseState.isSecurityEnabled(), is(true));
@ -566,4 +569,24 @@ public class XPackLicenseStateTests extends ESTestCase {
public void testTransformInactiveBasic() { public void testTransformInactiveBasic() {
assertAllowed(BASIC, false, s -> s.checkFeature(Feature.TRANSFORM), false); assertAllowed(BASIC, false, s -> s.checkFeature(Feature.TRANSFORM), false);
} }
public void testLastUsed() {
Feature basicFeature = Feature.SECURITY;
Feature goldFeature = Feature.SECURITY_DLS_FLS;
AtomicInteger currentTime = new AtomicInteger(100); // non zero start time
XPackLicenseState licenseState = new XPackLicenseState(Settings.EMPTY, currentTime::get);
assertThat("basic features not tracked", licenseState.getLastUsed(), not(hasKey(basicFeature)));
assertThat("initial epoch time", licenseState.getLastUsed(), not(hasKey(goldFeature)));
licenseState.isAllowed(basicFeature);
assertThat("basic features still not tracked", licenseState.getLastUsed(), not(hasKey(basicFeature)));
licenseState.isAllowed(goldFeature);
assertThat("isAllowed does not track", licenseState.getLastUsed(), not(hasKey(goldFeature)));
licenseState.checkFeature(basicFeature);
assertThat("basic features still not tracked", licenseState.getLastUsed(), not(hasKey(basicFeature)));
licenseState.checkFeature(goldFeature);
assertThat("checkFeature tracks used time", licenseState.getLastUsed(), hasEntry(goldFeature, 100L));
currentTime.set(200);
licenseState.checkFeature(goldFeature);
assertThat("checkFeature updates tracked time", licenseState.getLastUsed(), hasEntry(goldFeature, 200L));
}
} }

View File

@ -90,6 +90,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.LongSupplier;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
@ -103,6 +104,7 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip
private XPackLicenseState licenseState; private XPackLicenseState licenseState;
private SSLService sslService; private SSLService sslService;
private LicenseService licenseService; private LicenseService licenseService;
private LongSupplier epochMillisSupplier;
protected List<Plugin> plugins = new ArrayList<>(); protected List<Plugin> plugins = new ArrayList<>();
public LocalStateCompositeXPackPlugin(final Settings settings, final Path configPath) { public LocalStateCompositeXPackPlugin(final Settings settings, final Path configPath) {
@ -150,6 +152,15 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip
return modules; return modules;
} }
protected LongSupplier getEpochMillisSupplier() {
return epochMillisSupplier;
}
@Override
protected void setEpochMillisSupplier(LongSupplier epochMillisSupplier) {
this.epochMillisSupplier = epochMillisSupplier;
}
@Override @Override
public Collection<Object> createComponents(Client client, ClusterService clusterService, ThreadPool threadPool, public Collection<Object> createComponents(Client client, ClusterService clusterService, ThreadPool threadPool,
ResourceWatcherService resourceWatcherService, ScriptService scriptService, ResourceWatcherService resourceWatcherService, ScriptService scriptService,

View File

@ -0,0 +1,39 @@
/*
* 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.license;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class XPackCoreClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
private static final String BASIC_AUTH_VALUE =
basicAuthHeaderValue("x_pack_rest_user", new SecureString("x-pack-test-password"));
public XPackCoreClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
super(testCandidate);
}
@ParametersFactory
public static Iterable<Object[]> parameters() throws Exception {
return ESClientYamlSuiteTestCase.createParameters();
}
@Override
protected Settings restClientSettings() {
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE)
.build();
}
}

View File

@ -0,0 +1,17 @@
{
"license.get_feature_usage":{
"stability":"experimental",
"url":{
"paths":[
{
"path":"/_license/feature_usage",
"methods":[
"GET"
]
}
]
},
"params":{
}
}
}

View File

@ -0,0 +1,6 @@
---
"No features should be used just by starting up with default configuration":
- do:
license.get_feature_usage: {}
- length: { features: 0 }

View File

@ -48,7 +48,7 @@ public class InvalidLicenseEnforcer implements LicenseStateListener {
@Override @Override
public void licenseStateChanged() { public void licenseStateChanged() {
assert licenseStateListenerRegistered; assert licenseStateListenerRegistered;
if (licenseState.checkFeature(XPackLicenseState.Feature.MACHINE_LEARNING) == false) { if (licenseState.isAllowed(XPackLicenseState.Feature.MACHINE_LEARNING) == false) {
// if the license has expired, close jobs and datafeeds // if the license has expired, close jobs and datafeeds
threadPool.generic().execute(new AbstractRunnable() { threadPool.generic().execute(new AbstractRunnable() {
@Override @Override

View File

@ -338,7 +338,7 @@ public class MachineLearningFeatureSetTests extends ESTestCase {
} }
public void testUsageWithOrphanedTask() throws Exception { public void testUsageWithOrphanedTask() throws Exception {
when(licenseState.checkFeature(XPackLicenseState.Feature.MACHINE_LEARNING)).thenReturn(true); when(licenseState.isAllowed(XPackLicenseState.Feature.MACHINE_LEARNING)).thenReturn(true);
Settings.Builder settings = Settings.builder().put(commonSettings); Settings.Builder settings = Settings.builder().put(commonSettings);
settings.put("xpack.ml.enabled", true); settings.put("xpack.ml.enabled", true);

View File

@ -1057,11 +1057,14 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
if (enabled) { if (enabled) {
return index -> { return index -> {
XPackLicenseState licenseState = getLicenseState(); XPackLicenseState licenseState = getLicenseState();
if (licenseState.isSecurityEnabled() == false || licenseState.checkFeature(Feature.SECURITY_DLS_FLS) == false) { if (licenseState.isSecurityEnabled() == false) {
return MapperPlugin.NOOP_FIELD_PREDICATE; return MapperPlugin.NOOP_FIELD_PREDICATE;
} }
IndicesAccessControl indicesAccessControl = threadContext.get().getTransient( IndicesAccessControl indicesAccessControl = threadContext.get().getTransient(
AuthorizationServiceField.INDICES_PERMISSIONS_KEY); AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
if (indicesAccessControl == null) {
return MapperPlugin.NOOP_FIELD_PREDICATE;
}
IndicesAccessControl.IndexAccessControl indexPermissions = indicesAccessControl.getIndexPermissions(index); IndicesAccessControl.IndexAccessControl indexPermissions = indicesAccessControl.getIndexPermissions(index);
if (indexPermissions == null) { if (indexPermissions == null) {
return MapperPlugin.NOOP_FIELD_PREDICATE; return MapperPlugin.NOOP_FIELD_PREDICATE;
@ -1073,6 +1076,10 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
if (fieldPermissions.hasFieldLevelSecurity() == false) { if (fieldPermissions.hasFieldLevelSecurity() == false) {
return MapperPlugin.NOOP_FIELD_PREDICATE; return MapperPlugin.NOOP_FIELD_PREDICATE;
} }
if (licenseState.checkFeature(Feature.SECURITY_DLS_FLS) == false) {
// check license last, once we know FLS is actually used
return MapperPlugin.NOOP_FIELD_PREDICATE;
}
return fieldPermissions::grantsAccessTo; return fieldPermissions::grantsAccessTo;
}; };
} }

View File

@ -12,6 +12,7 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkItemRequest; import org.elasticsearch.action.bulk.BulkItemRequest;
import org.elasticsearch.action.bulk.BulkShardRequest; import org.elasticsearch.action.bulk.BulkShardRequest;
import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.MemoizedSupplier;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.license.XPackLicenseState.Feature; import org.elasticsearch.license.XPackLicenseState.Feature;
@ -41,7 +42,8 @@ public class BulkShardRequestInterceptor implements RequestInterceptor {
@Override @Override
public void intercept(RequestInfo requestInfo, AuthorizationEngine authzEngine, AuthorizationInfo authorizationInfo, public void intercept(RequestInfo requestInfo, AuthorizationEngine authzEngine, AuthorizationInfo authorizationInfo,
ActionListener<Void> listener) { ActionListener<Void> listener) {
boolean shouldIntercept = licenseState.isSecurityEnabled() && licenseState.checkFeature(Feature.SECURITY_DLS_FLS); boolean shouldIntercept = licenseState.isSecurityEnabled();
MemoizedSupplier<Boolean> licenseChecker = new MemoizedSupplier<>(() -> licenseState.checkFeature(Feature.SECURITY_DLS_FLS));
if (requestInfo.getRequest() instanceof BulkShardRequest && shouldIntercept) { if (requestInfo.getRequest() instanceof BulkShardRequest && shouldIntercept) {
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY); IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
@ -54,7 +56,7 @@ public class BulkShardRequestInterceptor implements RequestInterceptor {
boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity(); boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions(); boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
if (fls || dls) { if (fls || dls) {
if (bulkItemRequest.request() instanceof UpdateRequest) { if (licenseChecker.get() && bulkItemRequest.request() instanceof UpdateRequest) {
found = true; found = true;
logger.trace("aborting bulk item update request for index [{}]", bulkItemRequest.index()); logger.trace("aborting bulk item update request for index [{}]", bulkItemRequest.index());
bulkItemRequest.abort(bulkItemRequest.index(), new ElasticsearchSecurityException("Can't execute a bulk " + bulkItemRequest.abort(bulkItemRequest.index(), new ElasticsearchSecurityException("Can't execute a bulk " +

View File

@ -9,6 +9,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.common.MemoizedSupplier;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.license.XPackLicenseState.Feature; import org.elasticsearch.license.XPackLicenseState.Feature;
@ -39,7 +40,8 @@ abstract class FieldAndDocumentLevelSecurityRequestInterceptor implements Reques
ActionListener<Void> listener) { ActionListener<Void> listener) {
if (requestInfo.getRequest() instanceof IndicesRequest) { if (requestInfo.getRequest() instanceof IndicesRequest) {
IndicesRequest indicesRequest = (IndicesRequest) requestInfo.getRequest(); IndicesRequest indicesRequest = (IndicesRequest) requestInfo.getRequest();
boolean shouldIntercept = licenseState.isSecurityEnabled() && licenseState.checkFeature(Feature.SECURITY_DLS_FLS); boolean shouldIntercept = licenseState.isSecurityEnabled();
MemoizedSupplier<Boolean> licenseChecker = new MemoizedSupplier<>(() -> licenseState.checkFeature(Feature.SECURITY_DLS_FLS));
if (supports(indicesRequest) && shouldIntercept) { if (supports(indicesRequest) && shouldIntercept) {
final IndicesAccessControl indicesAccessControl = final IndicesAccessControl indicesAccessControl =
threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY); threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
@ -48,7 +50,7 @@ abstract class FieldAndDocumentLevelSecurityRequestInterceptor implements Reques
if (indexAccessControl != null) { if (indexAccessControl != null) {
boolean fieldLevelSecurityEnabled = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity(); boolean fieldLevelSecurityEnabled = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
boolean documentLevelSecurityEnabled = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions(); boolean documentLevelSecurityEnabled = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
if (fieldLevelSecurityEnabled || documentLevelSecurityEnabled) { if ((fieldLevelSecurityEnabled || documentLevelSecurityEnabled) && licenseChecker.get()) {
logger.trace("intercepted request for index [{}] with field level access controls [{}] " + logger.trace("intercepted request for index [{}] with field level access controls [{}] " +
"document level access controls [{}]. disabling conflicting features", "document level access controls [{}]. disabling conflicting features",
index, fieldLevelSecurityEnabled, documentLevelSecurityEnabled); index, fieldLevelSecurityEnabled, documentLevelSecurityEnabled);

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authz.interceptor;
import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.common.MemoizedSupplier;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
@ -52,7 +53,8 @@ public final class IndicesAliasesRequestInterceptor implements RequestIntercepto
final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState(); final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState();
final AuditTrail auditTrail = auditTrailService.get(); final AuditTrail auditTrail = auditTrailService.get();
if (frozenLicenseState.isSecurityEnabled()) { if (frozenLicenseState.isSecurityEnabled()) {
if (frozenLicenseState.checkFeature(Feature.SECURITY_DLS_FLS)) { MemoizedSupplier<Boolean> licenseChecker =
new MemoizedSupplier<>(() -> frozenLicenseState.checkFeature(Feature.SECURITY_DLS_FLS));
IndicesAccessControl indicesAccessControl = IndicesAccessControl indicesAccessControl =
threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY); threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
for (IndicesAliasesRequest.AliasActions aliasAction : request.getAliasActions()) { for (IndicesAliasesRequest.AliasActions aliasAction : request.getAliasActions()) {
@ -63,7 +65,7 @@ public final class IndicesAliasesRequestInterceptor implements RequestIntercepto
if (indexAccessControl != null) { if (indexAccessControl != null) {
final boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity(); final boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
final boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions(); final boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
if (fls || dls) { if ((fls || dls) && licenseChecker.get()) {
listener.onFailure(new ElasticsearchSecurityException("Alias requests are not allowed for " + listener.onFailure(new ElasticsearchSecurityException("Alias requests are not allowed for " +
"users who have field or document level security enabled on one of the indices", "users who have field or document level security enabled on one of the indices",
RestStatus.BAD_REQUEST)); RestStatus.BAD_REQUEST));
@ -73,7 +75,6 @@ public final class IndicesAliasesRequestInterceptor implements RequestIntercepto
} }
} }
} }
}
Map<String, List<String>> indexToAliasesMap = request.getAliasActions().stream() Map<String, List<String>> indexToAliasesMap = request.getAliasActions().stream()
.filter(aliasAction -> aliasAction.actionType() == IndicesAliasesRequest.AliasActions.Type.ADD) .filter(aliasAction -> aliasAction.actionType() == IndicesAliasesRequest.AliasActions.Type.ADD)

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authz.interceptor;
import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.common.MemoizedSupplier;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.license.XPackLicenseState.Feature; import org.elasticsearch.license.XPackLicenseState.Feature;
@ -48,7 +49,8 @@ public final class ResizeRequestInterceptor implements RequestInterceptor {
final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState(); final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState();
final AuditTrail auditTrail = auditTrailService.get(); final AuditTrail auditTrail = auditTrailService.get();
if (frozenLicenseState.isSecurityEnabled()) { if (frozenLicenseState.isSecurityEnabled()) {
if (frozenLicenseState.checkFeature(Feature.SECURITY_DLS_FLS)) { MemoizedSupplier<Boolean> licenseChecker =
new MemoizedSupplier<>(() -> frozenLicenseState.checkFeature(Feature.SECURITY_DLS_FLS));
IndicesAccessControl indicesAccessControl = IndicesAccessControl indicesAccessControl =
threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY); threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
IndicesAccessControl.IndexAccessControl indexAccessControl = IndicesAccessControl.IndexAccessControl indexAccessControl =
@ -56,13 +58,12 @@ public final class ResizeRequestInterceptor implements RequestInterceptor {
if (indexAccessControl != null) { if (indexAccessControl != null) {
final boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity(); final boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
final boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions(); final boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
if (fls || dls) { if ((fls || dls) && licenseChecker.get()) {
listener.onFailure(new ElasticsearchSecurityException("Resize requests are not allowed for users when " + listener.onFailure(new ElasticsearchSecurityException("Resize requests are not allowed for users when " +
"field or document level security is enabled on the source index", RestStatus.BAD_REQUEST)); "field or document level security is enabled on the source index", RestStatus.BAD_REQUEST));
return; return;
} }
} }
}
authorizationEngine.validateIndexPermissionsAreSubset(requestInfo, authorizationInfo, authorizationEngine.validateIndexPermissionsAreSubset(requestInfo, authorizationInfo,
Collections.singletonMap(request.getSourceIndex(), Collections.singletonList(request.getTargetIndexRequest().index())), Collections.singletonMap(request.getSourceIndex(), Collections.singletonList(request.getTargetIndexRequest().index())),

View File

@ -168,12 +168,14 @@ public class CompositeRolesStore {
rolesRetrievalResult.getMissingRoles())); rolesRetrievalResult.getMissingRoles()));
} }
final Set<RoleDescriptor> effectiveDescriptors; final Set<RoleDescriptor> effectiveDescriptors;
if (licenseState.checkFeature(Feature.SECURITY_DLS_FLS)) { Set<RoleDescriptor> roleDescriptors = rolesRetrievalResult.getRoleDescriptors();
effectiveDescriptors = rolesRetrievalResult.getRoleDescriptors(); if (roleDescriptors.stream().anyMatch(RoleDescriptor::isUsingDocumentOrFieldLevelSecurity) &&
} else { licenseState.checkFeature(Feature.SECURITY_DLS_FLS) == false) {
effectiveDescriptors = rolesRetrievalResult.getRoleDescriptors().stream() effectiveDescriptors = roleDescriptors.stream()
.filter((rd) -> rd.isUsingDocumentOrFieldLevelSecurity() == false) .filter(r -> r.isUsingDocumentOrFieldLevelSecurity() == false)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} else {
effectiveDescriptors = roleDescriptors;
} }
logger.trace(() -> new ParameterizedMessage("Exposing effective role descriptors [{}] for role names [{}]", logger.trace(() -> new ParameterizedMessage("Exposing effective role descriptors [{}] for role names [{}]",
effectiveDescriptors, roleNames)); effectiveDescriptors, roleNames));

View File

@ -12,6 +12,7 @@ import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.MemoizedSupplier;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.util.set.Sets;
@ -176,14 +177,15 @@ public class FileRolesStore implements BiConsumer<Set<String>, ActionListener<Ro
if (Files.exists(path)) { if (Files.exists(path)) {
try { try {
List<String> roleSegments = roleSegments(path); List<String> roleSegments = roleSegments(path);
final boolean flsDlsLicensed = licenseState.checkFeature(Feature.SECURITY_DLS_FLS); MemoizedSupplier<Boolean> licenseChecker =
new MemoizedSupplier<>(() -> licenseState.checkFeature(Feature.SECURITY_DLS_FLS));
for (String segment : roleSegments) { for (String segment : roleSegments) {
RoleDescriptor descriptor = parseRoleDescriptor(segment, path, logger, resolvePermission, settings, xContentRegistry); RoleDescriptor descriptor = parseRoleDescriptor(segment, path, logger, resolvePermission, settings, xContentRegistry);
if (descriptor != null) { if (descriptor != null) {
if (ReservedRolesStore.isReserved(descriptor.getName())) { if (ReservedRolesStore.isReserved(descriptor.getName())) {
logger.warn("role [{}] is reserved. the relevant role definition in the mapping file will be ignored", logger.warn("role [{}] is reserved. the relevant role definition in the mapping file will be ignored",
descriptor.getName()); descriptor.getName());
} else if (flsDlsLicensed == false && descriptor.isUsingDocumentOrFieldLevelSecurity()) { } else if (descriptor.isUsingDocumentOrFieldLevelSecurity() && licenseChecker.get() == false) {
logger.warn("role [{}] uses document and/or field level security, which is not enabled by the current license" + logger.warn("role [{}] uses document and/or field level security, which is not enabled by the current license" +
". this role will be ignored", descriptor.getName()); ". this role will be ignored", descriptor.getName());
// we still put the role in the map to avoid unnecessary negative lookups // we still put the role in the map to avoid unnecessary negative lookups

View File

@ -201,9 +201,7 @@ public class NativeRolesStore implements BiConsumer<Set<String>, ActionListener<
} }
public void putRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) { public void putRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
if (licenseState.checkFeature(Feature.SECURITY_DLS_FLS)) { if (role.isUsingDocumentOrFieldLevelSecurity() && licenseState.checkFeature(Feature.SECURITY_DLS_FLS) == false) {
innerPutRole(request, role, listener);
} else if (role.isUsingDocumentOrFieldLevelSecurity()) {
listener.onFailure(LicenseUtils.newComplianceException("field and document level security")); listener.onFailure(LicenseUtils.newComplianceException("field and document level security"));
} else { } else {
innerPutRole(request, role, listener); innerPutRole(request, role, listener);
@ -382,14 +380,11 @@ public class NativeRolesStore implements BiConsumer<Set<String>, ActionListener<
// we pass true as last parameter because we do not want to reject permissions if the field permissions // we pass true as last parameter because we do not want to reject permissions if the field permissions
// are given in 2.x syntax // are given in 2.x syntax
RoleDescriptor roleDescriptor = RoleDescriptor.parse(name, sourceBytes, true, XContentType.JSON); RoleDescriptor roleDescriptor = RoleDescriptor.parse(name, sourceBytes, true, XContentType.JSON);
if (licenseState.checkFeature(Feature.SECURITY_DLS_FLS)) {
return roleDescriptor;
} else {
final boolean dlsEnabled = final boolean dlsEnabled =
Arrays.stream(roleDescriptor.getIndicesPrivileges()).anyMatch(IndicesPrivileges::isUsingDocumentLevelSecurity); Arrays.stream(roleDescriptor.getIndicesPrivileges()).anyMatch(IndicesPrivileges::isUsingDocumentLevelSecurity);
final boolean flsEnabled = final boolean flsEnabled =
Arrays.stream(roleDescriptor.getIndicesPrivileges()).anyMatch(IndicesPrivileges::isUsingFieldLevelSecurity); Arrays.stream(roleDescriptor.getIndicesPrivileges()).anyMatch(IndicesPrivileges::isUsingFieldLevelSecurity);
if (dlsEnabled || flsEnabled) { if ((dlsEnabled || flsEnabled) && licenseState.checkFeature(Feature.SECURITY_DLS_FLS) == false) {
List<String> unlicensedFeatures = new ArrayList<>(2); List<String> unlicensedFeatures = new ArrayList<>(2);
if (flsEnabled) { if (flsEnabled) {
unlicensedFeatures.add("fls"); unlicensedFeatures.add("fls");
@ -405,8 +400,6 @@ public class NativeRolesStore implements BiConsumer<Set<String>, ActionListener<
} else { } else {
return roleDescriptor; return roleDescriptor;
} }
}
} catch (Exception e) { } catch (Exception e) {
logger.error(new ParameterizedMessage("error in the format of data for role [{}]", name), e); logger.error(new ParameterizedMessage("error in the format of data for role [{}]", name), e);
return null; return null;

View File

@ -90,7 +90,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
arg0.updateLocalNodeInfo(localNode); arg0.updateLocalNodeInfo(localNode);
return null; return null;
}).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class));
apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), mock(Client.class), new XPackLicenseState(settings), apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), mock(Client.class), new XPackLicenseState(settings, () -> 0),
mock(SecurityIndexManager.class), clusterService, mock(ThreadPool.class)); mock(SecurityIndexManager.class), clusterService, mock(ThreadPool.class));
} }

View File

@ -220,7 +220,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
logger = CapturingLogger.newCapturingLogger(randomFrom(Level.OFF, Level.FATAL, Level.ERROR, Level.WARN, Level.INFO), patternLayout); logger = CapturingLogger.newCapturingLogger(randomFrom(Level.OFF, Level.FATAL, Level.ERROR, Level.WARN, Level.INFO), patternLayout);
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), client, new XPackLicenseState(settings), apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), client, new XPackLicenseState(settings, () -> 0),
securityIndexManager, clusterService, mock(ThreadPool.class)); securityIndexManager, clusterService, mock(ThreadPool.class));
} }

View File

@ -689,7 +689,7 @@ public class AuthorizationServiceTests extends ESTestCase {
final AnonymousUser anonymousUser = new AnonymousUser(settings); final AnonymousUser anonymousUser = new AnonymousUser(settings);
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrailService, authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrailService,
new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, null, Collections.emptySet(), new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, null, Collections.emptySet(),
new XPackLicenseState(settings), new IndexNameExpressionResolver()); new XPackLicenseState(settings, () -> 0), new IndexNameExpressionResolver());
RoleDescriptor role = new RoleDescriptor("a_all", null, RoleDescriptor role = new RoleDescriptor("a_all", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null); new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null);
@ -717,7 +717,7 @@ public class AuthorizationServiceTests extends ESTestCase {
final Authentication authentication = createAuthentication(new AnonymousUser(settings)); final Authentication authentication = createAuthentication(new AnonymousUser(settings));
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrailService, authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrailService,
new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, new AnonymousUser(settings), null, new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, new AnonymousUser(settings), null,
Collections.emptySet(), new XPackLicenseState(settings), new IndexNameExpressionResolver()); Collections.emptySet(), new XPackLicenseState(settings, () -> 0), new IndexNameExpressionResolver());
RoleDescriptor role = new RoleDescriptor("a_all", null, RoleDescriptor role = new RoleDescriptor("a_all", null,
new IndicesPrivileges[]{IndicesPrivileges.builder().indices("a").privileges("all").build()}, null); new IndicesPrivileges[]{IndicesPrivileges.builder().indices("a").privileges("all").build()}, null);

View File

@ -354,7 +354,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final DocumentSubsetBitsetCache documentSubsetBitsetCache = buildBitsetCache(); final DocumentSubsetBitsetCache documentSubsetBitsetCache = buildBitsetCache();
final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore,
reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(settings), reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(settings),
new XPackLicenseState(settings), cache, mock(ApiKeyService.class), documentSubsetBitsetCache, new XPackLicenseState(settings, () -> 0), cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor
@ -393,7 +393,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, mock(ApiKeyService.class), documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor
@ -480,7 +480,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, inMemoryProvider2), mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, inMemoryProvider2),
new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0),
cache, mock(ApiKeyService.class), documentSubsetBitsetCache, cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
@ -709,7 +709,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, failingProvider), mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, failingProvider),
new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0),
cache, mock(ApiKeyService.class), documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds)); cache, mock(ApiKeyService.class), documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds));
final Set<String> roleNames = Sets.newHashSet("roleA", "roleB", "unknown"); final Set<String> roleNames = Sets.newHashSet("roleA", "roleB", "unknown");
@ -821,7 +821,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
CompositeRolesStore compositeRolesStore = new CompositeRolesStore( CompositeRolesStore compositeRolesStore = new CompositeRolesStore(
Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(Settings.EMPTY), mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(Settings.EMPTY),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, mock(ApiKeyService.class), documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> {}) { rds -> {}) {
@Override @Override
public void invalidateAll() { public void invalidateAll() {
@ -875,7 +875,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS,
fileRolesStore, nativeRolesStore, reservedRolesStore, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, mock(ApiKeyService.class), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class),
documentSubsetBitsetCache, rds -> {}) { documentSubsetBitsetCache, rds -> {}) {
@Override @Override
public void invalidateAll() { public void invalidateAll() {
@ -971,7 +971,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, mock(ApiKeyService.class), documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor
@ -1012,7 +1012,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, mock(ApiKeyService.class), documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
@ -1035,7 +1035,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final ReservedRolesStore reservedRolesStore = spy(new ReservedRolesStore()); final ReservedRolesStore reservedRolesStore = spy(new ReservedRolesStore());
ThreadContext threadContext = new ThreadContext(SECURITY_ENABLED_SETTINGS); ThreadContext threadContext = new ThreadContext(SECURITY_ENABLED_SETTINGS);
ApiKeyService apiKeyService = spy(new ApiKeyService(SECURITY_ENABLED_SETTINGS, Clock.systemUTC(), mock(Client.class), ApiKeyService apiKeyService = spy(new ApiKeyService(SECURITY_ENABLED_SETTINGS, Clock.systemUTC(), mock(Client.class),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), mock(SecurityIndexManager.class), mock(ClusterService.class), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), mock(SecurityIndexManager.class), mock(ClusterService.class),
mock(ThreadPool.class))); mock(ThreadPool.class)));
NativePrivilegeStore nativePrivStore = mock(NativePrivilegeStore.class); NativePrivilegeStore nativePrivStore = mock(NativePrivilegeStore.class);
doAnswer(invocationOnMock -> { doAnswer(invocationOnMock -> {
@ -1050,7 +1050,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, apiKeyService, documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, apiKeyService, documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
AuditUtil.getOrGenerateRequestId(threadContext); AuditUtil.getOrGenerateRequestId(threadContext);
final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1)); final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1));
@ -1088,7 +1088,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
ThreadContext threadContext = new ThreadContext(SECURITY_ENABLED_SETTINGS); ThreadContext threadContext = new ThreadContext(SECURITY_ENABLED_SETTINGS);
ApiKeyService apiKeyService = spy(new ApiKeyService(SECURITY_ENABLED_SETTINGS, Clock.systemUTC(), mock(Client.class), ApiKeyService apiKeyService = spy(new ApiKeyService(SECURITY_ENABLED_SETTINGS, Clock.systemUTC(), mock(Client.class),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), mock(SecurityIndexManager.class), mock(ClusterService.class), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), mock(SecurityIndexManager.class), mock(ClusterService.class),
mock(ThreadPool.class))); mock(ThreadPool.class)));
NativePrivilegeStore nativePrivStore = mock(NativePrivilegeStore.class); NativePrivilegeStore nativePrivStore = mock(NativePrivilegeStore.class);
doAnswer(invocationOnMock -> { doAnswer(invocationOnMock -> {
@ -1103,7 +1103,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
final CompositeRolesStore compositeRolesStore = final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore,
nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache, apiKeyService, documentSubsetBitsetCache, new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, apiKeyService, documentSubsetBitsetCache,
rds -> effectiveRoleDescriptors.set(rds)); rds -> effectiveRoleDescriptors.set(rds));
AuditUtil.getOrGenerateRequestId(threadContext); AuditUtil.getOrGenerateRequestId(threadContext);
final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1)); final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1));
@ -1238,7 +1238,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
nativePrivStore, nativePrivStore,
Collections.emptyList(), Collections.emptyList(),
new ThreadContext(SECURITY_ENABLED_SETTINGS), new ThreadContext(SECURITY_ENABLED_SETTINGS),
new XPackLicenseState(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0),
cache, cache,
apiKeyService, apiKeyService,
documentSubsetBitsetCache, documentSubsetBitsetCache,
@ -1364,7 +1364,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
}).when(privilegeStore).getPrivileges(isA(Set.class), isA(Set.class), any(ActionListener.class)); }).when(privilegeStore).getPrivileges(isA(Set.class), isA(Set.class), any(ActionListener.class));
} }
if (licenseState == null) { if (licenseState == null) {
licenseState = new XPackLicenseState(settings); licenseState = new XPackLicenseState(settings, () -> 0);
} }
if (apiKeyService == null) { if (apiKeyService == null) {
apiKeyService = mock(ApiKeyService.class); apiKeyService = mock(ApiKeyService.class);

View File

@ -215,13 +215,13 @@ public class ServerTransportFilterTests extends ESTestCase {
Settings settings = Settings.builder().put("path.home", createTempDir()).build(); Settings settings = Settings.builder().put("path.home", createTempDir()).build();
ThreadContext threadContext = new ThreadContext(settings); ThreadContext threadContext = new ThreadContext(settings);
return new ServerTransportFilter.ClientProfile(authcService, authzService, threadContext, false, destructiveOperations, return new ServerTransportFilter.ClientProfile(authcService, authzService, threadContext, false, destructiveOperations,
reservedRealmEnabled, new SecurityContext(settings, threadContext), new XPackLicenseState(settings)); reservedRealmEnabled, new SecurityContext(settings, threadContext), new XPackLicenseState(settings, () -> 0));
} }
private ServerTransportFilter.NodeProfile getNodeFilter(boolean reservedRealmEnabled) throws IOException { private ServerTransportFilter.NodeProfile getNodeFilter(boolean reservedRealmEnabled) throws IOException {
Settings settings = Settings.builder().put("path.home", createTempDir()).build(); Settings settings = Settings.builder().put("path.home", createTempDir()).build();
ThreadContext threadContext = new ThreadContext(settings); ThreadContext threadContext = new ThreadContext(settings);
return new ServerTransportFilter.NodeProfile(authcService, authzService, threadContext, false, destructiveOperations, return new ServerTransportFilter.NodeProfile(authcService, authzService, threadContext, false, destructiveOperations,
reservedRealmEnabled, new SecurityContext(settings, threadContext), new XPackLicenseState(settings)); reservedRealmEnabled, new SecurityContext(settings, threadContext), new XPackLicenseState(settings, () -> 0));
} }
} }