diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyAction.java new file mode 100644 index 0000000000000..cd0de9ef0588d --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyAction.java @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.action; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionType; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.SimpleBatchedExecutor; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.MasterServiceTaskQueue; +import org.elasticsearch.common.Priority; +import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.Index; +import org.elasticsearch.injection.guice.Inject; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; + +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; + +public class SetIndexMetadataPropertyAction extends ActionType { + + public static final String NAME = "indices:internal/admin/metadata/custom/set"; + + private SetIndexMetadataPropertyAction() { + super(NAME); + } + + public static final SetIndexMetadataPropertyAction INSTANCE = new SetIndexMetadataPropertyAction(); + + public static class TransportAction extends TransportMasterNodeAction< + SetIndexMetadataPropertyRequest, + SetIndexMetadataPropertyResponse> { + private final MasterServiceTaskQueue< + SetIndexMetadataPropertyAction.TransportAction.SetIndexMetadataPropertyTask> setIndexMetadataPropertyTaskMasterServiceTaskQueue; + + @Inject + public TransportAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + SetIndexMetadataPropertyAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + SetIndexMetadataPropertyRequest::new, + indexNameExpressionResolver, + SetIndexMetadataPropertyResponse::new, + threadPool.executor(ThreadPool.Names.MANAGEMENT) + ); + this.setIndexMetadataPropertyTaskMasterServiceTaskQueue = clusterService.createTaskQueue( + "set-index-metadata-property-task-queue", + Priority.LOW, + SET_INDEX_METADATA_PROPERTY_TASK_VOID_SIMPLE_BATCHED_EXECUTOR + ); + } + + private static final SimpleBatchedExecutor< + SetIndexMetadataPropertyAction.TransportAction.SetIndexMetadataPropertyTask, + Map> SET_INDEX_METADATA_PROPERTY_TASK_VOID_SIMPLE_BATCHED_EXECUTOR = new SimpleBatchedExecutor<>() { + @Override + public Tuple> executeTask( + SetIndexMetadataPropertyAction.TransportAction.SetIndexMetadataPropertyTask task, + ClusterState clusterState + ) { + return task.execute(clusterState); + } + + @Override + public void taskSucceeded( + SetIndexMetadataPropertyAction.TransportAction.SetIndexMetadataPropertyTask task, + Map value + ) { + task.success(value); + } + }; + + static class SetIndexMetadataPropertyTask implements ClusterStateTaskListener { + private final ActionListener listener; + private final Index index; + private final String key; + @Nullable + private final Map expected; + @Nullable + private final Map value; + + SetIndexMetadataPropertyTask( + ActionListener listener, + Index index, + String key, + @Nullable Map expected, + @Nullable Map value + ) { + this.listener = listener; + this.index = index; + this.key = key; + this.expected = expected; + this.value = value; + } + + Tuple> execute(ClusterState state) { + IndexMetadata indexMetadata = state.metadata().getIndexSafe(index); + Map existingValue = indexMetadata.getCustomData(key); + if (Objects.equals(expected, existingValue)) { + IndexMetadata.Builder indexMetadataBuilder = IndexMetadata.builder(indexMetadata); + if (value != null) { + indexMetadataBuilder.putCustom(key, value); + } else { + indexMetadataBuilder.removeCustom(key); + } + indexMetadataBuilder.version(indexMetadataBuilder.version() + 1); + ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder(state.metadata().indices()); + builder.put(index.getName(), indexMetadataBuilder.build()); + return new Tuple<>( + ClusterState.builder(state).metadata(Metadata.builder(state.metadata()).indices(builder.build()).build()).build(), + value + ); + } else { + // returns existing value when expectation is not met + return new Tuple<>(state, existingValue); + } + } + + void success(Map value) { + listener.onResponse(new SetIndexMetadataPropertyResponse(value)); + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + } + + @Override + protected void masterOperation( + Task task, + SetIndexMetadataPropertyRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request); + if (concreteIndices.length != 1) { + listener.onFailure( + new ElasticsearchException("Exactly one concrete index expected, but resolved " + Arrays.toString(concreteIndices)) + ); + return; + } + IndexMetadata indexMetadata = state.metadata().getIndexSafe(concreteIndices[0]); + Map existingValue = indexMetadata.getCustomData(request.key()); + if (Objects.equals(request.expected(), existingValue)) { + setIndexMetadataPropertyTaskMasterServiceTaskQueue.submitTask( + "Set index metadata custom value", + new SetIndexMetadataPropertyAction.TransportAction.SetIndexMetadataPropertyTask( + listener, + concreteIndices[0], + request.key(), + request.expected(), + request.value() + ), + null + ); + } else { + // returns existing value when expectation is not met + listener.onResponse(new SetIndexMetadataPropertyResponse(existingValue)); + } + } + + @Override + protected ClusterBlockException checkBlock(SetIndexMetadataPropertyRequest request, ClusterState state) { + return state.blocks() + .indicesBlockedException(ClusterBlockLevel.METADATA_WRITE, indexNameExpressionResolver.concreteIndexNames(state, request)); + } + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyRequest.java new file mode 100644 index 0000000000000..633503c5df85e --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyRequest.java @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.action; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.IndicesRequest; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.action.support.master.MasterNodeRequest; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.TimeValue; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; + +public class SetIndexMetadataPropertyRequest extends MasterNodeRequest implements IndicesRequest { + private final String index; + private final String key; + @Nullable + private final Map expected; + @Nullable + private final Map value; + + public SetIndexMetadataPropertyRequest( + TimeValue timeout, + String index, + String key, + @Nullable Map expected, + @Nullable Map value + ) { + super(timeout); + this.index = Objects.requireNonNull(index); + this.key = Objects.requireNonNull(key); + this.expected = expected; + this.value = value; + } + + protected SetIndexMetadataPropertyRequest(StreamInput in) throws IOException { + super(in); + this.index = in.readString(); + this.key = in.readString(); + this.expected = readOptionalStringMap(in); + this.value = readOptionalStringMap(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(index); + out.writeString(key); + writeOptionalStringMap(expected, out); + writeOptionalStringMap(value, out); + } + + static void writeOptionalStringMap(@Nullable Map map, StreamOutput out) throws IOException { + if (map == null) { + out.writeBoolean(false); + } else { + out.writeBoolean(true); + out.writeMap(map, StreamOutput::writeString); + } + } + + static Map readOptionalStringMap(StreamInput in) throws IOException { + if (in.readBoolean()) { + return in.readImmutableMap(StreamInput::readString); + } else { + return null; + } + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public String[] indices() { + return new String[] { index }; + } + + @Override + public IndicesOptions indicesOptions() { + return IndicesOptions.strictSingleIndexNoExpandForbidClosed(); + } + + public String index() { + return index; + } + + public String key() { + return key; + } + + public @Nullable Map expected() { + return expected; + } + + public @Nullable Map value() { + return value; + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyResponse.java new file mode 100644 index 0000000000000..657d08053221a --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/SetIndexMetadataPropertyResponse.java @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.action; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.Nullable; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.xpack.core.security.action.SetIndexMetadataPropertyRequest.readOptionalStringMap; +import static org.elasticsearch.xpack.core.security.action.SetIndexMetadataPropertyRequest.writeOptionalStringMap; + +public class SetIndexMetadataPropertyResponse extends ActionResponse { + @Nullable + private final Map value; + + public SetIndexMetadataPropertyResponse(@Nullable Map value) { + this.value = value; + } + + public SetIndexMetadataPropertyResponse(StreamInput in) throws IOException { + value = readOptionalStringMap(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + writeOptionalStringMap(value, out); + } + + public @Nullable Map value() { + return value; + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java index 89733761f3dc0..c6c7895b7d003 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java @@ -479,7 +479,7 @@ static RoleDescriptor kibanaSystem(String name) { new ConfigurableClusterPrivileges.ManageApplicationPrivileges(Set.of("kibana-*")), new ConfigurableClusterPrivileges.WriteProfileDataPrivileges(Set.of("kibana*")) }, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, new RoleDescriptor.RemoteIndicesPrivileges[] { getRemoteIndicesReadPrivileges(".monitoring-*"), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index 2380c13e147d5..029eb4f012637 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.core.Tuple; import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.ILMActions; import org.elasticsearch.xpack.core.monitoring.action.MonitoringBulkAction; @@ -39,6 +40,7 @@ import java.util.stream.Collectors; import static java.util.Map.entry; +import static java.util.stream.Collectors.toUnmodifiableMap; public class ReservedRolesStore implements BiConsumer, ActionListener> { /** "Security Solutions" only legacy signals index */ @@ -80,7 +82,7 @@ public class ReservedRolesStore implements BiConsumer, ActionListene RoleDescriptor.ApplicationResourcePrivileges.builder().application("*").privileges("*").resources("*").build() }, null, new String[] { "*" }, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, Collections.emptyMap(), new RoleDescriptor.RemoteIndicesPrivileges[] { new RoleDescriptor.RemoteIndicesPrivileges( @@ -139,7 +141,7 @@ public ReservedRolesStore(Set includes) { RESERVED_ROLES = ALL_RESERVED_ROLES.entrySet() .stream() .filter(entry -> includes.contains(entry.getKey())) - .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + .collect(toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); } static RoleDescriptor.RemoteIndicesPrivileges getRemoteIndicesReadPrivileges(String indexPattern) { @@ -165,7 +167,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -176,10 +178,13 @@ private static Map initializeReservedRoles() { + "Assign your users this role if they use the Transport Client." ) ), - entry("kibana_admin", kibanaAdminUser("kibana_admin", MetadataUtils.DEFAULT_RESERVED_METADATA)), + entry("kibana_admin", kibanaAdminUser("kibana_admin", MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA)), entry( "kibana_user", - kibanaAdminUser("kibana_user", MetadataUtils.getDeprecatedReservedMetadata("Please use the [kibana_admin] role instead")) + kibanaAdminUser( + "kibana_user", + MetadataUtils.getDeprecatedReservedRoleMetadata("Please use the [kibana_admin] role instead") + ) ), entry( "monitoring_user", @@ -207,7 +212,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, new RoleDescriptor.RemoteIndicesPrivileges[] { getRemoteIndicesReadPrivileges(".monitoring-*"), @@ -251,7 +256,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -276,7 +281,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -293,7 +298,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -311,7 +316,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.getDeprecatedReservedMetadata("Please use Kibana feature privileges instead"), + MetadataUtils.getDeprecatedReservedRoleMetadata("Please use Kibana feature privileges instead"), null, null, null, @@ -332,7 +337,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -351,7 +356,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -372,7 +377,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -394,7 +399,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -452,7 +457,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.getDeprecatedReservedMetadata( + MetadataUtils.getDeprecatedReservedRoleMetadata( "This role will be removed in a future major release. Please use editor and viewer roles instead" ), null, @@ -472,7 +477,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -489,7 +494,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -526,7 +531,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -568,7 +573,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -603,7 +608,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.getDeprecatedReservedMetadata("Please use the [transform_admin] role instead"), + MetadataUtils.getDeprecatedReservedRoleMetadata("Please use the [transform_admin] role instead"), null, null, null, @@ -635,7 +640,7 @@ private static Map initializeReservedRoles() { .build() }, null, null, - MetadataUtils.getDeprecatedReservedMetadata("Please use the [transform_user] role instead"), + MetadataUtils.getDeprecatedReservedRoleMetadata("Please use the [transform_user] role instead"), null, null, null, @@ -661,7 +666,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -687,7 +692,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -710,7 +715,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -738,7 +743,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -760,7 +765,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -778,7 +783,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -795,7 +800,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -817,7 +822,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -843,7 +848,7 @@ private static Map initializeReservedRoles() { null, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -894,7 +899,7 @@ private static RoleDescriptor buildViewerRoleDescriptor() { .build() }, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -949,7 +954,7 @@ private static RoleDescriptor buildEditorRoleDescriptor() { .build() }, null, null, - MetadataUtils.DEFAULT_RESERVED_METADATA, + MetadataUtils.DEFAULT_RESERVED_ROLE_METADATA, null, null, null, @@ -985,6 +990,18 @@ public static Collection roleDescriptors() { return RESERVED_ROLES.values(); } + public static Map versionMap() { + return ReservedRolesStore.roleDescriptors() + .stream() + .map( + roleDescriptor -> new Tuple<>( + roleDescriptor.getName(), + (String) roleDescriptor.getMetadata().get(MetadataUtils.RESERVED_ROLE_VERSION_METADATA_KEY) + ) + ) + .collect(toUnmodifiableMap(Tuple::v1, Tuple::v2)); + } + public static Set names() { if (RESERVED_ROLES == null) { throw new IllegalStateException("ReserveRolesStore is not initialized properly"); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MetadataUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MetadataUtils.java index 41ebac766cabc..736ed11871b2c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MetadataUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MetadataUtils.java @@ -11,10 +11,21 @@ public class MetadataUtils { public static final String RESERVED_PREFIX = "_"; + public static final String RESERVED_ROLE_VERSION_METADATA_KEY = RESERVED_PREFIX + "version"; public static final String RESERVED_METADATA_KEY = RESERVED_PREFIX + "reserved"; public static final String DEPRECATED_METADATA_KEY = RESERVED_PREFIX + "deprecated"; public static final String DEPRECATED_REASON_METADATA_KEY = RESERVED_PREFIX + "deprecated_reason"; + /* !!!!!! IMPORTANT !!!!!!! + * reserved role version must be incremented when builtin reserved roles are changed + */ + public static final String RESERVED_ROLE_VERSION_VALUE = "2"; public static final Map DEFAULT_RESERVED_METADATA = Map.of(RESERVED_METADATA_KEY, true); + public static final Map DEFAULT_RESERVED_ROLE_METADATA = Map.of( + RESERVED_METADATA_KEY, + true, + RESERVED_ROLE_VERSION_METADATA_KEY, + RESERVED_ROLE_VERSION_VALUE + ); private MetadataUtils() {} @@ -30,4 +41,17 @@ public static boolean containsReservedMetadata(Map metadata) { public static Map getDeprecatedReservedMetadata(String reason) { return Map.of(RESERVED_METADATA_KEY, true, DEPRECATED_METADATA_KEY, true, DEPRECATED_REASON_METADATA_KEY, reason); } + + public static Map getDeprecatedReservedRoleMetadata(String reason) { + return Map.of( + RESERVED_METADATA_KEY, + true, + DEPRECATED_METADATA_KEY, + true, + DEPRECATED_REASON_METADATA_KEY, + reason, + RESERVED_ROLE_VERSION_METADATA_KEY, + RESERVED_ROLE_VERSION_VALUE + ); + } } diff --git a/x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/QueryRoleIT.java b/x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/QueryRoleIT.java index 1588749b9a331..4d84d1623e695 100644 --- a/x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/QueryRoleIT.java +++ b/x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/QueryRoleIT.java @@ -50,14 +50,16 @@ public final class QueryRoleIT extends SecurityInBasicRestTestCase { private static final String READ_SECURITY_USER_AUTH_HEADER = "Basic cmVhZF9zZWN1cml0eV91c2VyOnJlYWQtc2VjdXJpdHktcGFzc3dvcmQ="; public void testSimpleQueryAllRoles() throws IOException { + // the index does not exist assertQuery("", 0, roles -> assertThat(roles, emptyIterable())); - RoleDescriptor createdRole = createRandomRole(); - assertQuery("", 1, roles -> { - assertThat(roles, iterableWithSize(1)); - assertRoleMap(roles.get(0), createdRole); + createRandomRole(); + // 32 built-in reserved roles + assertQuery("", 1 + 32, roles -> { + // default size is 10 + assertThat(roles, iterableWithSize(10)); }); assertQuery(""" - {"query":{"match_all":{}},"from":1}""", 1, roles -> assertThat(roles, emptyIterable())); + {"query":{"match_all":{}},"from":33}""", 1 + 32, roles -> assertThat(roles, emptyIterable())); } public void testDisallowedFields() throws Exception { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 79a00fa1293bd..6861acdd359ac 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -133,6 +133,7 @@ import org.elasticsearch.xpack.core.security.action.ActionTypes; import org.elasticsearch.xpack.core.security.action.ClearSecurityCacheAction; import org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationAction; +import org.elasticsearch.xpack.core.security.action.SetIndexMetadataPropertyAction; import org.elasticsearch.xpack.core.security.action.UpdateIndexMigrationVersionAction; import org.elasticsearch.xpack.core.security.action.apikey.BulkUpdateApiKeyAction; import org.elasticsearch.xpack.core.security.action.apikey.BulkUpdateApiKeyRequestTranslator; @@ -786,6 +787,8 @@ Collection createComponents( // See Plugin#additionalSettings() this.settings = environment.settings(); + final ReservedRolesStore reservedRolesStore = new ReservedRolesStore(Set.copyOf(INCLUDED_RESERVED_ROLES_SETTING.get(settings))); + systemIndices.init(client, featureService, clusterService); this.securityMigrationExecutor.set( @@ -919,7 +922,6 @@ Collection createComponents( ); components.add(privilegeStore); - final ReservedRolesStore reservedRolesStore = new ReservedRolesStore(Set.copyOf(INCLUDED_RESERVED_ROLES_SETTING.get(settings))); dlsBitsetCache.set(new DocumentSubsetBitsetCache(settings, threadPool)); final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(settings); @@ -1625,6 +1627,7 @@ public void onIndexModule(IndexModule module) { new ActionHandler<>(UpdateSecuritySettingsAction.INSTANCE, TransportUpdateSecuritySettingsAction.class), new ActionHandler<>(ActionTypes.RELOAD_REMOTE_CLUSTER_CREDENTIALS_ACTION, TransportReloadRemoteClusterCredentialsAction.class), new ActionHandler<>(UpdateIndexMigrationVersionAction.INSTANCE, UpdateIndexMigrationVersionAction.TransportAction.class), + new ActionHandler<>(SetIndexMetadataPropertyAction.INSTANCE, SetIndexMetadataPropertyAction.TransportAction.class), usageAction, infoAction ).filter(Objects::nonNull).toList(); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java index 9ddda193dba39..bee5740d4199f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java @@ -9,6 +9,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequestValidationException; @@ -36,12 +37,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Nullable; import org.elasticsearch.features.FeatureService; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -61,15 +64,16 @@ import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.core.security.authz.permission.RemoteClusterPermissions; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges; +import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.authz.support.DLSRoleQueryValidator; -import org.elasticsearch.xpack.core.security.support.NativeRealmValidationUtil; import org.elasticsearch.xpack.security.authz.ReservedRoleNameChecker; import org.elasticsearch.xpack.security.support.SecurityIndexManager; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -168,6 +172,10 @@ public NativeRolesStore( this.enabled = settings.getAsBoolean(NATIVE_ROLES_ENABLED, true); } + public boolean isEnabled() { + return enabled; + } + @Override public void accept(Set names, ActionListener listener) { getRoleDescriptors(names, listener); @@ -262,6 +270,10 @@ public boolean isMetadataSearchable() { } public void queryRoleDescriptors(SearchSourceBuilder searchSourceBuilder, ActionListener listener) { + if (enabled == false) { + listener.onFailure(new IllegalStateException("Native role management is disabled")); + return; + } SearchRequest searchRequest = new SearchRequest(new String[] { SECURITY_MAIN_ALIAS }, searchSourceBuilder); SecurityIndexManager frozenSecurityIndex = securityIndex.defensiveCopy(); if (frozenSecurityIndex.indexExists() == false) { @@ -344,6 +356,16 @@ public void deleteRoles( final List roleNames, WriteRequest.RefreshPolicy refreshPolicy, final ActionListener listener + ) { + deleteRoles(null, roleNames, refreshPolicy, true, listener); + } + + private void deleteRoles( + SecurityIndexManager securityIndexManager, + final Collection roleNames, + WriteRequest.RefreshPolicy refreshPolicy, + boolean validateRoles, + final ActionListener listener ) { if (enabled == false) { listener.onFailure(new IllegalStateException("Native role management is disabled")); @@ -354,7 +376,7 @@ public void deleteRoles( Map validationErrorByRoleName = new HashMap<>(); for (String roleName : roleNames) { - if (reservedRoleNameChecker.isReserved(roleName)) { + if (validateRoles && reservedRoleNameChecker.isReserved(roleName)) { validationErrorByRoleName.put( roleName, new IllegalArgumentException("role [" + roleName + "] is reserved and cannot be deleted") @@ -369,7 +391,9 @@ public void deleteRoles( return; } - final SecurityIndexManager frozenSecurityIndex = securityIndex.defensiveCopy(); + final SecurityIndexManager frozenSecurityIndex = securityIndexManager != null + ? securityIndexManager + : securityIndex.defensiveCopy(); if (frozenSecurityIndex.indexExists() == false) { logger.debug("security index does not exist"); listener.onResponse(new BulkRolesResponse(List.of())); @@ -401,7 +425,7 @@ public void onFailure(Exception e) { } private void bulkResponseAndRefreshRolesCache( - List roleNames, + Collection roleNames, BulkResponse bulkResponse, Map validationErrorByRoleName, ActionListener listener @@ -429,7 +453,7 @@ private void bulkResponseAndRefreshRolesCache( } private void bulkResponseWithOnlyValidationErrors( - List roleNames, + Collection roleNames, Map validationErrorByRoleName, ActionListener listener ) { @@ -538,9 +562,90 @@ public void onFailure(Exception e) { } } + public void ensureBuiltinRolesAreQueryable(ActionListener listener) { + if (enabled == false) { + listener.onFailure(new IllegalStateException("Native role management is disabled")); + return; + } + securityIndex.prepareIndexIfNeededThenExecute(listener::onFailure, () -> { + final SecurityIndexManager frozenSecurityIndex = securityIndex.defensiveCopy(); + if (frozenSecurityIndex.isAvailable(SEARCH_SHARDS) == false) { + listener.onFailure(frozenSecurityIndex.getUnavailableReason(SEARCH_SHARDS)); + } else if (frozenSecurityIndex.areReservedRolesIndexed()) { + logger.debug("security index already contains the latest reserved roles indexed"); + listener.onResponse(null); + } else { + // we will first create new or update the existing roles in .security index + // then potentially delete the roles from .security index that have been removed from the builtin roles + indexReservedRoles(frozenSecurityIndex, ActionListener.wrap(onResponse -> { + Map indexedRolesVersions = frozenSecurityIndex.getReservedRolesVersionMap() != null + ? frozenSecurityIndex.getReservedRolesVersionMap() + : Map.of(); + Set rolesToDelete = Sets.difference(indexedRolesVersions.keySet(), ReservedRolesStore.names()); + if (false == rolesToDelete.isEmpty()) { + deleteRoles( + frozenSecurityIndex, + rolesToDelete, + WriteRequest.RefreshPolicy.IMMEDIATE, + false, + ActionListener.wrap(deleteResponse -> { + if (deleteResponse.getItems().stream().anyMatch(BulkRolesResponse.Item::isFailed)) { + listener.onFailure( + new ElasticsearchStatusException( + "Automatic deletion of builtin reserved roles failed", + RestStatus.INTERNAL_SERVER_ERROR + ) + ); + } else { + frozenSecurityIndex.markReservedRolesAsIndexed(listener); + } + + }, listener::onFailure) + ); + } else { + frozenSecurityIndex.markReservedRolesAsIndexed(listener); + } + }, listener::onFailure)); + } + }); + + } + public void putRoles( final WriteRequest.RefreshPolicy refreshPolicy, - final List roles, + final Collection roles, + final ActionListener listener + ) { + putRoles(securityIndex, refreshPolicy, roles, true, listener); + } + + private void indexReservedRoles(SecurityIndexManager frozenSecurityIndex, ActionListener listener) { + putRoles( + frozenSecurityIndex, + WriteRequest.RefreshPolicy.IMMEDIATE, + ReservedRolesStore.roleDescriptors(), + false, + ActionListener.wrap(onResponse -> { + if (onResponse.getItems().stream().anyMatch(BulkRolesResponse.Item::isFailed)) { + logger.warn("Automatic indexing of builtin reserved roles failed, {}", onResponse); + listener.onFailure( + new ElasticsearchStatusException( + "Automatic indexing of builtin reserved roles failed", + RestStatus.INTERNAL_SERVER_ERROR + ) + ); + } else { + listener.onResponse(null); + } + }, listener::onFailure) + ); + } + + private void putRoles( + SecurityIndexManager securityIndexManager, + final WriteRequest.RefreshPolicy refreshPolicy, + final Collection roles, + boolean validateRoles, final ActionListener listener ) { if (enabled == false) { @@ -553,7 +658,7 @@ public void putRoles( for (RoleDescriptor role : roles) { Exception validationException; try { - validationException = validateRoleDescriptor(role); + validationException = validateRoles ? validateRoleDescriptor(role) : null; } catch (Exception e) { validationException = e; } @@ -576,7 +681,7 @@ public void putRoles( return; } - securityIndex.prepareIndexIfNeededThenExecute( + securityIndexManager.prepareIndexIfNeededThenExecute( listener::onFailure, () -> executeAsyncWithOrigin( client.threadPool().getThreadContext(), @@ -619,8 +724,6 @@ private DeleteRequest createRoleDeleteRequest(final String roleName) { // Package private for testing XContentBuilder createRoleXContentBuilder(RoleDescriptor role) throws IOException { - assert NativeRealmValidationUtil.validateRoleName(role.getName(), false) == null - : "Role name was invalid or reserved: " + role.getName(); assert false == role.hasRestriction() : "restriction is not supported for native roles"; XContentBuilder builder = jsonBuilder().startObject(); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityIndexManager.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityIndexManager.java index 6d9b0ef6aeebe..cdc065e82d091 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityIndexManager.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/SecurityIndexManager.java @@ -46,6 +46,9 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.Scheduler; import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.core.security.action.SetIndexMetadataPropertyAction; +import org.elasticsearch.xpack.core.security.action.SetIndexMetadataPropertyRequest; +import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.security.SecurityFeatures; import java.time.Instant; @@ -62,10 +65,12 @@ import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_FORMAT_SETTING; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_INDEX_VERSION_CREATED; import static org.elasticsearch.indices.SystemIndexDescriptor.VERSION_META_KEY; +import static org.elasticsearch.xpack.core.ClientHelper.SECURITY_ORIGIN; import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; import static org.elasticsearch.xpack.core.security.action.UpdateIndexMigrationVersionAction.MIGRATION_VERSION_CUSTOM_DATA_KEY; import static org.elasticsearch.xpack.core.security.action.UpdateIndexMigrationVersionAction.MIGRATION_VERSION_CUSTOM_KEY; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.State.UNRECOVERED_STATE; +import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SECURITY_MIGRATION_FRAMEWORK; /** @@ -75,6 +80,7 @@ public class SecurityIndexManager implements ClusterStateListener { public static final String SECURITY_VERSION_STRING = "security-version"; + private static final String METADATA_PROPERTY_FOR_INDEXED_RESERVED_ROLES = "indexed-reserved-roles"; private static final Logger logger = LogManager.getLogger(SecurityIndexManager.class); @@ -133,7 +139,7 @@ private SecurityIndexManager( * should be reused for multiple checks in the same workflow. */ public SecurityIndexManager defensiveCopy() { - return new SecurityIndexManager(null, null, systemIndexDescriptor, state, true); + return new SecurityIndexManager(null, client, systemIndexDescriptor, state, true); } public String aliasName() { @@ -294,18 +300,22 @@ public void clusterChanged(ClusterChangedEvent event) { : indexMetadata.getIndex().getName(); final ClusterHealthStatus indexHealth; final IndexMetadata.State indexState; + final Map indexedReservedRolesVersionMap; if (indexMetadata == null) { // Index does not exist indexState = null; indexHealth = null; + indexedReservedRolesVersionMap = null; } else if (indexMetadata.getState() == IndexMetadata.State.CLOSE) { indexState = IndexMetadata.State.CLOSE; indexHealth = null; + indexedReservedRolesVersionMap = null; logger.warn("Index [{}] is closed. This is likely to prevent security from functioning correctly", concreteIndexName); } else { indexState = IndexMetadata.State.OPEN; final IndexRoutingTable routingTable = event.state().getRoutingTable().index(indexMetadata.getIndex()); indexHealth = new ClusterIndexHealth(indexMetadata, routingTable).getStatus(); + indexedReservedRolesVersionMap = indexMetadata.getCustomData(METADATA_PROPERTY_FOR_INDEXED_RESERVED_ROLES); } final String indexUUID = indexMetadata != null ? indexMetadata.getIndexUUID() : null; final State newState = new State( @@ -324,7 +334,8 @@ public void clusterChanged(ClusterChangedEvent event) { indexUUID, allSecurityFeatures.stream() .filter(feature -> featureService.clusterHasFeature(event.state(), feature)) - .collect(Collectors.toSet()) + .collect(Collectors.toSet()), + indexedReservedRolesVersionMap ); this.state = newState; @@ -567,6 +578,52 @@ public String getConcreteIndexName() { return state.concreteIndexName; } + public boolean areReservedRolesIndexed() { + return areReservedRolesIndexed(state.indexedReservedRolesVersionMap); + } + + public Map getReservedRolesVersionMap() { + return state.indexedReservedRolesVersionMap; + } + + private static boolean areReservedRolesIndexed(Map reservedRolesVersionMap) { + if (reservedRolesVersionMap == null) { + return false; + } + Map reservedRolesVersions = ReservedRolesStore.versionMap(); + if (reservedRolesVersionMap.keySet().equals(reservedRolesVersions.keySet()) == false) { + return false; + } + for (String roleName : reservedRolesVersions.keySet()) { + if (Integer.parseInt(reservedRolesVersions.get(roleName)) > Integer.parseInt(reservedRolesVersionMap.get(roleName))) { + return false; + } + } + return true; + } + + public void markReservedRolesAsIndexed(ActionListener listener) { + executeAsyncWithOrigin( + client, + SECURITY_ORIGIN, + SetIndexMetadataPropertyAction.INSTANCE, + new SetIndexMetadataPropertyRequest( + TimeValue.MINUS_ONE, + SECURITY_MAIN_ALIAS, + METADATA_PROPERTY_FOR_INDEXED_RESERVED_ROLES, + state.indexedReservedRolesVersionMap, + ReservedRolesStore.versionMap() + ), + ActionListener.wrap(response -> { + if (areReservedRolesIndexed(response.value()) == false) { + listener.onFailure(new IllegalStateException("Failed to mark reserved roles as indexed")); + } else { + listener.onResponse(null); + } + }, listener::onFailure) + ); + } + /** * Prepares the index by creating it if it doesn't exist, then executes the runnable. * @param consumer a handler for any exceptions that are raised either during preparation or execution @@ -714,7 +771,8 @@ public static class State { null, null, null, - Set.of() + Set.of(), + null ); public final Instant creationTime; public final boolean isIndexUpToDate; @@ -732,6 +790,7 @@ public static class State { public final IndexMetadata.State indexState; public final String indexUUID; public final Set securityFeatures; + private final Map indexedReservedRolesVersionMap; public State( Instant creationTime, @@ -747,7 +806,8 @@ public State( ClusterHealthStatus indexHealth, IndexMetadata.State indexState, String indexUUID, - Set securityFeatures + Set securityFeatures, + Map indexedReservedRolesVersionMap ) { this.creationTime = creationTime; this.isIndexUpToDate = isIndexUpToDate; @@ -763,6 +823,7 @@ public State( this.indexState = indexState; this.indexUUID = indexUUID; this.securityFeatures = securityFeatures; + this.indexedReservedRolesVersionMap = indexedReservedRolesVersionMap; } @Override @@ -782,7 +843,8 @@ public boolean equals(Object o) { && Objects.equals(concreteIndexName, state.concreteIndexName) && indexHealth == state.indexHealth && indexState == state.indexState - && Objects.equals(securityFeatures, state.securityFeatures); + && Objects.equals(securityFeatures, state.securityFeatures) + && Objects.equals(indexedReservedRolesVersionMap, state.indexedReservedRolesVersionMap); } public boolean indexExists() { @@ -803,7 +865,8 @@ public int hashCode() { indexMappingVersion, concreteIndexName, indexHealth, - securityFeatures + securityFeatures, + indexedReservedRolesVersionMap ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java index e1c3b936e5a32..86a308eaf7861 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java @@ -2522,7 +2522,8 @@ private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { indexStatus, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java index 2254c78a2910c..225e7e32ddc9e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java @@ -47,7 +47,8 @@ private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) { indexStatus, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java index 2a084bacfaf76..a1a619eab6dea 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java @@ -419,7 +419,8 @@ private SecurityIndexManager.State indexState(boolean isUpToDate, ClusterHealthS healthStatus, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 9587533d87d86..03c57338b0e70 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -1706,7 +1706,8 @@ public SecurityIndexManager.State dummyIndexState(boolean isIndexUpToDate, Clust healthStatus, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java index f91cb567ba689..de0b761f764f4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java @@ -908,7 +908,8 @@ private SecurityIndexManager.State dummyState( healthStatus, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/support/CacheInvalidatorRegistryTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/support/CacheInvalidatorRegistryTests.java index e3b00dfbcc6b8..d2b583893dc81 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/support/CacheInvalidatorRegistryTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/support/CacheInvalidatorRegistryTests.java @@ -69,7 +69,8 @@ public void testSecurityIndexStateChangeWillInvalidateAllRegisteredInvalidators( ClusterHealthStatus.GREEN, IndexMetadata.State.OPEN, "my_uuid", - Set.of() + Set.of(), + null ); cacheInvalidatorRegistry.onSecurityIndexStateChange(previousState, currentState);