diff --git a/build-tools-internal/src/main/groovy/elasticsearch.run-ccs.gradle b/build-tools-internal/src/main/groovy/elasticsearch.run-ccs.gradle
index 587c97d3476ea..07abe9a8e7633 100644
--- a/build-tools-internal/src/main/groovy/elasticsearch.run-ccs.gradle
+++ b/build-tools-internal/src/main/groovy/elasticsearch.run-ccs.gradle
@@ -48,6 +48,7 @@ tasks.register("run-ccs", RunTask) {
useCluster queryingCluster
doFirst {
queryingCluster.get().getNodes().each { node ->
+ node.setting('cluster.remote.my_remote_cluster.tags', 'env-dev')
if (proxyMode) {
node.setting('cluster.remote.my_remote_cluster.mode', 'proxy')
if (basicSecurityMode) {
diff --git a/server/src/main/java/org/elasticsearch/FlatIndicesRequest.java b/server/src/main/java/org/elasticsearch/FlatIndicesRequest.java
new file mode 100644
index 0000000000000..a3ca1528bd7e1
--- /dev/null
+++ b/server/src/main/java/org/elasticsearch/FlatIndicesRequest.java
@@ -0,0 +1,25 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch;
+
+import org.elasticsearch.action.IndicesRequest;
+import org.elasticsearch.transport.RemoteClusterService;
+
+import java.util.List;
+
+public interface FlatIndicesRequest extends IndicesRequest {
+ boolean requiresRewrite();
+
+ void indexExpressions(List indexExpressions);
+
+ boolean checkRemote(List tags);
+
+ record IndexExpression(String original, List rewritten) {}
+}
diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java
index 2e24858d9781f..ec04bb9bd67c5 100644
--- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java
@@ -9,6 +9,7 @@
package org.elasticsearch.action.fieldcaps;
+import org.elasticsearch.FlatIndicesRequest;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest;
@@ -18,11 +19,13 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.tasks.CancellableTask;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.transport.RemoteClusterAware;
+import org.elasticsearch.transport.RemoteClusterService;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;
@@ -30,11 +33,16 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-public final class FieldCapabilitiesRequest extends LegacyActionRequest implements IndicesRequest.Replaceable, ToXContentObject {
+public final class FieldCapabilitiesRequest extends LegacyActionRequest
+ implements
+ FlatIndicesRequest,
+ IndicesRequest.Replaceable,
+ ToXContentObject {
public static final String NAME = "field_caps_request";
public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpenAndForbidClosed();
@@ -52,6 +60,8 @@ public final class FieldCapabilitiesRequest extends LegacyActionRequest implemen
private QueryBuilder indexFilter;
private Map runtimeFields = Collections.emptyMap();
private Long nowInMillis;
+ @Nullable
+ private List indexExpressions;
public FieldCapabilitiesRequest(StreamInput in) throws IOException {
super(in);
@@ -323,4 +333,21 @@ public String getDescription() {
}
};
}
+
+ @Override
+ public boolean requiresRewrite() {
+ return indexExpressions == null;
+ }
+
+ @Override
+ public void indexExpressions(List indexExpressions) {
+ assert requiresRewrite();
+ this.indexExpressions = indexExpressions;
+ indices(indexExpressions.stream().flatMap(indexExpression -> indexExpression.rewritten().stream()).toArray(String[]::new));
+ }
+
+ @Override
+ public boolean checkRemote(List tags) {
+ return true;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java
index fda2df81d3f94..7d890cd71140e 100644
--- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java
@@ -9,6 +9,7 @@
package org.elasticsearch.action.search;
+import org.elasticsearch.FlatIndicesRequest;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionRequestValidationException;
@@ -31,6 +32,7 @@
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.tasks.TaskId;
+import org.elasticsearch.transport.RemoteClusterService;
import org.elasticsearch.xcontent.ToXContent;
import java.io.IOException;
@@ -53,7 +55,11 @@
* @see Client#search(SearchRequest)
* @see SearchResponse
*/
-public class SearchRequest extends LegacyActionRequest implements IndicesRequest.Replaceable, Rewriteable {
+public class SearchRequest extends LegacyActionRequest
+ implements
+ FlatIndicesRequest,
+ IndicesRequest.Replaceable,
+ Rewriteable {
public static final ToXContent.Params FORMAT_PARAMS = new ToXContent.MapParams(Collections.singletonMap("pretty", "false"));
@@ -69,6 +75,11 @@ public class SearchRequest extends LegacyActionRequest implements IndicesRequest
private SearchType searchType = SearchType.DEFAULT;
private String[] indices = Strings.EMPTY_ARRAY;
+ // This will be a more complex thing in the real implementation -- a lucene expression instead of just a list of literals
+ private List routingTags = List.of();
+
+ @Nullable
+ private List indexExpressions;
@Nullable
private String routing;
@@ -400,6 +411,11 @@ public SearchRequest indices(String... indices) {
return this;
}
+ public SearchRequest routingTags(List routingTags) {
+ this.routingTags = routingTags;
+ return this;
+ }
+
private static void validateIndices(String... indices) {
Objects.requireNonNull(indices, "indices must not be null");
for (String index : indices) {
@@ -853,4 +869,30 @@ public String toString() {
+ source
+ '}';
}
+
+ @Override
+ public boolean requiresRewrite() {
+ return indexExpressions == null;
+ }
+
+ @Override
+ public void indexExpressions(List indexExpressions) {
+ assert requiresRewrite();
+ this.indexExpressions = indexExpressions;
+ indices(indexExpressions.stream().flatMap(indexExpression -> indexExpression.rewritten().stream()).toArray(String[]::new));
+ }
+
+ @Override
+ public boolean checkRemote(List tags) {
+ if (routingTags.isEmpty()) {
+ return true; // no routing requested, so no constraints
+ }
+ // if any tag in routingTags matches one in tags, return true
+ for (RemoteClusterService.RemoteTag tag : routingTags) {
+ if (tags.contains(tag)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
index 1fbc8993cc5aa..f21f1231c76d0 100644
--- a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
+++ b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
@@ -367,6 +367,7 @@ public void apply(Settings value, Settings current, Settings previous) {
TransportSearchAction.SHARD_COUNT_LIMIT_SETTING,
TransportSearchAction.DEFAULT_PRE_FILTER_SHARD_SIZE,
RemoteClusterService.REMOTE_CLUSTER_SKIP_UNAVAILABLE,
+ RemoteClusterService.REMOTE_CLUSTER_TAGS,
SniffConnectionStrategy.REMOTE_CONNECTIONS_PER_CLUSTER,
RemoteClusterService.REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING,
RemoteClusterService.REMOTE_NODE_ATTRIBUTE,
@@ -483,6 +484,7 @@ public void apply(Settings value, Settings current, Settings previous) {
SearchService.ALLOW_EXPENSIVE_QUERIES,
SearchService.CCS_VERSION_CHECK_SETTING,
SearchService.CCS_COLLECT_TELEMETRY,
+ SearchService.FLAT_WORLD_ENABLED,
SearchService.BATCHED_QUERY_PHASE,
SearchService.PREWARMING_THRESHOLD_THREADPOOL_SIZE_FACTOR_POOL_SIZE,
MultiBucketConsumerService.MAX_BUCKET_SETTING,
diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java
index 5eda47bc32354..ee67432e6050f 100644
--- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java
+++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java
@@ -9,6 +9,8 @@
package org.elasticsearch.rest.action.search;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.search.SearchRequest;
@@ -35,6 +37,7 @@
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
+import org.elasticsearch.transport.RemoteClusterService;
import org.elasticsearch.usage.SearchUsageHolder;
import org.elasticsearch.xcontent.XContentParser;
@@ -63,6 +66,7 @@ public class RestSearchAction extends BaseRestHandler {
public static final String TYPED_KEYS_PARAM = "typed_keys";
public static final String INCLUDE_NAMED_QUERIES_SCORE_PARAM = "include_named_queries_score";
public static final Set RESPONSE_PARAMS = Set.of(TYPED_KEYS_PARAM, TOTAL_HITS_AS_INT_PARAM, INCLUDE_NAMED_QUERIES_SCORE_PARAM);
+ private static final Logger log = LogManager.getLogger(RestSearchAction.class);
private final SearchUsageHolder searchUsageHolder;
private final Predicate clusterSupportsFeature;
@@ -98,6 +102,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
client.threadPool().getThreadContext().setErrorTraceTransportHeader(request);
}
SearchRequest searchRequest = new SearchRequest();
+
// access the BwC param, but just drop it
// this might be set by old clients
request.param("min_compatible_shard_node");
@@ -167,6 +172,16 @@ public static void parseSearchRequest(
searchRequest.source(new SearchSourceBuilder());
}
searchRequest.indices(Strings.splitStringByCommaToArray(request.param("index")));
+
+ var routingTags = request.param("routing_tags", null);
+ if (routingTags != null) {
+ searchRequest.routingTags(
+ Arrays.stream(Strings.splitStringByCommaToArray(routingTags)).map(RemoteClusterService.RemoteTag::fromString).toList()
+ );
+ } else {
+ log.info("No routing tags");
+ }
+
if (requestContentParser != null) {
if (searchUsageHolder == null) {
searchRequest.source().parseXContent(requestContentParser, true, clusterSupportsFeature);
diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java
index af568c7b5d2cb..ddd39676b35cd 100644
--- a/server/src/main/java/org/elasticsearch/search/SearchService.java
+++ b/server/src/main/java/org/elasticsearch/search/SearchService.java
@@ -297,6 +297,8 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
Setting.Property.NodeScope
);
+ public static final Setting FLAT_WORLD_ENABLED = Setting.boolSetting("search.flat_world.enabled", false, Property.NodeScope);
+
private static final boolean BATCHED_QUERY_PHASE_FEATURE_FLAG = new FeatureFlag("batched_query_phase").isEnabled();
/**
diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java b/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java
index 95e507f70d7a9..763faed028b10 100644
--- a/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java
+++ b/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java
@@ -56,6 +56,10 @@ protected static Set getEnabledRemoteClusters(final Settings settings) {
return RemoteConnectionStrategy.getRemoteClusters(settings);
}
+ protected static Map> getEnabledRemoteClustersWithTags(final Settings settings) {
+ return RemoteConnectionStrategy.getRemoteTags(settings);
+ }
+
/**
* Check whether the index expression represents remote index or not.
* The index name is assumed to be individual index (no commas) but can contain `-`, wildcards,
diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java
index fdb597b47c137..0ac19dff6971c 100644
--- a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java
+++ b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java
@@ -41,6 +41,7 @@
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -97,6 +98,33 @@ public final class RemoteClusterService extends RemoteClusterAware
(ns, key) -> boolSetting(key, true, new RemoteConnectionEnabled<>(ns, key), Setting.Property.Dynamic, Setting.Property.NodeScope)
);
+ public record RemoteTag(String key, String value) {
+ public static RemoteTag fromString(String tag) {
+ if (tag == null || tag.isEmpty()) {
+ throw new IllegalArgumentException("Remote tag must not be null or empty");
+ }
+ // - as a separator to simplify search path param parsing; won't be like this in the real implementation
+ int idx = tag.indexOf('-');
+ if (idx < 0) {
+ return new RemoteTag(tag, "");
+ } else {
+ return new RemoteTag(tag.substring(0, idx), tag.substring(idx + 1));
+ }
+ }
+ }
+
+ public static final Setting.AffixSetting> REMOTE_CLUSTER_TAGS = Setting.affixKeySetting(
+ "cluster.remote.",
+ "tags",
+ (ns, key) -> Setting.listSetting(
+ key,
+ Collections.emptyList(),
+ RemoteTag::fromString,
+ Setting.Property.Dynamic,
+ Setting.Property.NodeScope
+ )
+ );
+
public static final Setting.AffixSetting REMOTE_CLUSTER_PING_SCHEDULE = Setting.affixKeySetting(
"cluster.remote.",
"transport.ping_schedule",
diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteConnectionStrategy.java b/server/src/main/java/org/elasticsearch/transport/RemoteConnectionStrategy.java
index a715797b97977..5b1925b0a20fa 100644
--- a/server/src/main/java/org/elasticsearch/transport/RemoteConnectionStrategy.java
+++ b/server/src/main/java/org/elasticsearch/transport/RemoteConnectionStrategy.java
@@ -189,6 +189,10 @@ static Set getRemoteClusters(Settings settings) {
return enablementSettings.flatMap(s -> getClusterAlias(settings, s)).collect(Collectors.toSet());
}
+ static Map> getRemoteTags(Settings settings) {
+ return RemoteClusterService.REMOTE_CLUSTER_TAGS.getAsMap(settings);
+ }
+
public static boolean isConnectionEnabled(String clusterAlias, Settings settings) {
ConnectionStrategy mode = REMOTE_CONNECTION_MODE.getConcreteSettingForNamespace(clusterAlias).get(settings);
if (mode.equals(ConnectionStrategy.SNIFF)) {
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/AuthorizationEngine.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/AuthorizationEngine.java
index 2c831645d0e69..ef7446ae22ae5 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/AuthorizationEngine.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/AuthorizationEngine.java
@@ -299,6 +299,11 @@ interface AuthorizedIndices {
* Checks if an index-like resource name is authorized, for an action by a user. The resource might or might not exist.
*/
boolean check(String name, IndexComponentSelector selector);
+
+ // Does not belong here
+ default boolean checkRemote(String remoteAlias) {
+ return false;
+ }
}
/**
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java
index ff39fd587dc3a..6057010c641cf 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java
@@ -6,6 +6,9 @@
*/
package org.elasticsearch.xpack.security.authz;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.FlatIndicesRequest;
import org.elasticsearch.action.AliasesRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
@@ -31,8 +34,10 @@
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.search.SearchService;
import org.elasticsearch.transport.NoSuchRemoteClusterException;
import org.elasticsearch.transport.RemoteClusterAware;
+import org.elasticsearch.transport.RemoteClusterService;
import org.elasticsearch.transport.RemoteConnectionStrategy;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
@@ -55,14 +60,18 @@
class IndicesAndAliasesResolver {
+ private static final Logger logger = LogManager.getLogger(IndicesAndAliasesResolver.class);
+
private final IndexNameExpressionResolver nameExpressionResolver;
private final IndexAbstractionResolver indexAbstractionResolver;
private final RemoteClusterResolver remoteClusterResolver;
+ private final boolean flatWorldEnabled;
IndicesAndAliasesResolver(Settings settings, ClusterService clusterService, IndexNameExpressionResolver resolver) {
this.nameExpressionResolver = resolver;
this.indexAbstractionResolver = new IndexAbstractionResolver(resolver);
this.remoteClusterResolver = new RemoteClusterResolver(settings, clusterService.getClusterSettings());
+ this.flatWorldEnabled = SearchService.FLAT_WORLD_ENABLED.get(settings);
}
/**
@@ -103,7 +112,6 @@ class IndicesAndAliasesResolver {
* resolving wildcards.
*
*/
-
ResolvedIndices resolve(
String action,
TransportRequest request,
@@ -124,9 +132,67 @@ ResolvedIndices resolve(
if (request instanceof IndicesRequest == false) {
throw new IllegalStateException("Request [" + request + "] is not an Indices request, but should be.");
}
+
+ if (flatWorldEnabled && request instanceof FlatIndicesRequest flatIndicesRequest && flatIndicesRequest.requiresRewrite()) {
+ rewriteFlatIndexExpression(flatIndicesRequest, authorizedIndices);
+ }
+
return resolveIndicesAndAliases(action, (IndicesRequest) request, projectMetadata, authorizedIndices);
}
+ void rewriteFlatIndexExpression(FlatIndicesRequest request, AuthorizationEngine.AuthorizedIndices authorizedIndices) {
+ assert flatWorldEnabled && request.requiresRewrite();
+
+ Set remotes = remoteClusterResolver.clusters();
+ Map> tags = remoteClusterResolver.tags();
+
+ logger.info("Remote available: {} with tags {}", remotes, tags);
+ // no remotes, nothing to rewrite...
+ if (remotes.isEmpty()) {
+ logger.info("No remotes, skipping...");
+ return;
+ }
+
+ var indices = request.indices();
+ // empty indices actually means search everything so would need to also rewrite that
+
+ var targetRemotes = new HashSet();
+ for (var remote : remotes) {
+ List tagsForRemote = tags.get(remote);
+ logger.info("Remote [{}] has tags [{}]", remote, tagsForRemote);
+ // TODO routing also needs to apply to local
+ if (authorizedIndices.checkRemote(remote) && request.checkRemote(tagsForRemote)) {
+ logger.info("Remote [{}] authorized and matches routing", remote);
+ targetRemotes.add(remote);
+ }
+ }
+
+ if (targetRemotes.isEmpty()) {
+ logger.info("No target remotes, skipping...");
+ return;
+ }
+
+ ResolvedIndices resolved = remoteClusterResolver.splitLocalAndRemoteIndexNames(indices);
+ // skip handling searches where there's both qualified and flat expressions to simplify POC
+ // in the real thing, we'd also rewrite these
+ if (resolved.getRemote().isEmpty() == false) {
+ return;
+ }
+
+ List indexExpressions = new ArrayList<>(indices.length);
+ for (var local : resolved.getLocal()) {
+ List rewritten = new ArrayList<>();
+ rewritten.add(local);
+ for (var cluster : targetRemotes) {
+ rewritten.add(RemoteClusterAware.buildRemoteIndexName(cluster, local));
+ indexExpressions.add(new FlatIndicesRequest.IndexExpression(local, rewritten));
+ }
+ logger.info("Rewrote [{}] to [{}]", local, rewritten);
+ }
+
+ request.indexExpressions(indexExpressions);
+ }
+
/**
* Attempt to resolve requested indices without expanding any wildcards.
* @return The {@link ResolvedIndices} or null if wildcard expansion must be performed.
@@ -544,10 +610,13 @@ private static List indicesList(String[] list) {
private static class RemoteClusterResolver extends RemoteClusterAware {
private final CopyOnWriteArraySet clusters;
+ // TODO consolidate
+ private final Map> tags;
private RemoteClusterResolver(Settings settings, ClusterSettings clusterSettings) {
super(settings);
clusters = new CopyOnWriteArraySet<>(getEnabledRemoteClusters(settings));
+ tags = RemoteClusterService.getEnabledRemoteClustersWithTags(settings);
listenForUpdates(clusterSettings);
}
@@ -569,5 +638,13 @@ ResolvedIndices splitLocalAndRemoteIndexNames(String... indices) {
.toList();
return new ResolvedIndices(local == null ? List.of() : local, remote);
}
+
+ Set clusters() {
+ return Collections.unmodifiableSet(clusters);
+ }
+
+ Map> tags() {
+ return Collections.unmodifiableMap(tags);
+ }
}
}
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java
index 1b99bd6888c4f..2e9a20d994e77 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java
@@ -998,6 +998,10 @@ static AuthorizedIndices resolveAuthorizedIndicesFromRole(
} // we don't support granting access to a backing index with a failure selector via the parent data stream
}
return predicate.test(indexAbstraction, selector);
+ }, name -> {
+ // just some bogus predicate that lets us differentiate between roles, not at all
+ // how this will work in the end
+ return Arrays.asList(role.names()).contains("_es_test_root");
});
}
@@ -1125,15 +1129,18 @@ static final class AuthorizedIndices implements AuthorizationEngine.AuthorizedIn
private final CachedSupplier> authorizedAndAvailableDataResources;
private final CachedSupplier> authorizedAndAvailableFailuresResources;
private final BiPredicate isAuthorizedPredicate;
+ private final Predicate projectPredicate;
AuthorizedIndices(
Supplier> authorizedAndAvailableDataResources,
Supplier> authorizedAndAvailableFailuresResources,
- BiPredicate isAuthorizedPredicate
+ BiPredicate isAuthorizedPredicate,
+ Predicate projectPredicate
) {
this.authorizedAndAvailableDataResources = CachedSupplier.wrap(authorizedAndAvailableDataResources);
this.authorizedAndAvailableFailuresResources = CachedSupplier.wrap(authorizedAndAvailableFailuresResources);
this.isAuthorizedPredicate = Objects.requireNonNull(isAuthorizedPredicate);
+ this.projectPredicate = projectPredicate;
}
@Override
@@ -1149,5 +1156,11 @@ public boolean check(String name, IndexComponentSelector selector) {
Objects.requireNonNull(selector, "must specify a selector for authorization check");
return isAuthorizedPredicate.test(name, selector);
}
+
+ @Override
+ public boolean checkRemote(String remoteAlias) {
+ Objects.requireNonNull(remoteAlias, "must specify a project name for authorization check");
+ return projectPredicate.test(remoteAlias);
+ }
}
}