Browse Source

Protect newly introduced system indices fully (#74186)

This change updates the way we handle net new system indices, which are
those that have been newly introduced and do not require any BWC
guarantees around non-system access. These indices will not be included
in wildcard expansions for user searches and operations. Direct access
to these indices will also not be allowed for user searches.

The first index of this type is the GeoIp index, which this change sets
the new flag on.

Closes #72572
Jay Modi 4 years ago
parent
commit
d4afd6a216
20 changed files with 645 additions and 159 deletions
  1. 1 1
      modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderTaskExecutor.java
  2. 1 0
      modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java
  3. 29 0
      qa/system-indices/build.gradle
  4. 105 0
      qa/system-indices/src/javaRestTest/java/org/elasticsearch/system/indices/NetNewSystemIndicesIT.java
  5. 164 0
      qa/system-indices/src/main/java/org/elasticsearch/system/indices/SystemIndicesQA.java
  6. 1 1
      server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java
  7. 28 9
      server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java
  8. 16 0
      server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java
  9. 14 2
      server/src/main/java/org/elasticsearch/cluster/metadata/IndexAbstractionResolver.java
  10. 109 50
      server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
  11. 10 0
      server/src/main/java/org/elasticsearch/indices/SystemDataStreamDescriptor.java
  12. 19 4
      server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java
  13. 26 0
      server/src/main/java/org/elasticsearch/indices/SystemIndices.java
  14. 1 1
      server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesActionTests.java
  15. 4 2
      server/src/test/java/org/elasticsearch/cluster/metadata/DateMathExpressionResolverTests.java
  16. 84 67
      server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java
  17. 19 15
      server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java
  18. 2 1
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/GenerateSnapshotNameStep.java
  19. 4 3
      x-pack/plugin/fleet/src/javaRestTest/java/org/elasticsearch/xpack/fleet/FleetDataStreamIT.java
  20. 8 3
      x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java

+ 1 - 1
modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderTaskExecutor.java

@@ -37,7 +37,7 @@ import static org.elasticsearch.ingest.geoip.GeoIpDownloader.GEOIP_DOWNLOADER;
  */
 public final class GeoIpDownloaderTaskExecutor extends PersistentTasksExecutor<GeoIpTaskParams> implements ClusterStateListener {
 
-    static final boolean ENABLED_DEFAULT = "true".equals(System.getProperty("ingest.geoip.downloader.enabled.default"));
+    static final boolean ENABLED_DEFAULT = "true".equals(System.getProperty("ingest.geoip.downloader.enabled.default", "true"));
     public static final Setting<Boolean> ENABLED_SETTING = Setting.boolSetting("ingest.geoip.downloader.enabled", ENABLED_DEFAULT,
         Setting.Property.Dynamic, Setting.Property.NodeScope);
 

+ 1 - 0
modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java

@@ -180,6 +180,7 @@ public class IngestGeoIpPlugin extends Plugin implements IngestPlugin, SystemInd
             .setOrigin("geoip")
             .setVersionMetaKey("version")
             .setPrimaryIndex(DATABASES_INDEX)
+            .setNetNew()
             .build();
         return Collections.singleton(geoipDatabasesIndex);
     }

+ 29 - 0
qa/system-indices/build.gradle

@@ -0,0 +1,29 @@
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+apply plugin: 'elasticsearch.internal-es-plugin'
+apply plugin: 'elasticsearch.java-rest-test'
+
+esplugin {
+  name 'system-indices-qa'
+  description 'Plugin for performing QA of system indices'
+  classname 'org.elasticsearch.system.indices.SystemIndicesQA'
+  licenseFile rootProject.file('licenses/SSPL-1.0+ELASTIC-LICENSE-2.0.txt')
+  noticeFile rootProject.file('NOTICE.txt')
+}
+
+tasks.named("test").configure { enabled = false }
+tasks.named("javaRestTest").configure {
+  dependsOn "buildZip"
+}
+
+testClusters.all {
+  testDistribution = 'DEFAULT'
+  setting 'xpack.security.enabled', 'true'
+  user username: 'rest_user', password: 'password', role: 'superuser'
+}

+ 105 - 0
qa/system-indices/src/javaRestTest/java/org/elasticsearch/system/indices/NetNewSystemIndicesIT.java

@@ -0,0 +1,105 @@
+
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.system.indices;
+
+import org.apache.http.util.EntityUtils;
+import org.elasticsearch.Version;
+import org.elasticsearch.client.Request;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.ResponseException;
+import org.elasticsearch.common.settings.SecureString;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.test.rest.ESRestTestCase;
+import org.junit.After;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+public class NetNewSystemIndicesIT extends ESRestTestCase {
+
+    static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("rest_user", new SecureString("password".toCharArray()));
+
+    @Override
+    protected Settings restClientSettings() {
+        return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE).build();
+    }
+
+    public void testCreatingSystemIndex() throws Exception {
+        ResponseException e = expectThrows(
+            ResponseException.class,
+            () -> client().performRequest(new Request("PUT", "/.net-new-system-index-" + Version.CURRENT.major))
+        );
+        assertThat(EntityUtils.toString(e.getResponse().getEntity()), containsString("system"));
+
+        Response response = client().performRequest(new Request("PUT", "/_net_new_sys_index/_create"));
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+    }
+
+    public void testIndexDoc() throws Exception {
+        String id = randomAlphaOfLength(4);
+
+        ResponseException e = expectThrows(ResponseException.class, () -> {
+            Request request = new Request("PUT", "/.net-new-system-index-" + Version.CURRENT.major + "/_doc" + id);
+            request.setJsonEntity("{}");
+            client().performRequest(request);
+        });
+        assertThat(EntityUtils.toString(e.getResponse().getEntity()), containsString("system"));
+
+        Request request = new Request("PUT", "/_net_new_sys_index/" + id);
+        request.setJsonEntity("{}");
+        Response response = client().performRequest(request);
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+    }
+
+    public void testSearch() throws Exception {
+        // search before indexing doc
+        Request searchRequest = new Request("GET", "/_search");
+        searchRequest.setJsonEntity("{ \"query\": { \"match_all\": {} } }");
+        searchRequest.addParameter("size", "10000");
+        Response searchResponse = client().performRequest(searchRequest);
+        assertThat(searchResponse.getStatusLine().getStatusCode(), is(200));
+        assertThat(EntityUtils.toString(searchResponse.getEntity()), not(containsString(".net-new")));
+
+        // create a doc
+        String id = randomAlphaOfLength(4);
+        Request request = new Request("PUT", "/_net_new_sys_index/" + id);
+        request.setJsonEntity("{}");
+        request.addParameter("refresh", "true");
+        Response response = client().performRequest(request);
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+
+        // search again
+        searchResponse = client().performRequest(searchRequest);
+        assertThat(searchResponse.getStatusLine().getStatusCode(), is(200));
+        assertThat(EntityUtils.toString(searchResponse.getEntity()), not(containsString(".net-new")));
+
+        // index wildcard search
+        searchRequest = new Request("GET", "/.net-new-system-index*/_search");
+        searchRequest.setJsonEntity("{ \"query\": { \"match_all\": {} } }");
+        searchRequest.addParameter("size", "10000");
+        searchResponse = client().performRequest(searchRequest);
+        assertThat(searchResponse.getStatusLine().getStatusCode(), is(200));
+        assertThat(EntityUtils.toString(searchResponse.getEntity()), not(containsString(".net-new")));
+
+        // direct index search
+        Request directRequest = new Request("GET", "/.net-new-system-index-" + Version.CURRENT.major + "/_search");
+        directRequest.setJsonEntity("{ \"query\": { \"match_all\": {} } }");
+        directRequest.addParameter("size", "10000");
+        ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(directRequest));
+        assertThat(EntityUtils.toString(e.getResponse().getEntity()), containsString("system"));
+    }
+
+    @After
+    public void resetFeatures() throws Exception {
+        client().performRequest(new Request("POST", "/_features/_reset"));
+    }
+}

+ 164 - 0
qa/system-indices/src/main/java/org/elasticsearch/system/indices/SystemIndicesQA.java

@@ -0,0 +1,164 @@
+
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.system.indices;
+
+import org.elasticsearch.Version;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.index.IndexRequest;
+import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.cluster.metadata.IndexMetadata;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
+import org.elasticsearch.common.settings.ClusterSettings;
+import org.elasticsearch.common.settings.IndexScopedSettings;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.settings.SettingsFilter;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.indices.SystemIndexDescriptor;
+import org.elasticsearch.plugins.ActionPlugin;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.plugins.SystemIndexPlugin;
+import org.elasticsearch.rest.BaseRestHandler;
+import org.elasticsearch.rest.RestController;
+import org.elasticsearch.rest.RestHandler;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.RestRequest.Method;
+import org.elasticsearch.rest.action.RestToXContentListener;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Supplier;
+
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME;
+import static org.elasticsearch.rest.RestRequest.Method.POST;
+import static org.elasticsearch.rest.RestRequest.Method.PUT;
+
+public class SystemIndicesQA extends Plugin implements SystemIndexPlugin, ActionPlugin {
+
+    @Override
+    public String getFeatureName() {
+        return "system indices qa";
+    }
+
+    @Override
+    public String getFeatureDescription() {
+        return "plugin used to perform qa on system index behavior";
+    }
+
+    @Override
+    public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings settings) {
+        return List.of(
+            SystemIndexDescriptor.builder()
+                .setNetNew()
+                .setIndexPattern(".net-new-system-index*")
+                .setDescription("net new system index")
+                .setMappings(mappings())
+                .setSettings(
+                    Settings.builder()
+                        .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+                        .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+                        .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1")
+                        .build()
+                )
+                .setOrigin("net-new")
+                .setVersionMetaKey("version")
+                .setPrimaryIndex(".net-new-system-index-" + Version.CURRENT.major)
+                .build()
+        );
+    }
+
+    private static XContentBuilder mappings() {
+        try {
+            return jsonBuilder().startObject()
+                .startObject(SINGLE_MAPPING_NAME)
+                .startObject("_meta")
+                .field("version", Version.CURRENT)
+                .endObject()
+                .field("dynamic", "strict")
+                .startObject("properties")
+                .startObject("name")
+                .field("type", "keyword")
+                .endObject()
+                .endObject()
+                .endObject()
+                .endObject();
+        } catch (IOException e) {
+            throw new UncheckedIOException("Failed to build mappings for net new system index", e);
+        }
+    }
+
+    @Override
+    public List<RestHandler> getRestHandlers(
+        Settings settings,
+        RestController restController,
+        ClusterSettings clusterSettings,
+        IndexScopedSettings indexScopedSettings,
+        SettingsFilter settingsFilter,
+        IndexNameExpressionResolver indexNameExpressionResolver,
+        Supplier<DiscoveryNodes> nodesInCluster
+    ) {
+        return List.of(new CreateNetNewSystemIndexHandler(), new IndexDocHandler());
+    }
+
+    private static class CreateNetNewSystemIndexHandler extends BaseRestHandler {
+
+        @Override
+        public String getName() {
+            return "create net new system index for qa";
+        }
+
+        @Override
+        public List<Route> routes() {
+            return List.of(Route.builder(Method.PUT, "/_net_new_sys_index/_create").build());
+        }
+
+        @Override
+        protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
+            return channel -> client.admin()
+                .indices()
+                .create(new CreateIndexRequest(".net-new-system-index-" + Version.CURRENT.major), new RestToXContentListener<>(channel));
+        }
+
+        @Override
+        public boolean allowSystemIndexAccessByDefault() {
+            return true;
+        }
+    }
+
+    private static class IndexDocHandler extends BaseRestHandler {
+        @Override
+        public String getName() {
+            return "index doc into net new for qa";
+        }
+
+        @Override
+        public List<Route> routes() {
+            return List.of(new Route(POST, "/_net_new_sys_index/{id}"), new Route(PUT, "/_net_new_sys_index/{id}"));
+        }
+
+        @Override
+        protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
+            IndexRequest indexRequest = new IndexRequest(".net-new-system-index-" + Version.CURRENT.major);
+            indexRequest.source(request.requiredContent(), request.getXContentType());
+            indexRequest.id(request.param("id"));
+            indexRequest.setRefreshPolicy(request.param("refresh"));
+
+            return channel -> client.index(indexRequest, new RestToXContentListener<>(channel));
+        }
+
+        @Override
+        public boolean allowSystemIndexAccessByDefault() {
+            return true;
+        }
+    }
+}

+ 1 - 1
server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java

@@ -40,7 +40,7 @@ public class TestSystemIndexDescriptor extends SystemIndexDescriptor {
 
     TestSystemIndexDescriptor() {
         super(INDEX_NAME + "*", PRIMARY_INDEX_NAME, "Test system index", getOldMappings(), SETTINGS, INDEX_NAME, 0, "version", "stack",
-            Version.CURRENT.minimumCompatibilityVersion(), Type.INTERNAL_MANAGED, List.of(), List.of(), null);
+            Version.CURRENT.minimumCompatibilityVersion(), Type.INTERNAL_MANAGED, List.of(), List.of(), null, false);
     }
 
     @Override

+ 28 - 9
server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java

@@ -33,7 +33,6 @@ import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.transport.Transports;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -135,13 +134,18 @@ public class TransportGetAliasesAction extends TransportMasterNodeReadAction<Get
             throw new IllegalArgumentException("Unexpected system index access level: " + systemIndexAccessLevel);
         }
 
+        List<String> netNewSystemIndices = new ArrayList<>();
         List<String> systemIndicesNames = new ArrayList<>();
         for (Iterator<String> it = aliasesMap.keysIt(); it.hasNext(); ) {
             String indexName = it.next();
             IndexMetadata index = state.metadata().index(indexName);
             if (index != null && index.isSystem()) {
                 if (systemIndexAccessAllowPredicate.test(index) == false) {
-                    systemIndicesNames.add(indexName);
+                    if (systemIndices.isNetNewSystemIndex(indexName)) {
+                        netNewSystemIndices.add(indexName);
+                    } else {
+                        systemIndicesNames.add(indexName);
+                    }
                 }
             }
         }
@@ -149,9 +153,11 @@ public class TransportGetAliasesAction extends TransportMasterNodeReadAction<Get
             deprecationLogger.deprecate(DeprecationCategory.API, "open_system_index_access",
                 "this request accesses system indices: {}, but in a future major version, direct access to system " +
                     "indices will be prevented by default", systemIndicesNames);
-        } else {
-            checkSystemAliasAccess(request, systemIndices, systemIndexAccessLevel, threadContext);
         }
+        if (netNewSystemIndices.isEmpty() == false) {
+            throw systemIndices.netNewSystemIndexAccessException(threadContext, netNewSystemIndices);
+        }
+        checkSystemAliasAccess(request, systemIndices, systemIndexAccessLevel, threadContext);
     }
 
     private static void checkSystemAliasAccess(GetAliasesRequest request, SystemIndices systemIndices,
@@ -165,14 +171,27 @@ public class TransportGetAliasesAction extends TransportMasterNodeReadAction<Get
             throw new IllegalArgumentException("Unexpected system index access level: " + systemIndexAccessLevel);
         }
 
-        final List<String> systemAliases = Arrays.stream(request.aliases())
-            .filter(systemIndices::isSystemName)
-            .filter(systemIndexAccessAllowPredicate)
-            .collect(Collectors.toList());
+        final List<String> systemAliases = new ArrayList<>();
+        final List<String> netNewSystemAliases = new ArrayList<>();
+        for (String alias : request.aliases()) {
+            if (systemIndices.isSystemName(alias)) {
+                if (systemIndexAccessAllowPredicate.test(alias)) {
+                    if (systemIndices.isNetNewSystemIndex(alias)) {
+                        netNewSystemAliases.add(alias);
+                    } else {
+                        systemAliases.add(alias);
+                    }
+                }
+            }
+        }
+
         if (systemAliases.isEmpty() == false) {
             deprecationLogger.deprecate(DeprecationCategory.API, "open_system_alias_access",
-                "this request accesses aliases with names reserved for system indices: {}, but in a future major version, direct" +
+                "this request accesses aliases with names reserved for system indices: {}, but in a future major version, direct " +
                     "access to system indices and their aliases will not be allowed", systemAliases);
         }
+        if (netNewSystemAliases.isEmpty() == false) {
+            throw systemIndices.netNewSystemIndexAccessException(threadContext, netNewSystemAliases);
+        }
     }
 }

+ 16 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java

@@ -26,10 +26,12 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.indices.SystemIndexDescriptor;
 import org.elasticsearch.indices.SystemIndices;
+import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -72,6 +74,20 @@ public class TransportCreateIndexAction extends TransportMasterNodeAction<Create
 
         final SystemIndexDescriptor mainDescriptor = systemIndices.findMatchingDescriptor(indexName);
         final boolean isSystemIndex = mainDescriptor != null && mainDescriptor.isAutomaticallyManaged();
+        if (mainDescriptor != null && mainDescriptor.isNetNew()) {
+            final SystemIndexAccessLevel systemIndexAccessLevel = systemIndices.getSystemIndexAccessLevel(threadPool.getThreadContext());
+            if (systemIndexAccessLevel != SystemIndexAccessLevel.ALL) {
+                if (systemIndexAccessLevel == SystemIndexAccessLevel.RESTRICTED) {
+                    if (systemIndices.getProductSystemIndexNamePredicate(threadPool.getThreadContext()).test(indexName) == false) {
+                        throw systemIndices.netNewSystemIndexAccessException(threadPool.getThreadContext(), List.of(indexName));
+                    }
+                } else {
+                    assert systemIndexAccessLevel == SystemIndexAccessLevel.NONE :
+                        "Expected no system index access but level is " + systemIndexAccessLevel;
+                    throw systemIndices.netNewSystemIndexAccessException(threadPool.getThreadContext(), List.of(indexName));
+                }
+            }
+        }
 
         final CreateIndexClusterStateUpdateRequest updateRequest;
 

+ 14 - 2
server/src/main/java/org/elasticsearch/cluster/metadata/IndexAbstractionResolver.java

@@ -133,7 +133,6 @@ public class IndexAbstractionResolver {
             if (includeDataStreams == false) {
                 return false;
             }
-
             if (indexAbstraction.isSystem()) {
                 final SystemIndexAccessLevel level = resolver.getSystemIndexAccessLevel();
                 if (level == SystemIndexAccessLevel.ALL) {
@@ -158,9 +157,22 @@ public class IndexAbstractionResolver {
             return false;
         }
         if (indexAbstraction.isSystem()) {
-            // system index that backs system data stream
+            // check if it is net new
+            if (resolver.getNetNewSystemIndexPredicate().test(indexAbstraction.getName())) {
+                final SystemIndexAccessLevel level = resolver.getSystemIndexAccessLevel();
+                if (level == SystemIndexAccessLevel.ALL) {
+                    return true;
+                } else if (level == SystemIndexAccessLevel.NONE) {
+                    return false;
+                } else if (level == SystemIndexAccessLevel.RESTRICTED) {
+                    return resolver.getSystemIndexAccessPredicate().test(indexAbstraction.getName());
+                }
+            }
+
+            // does the system index back a system data stream?
             if (indexAbstraction.getParentDataStream() != null) {
                 if (indexAbstraction.getParentDataStream().isSystem() == false) {
+                    assert false : "system index is part of a data stream that is not a system data stream";
                     throw new IllegalStateException("system index is part of a data stream that is not a system data stream");
                 }
                 final SystemIndexAccessLevel level = resolver.getSystemIndexAccessLevel();

+ 109 - 50
server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java

@@ -78,7 +78,7 @@ public class IndexNameExpressionResolver {
      */
     public String[] concreteIndexNames(ClusterState state, IndicesRequest request) {
         Context context = new Context(state, request.indicesOptions(), false, false, request.includeDataStreams(),
-            getSystemIndexAccessPredicate());
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndexNames(context, request.indices());
     }
 
@@ -87,7 +87,7 @@ public class IndexNameExpressionResolver {
      */
     public String[] concreteIndexNamesWithSystemIndexAccess(ClusterState state, IndicesRequest request) {
         Context context = new Context(state, request.indicesOptions(), false, false, request.includeDataStreams(),
-            name -> true);
+            SystemIndexAccessLevel.ALL, name -> true, name -> false);
         return concreteIndexNames(context, request.indices());
     }
 
@@ -97,7 +97,7 @@ public class IndexNameExpressionResolver {
      */
     public Index[] concreteIndices(ClusterState state, IndicesRequest request) {
         Context context = new Context(state, request.indicesOptions(), false, false, request.includeDataStreams(),
-            getSystemIndexAccessPredicate());
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndices(context, request.indices());
     }
 
@@ -115,27 +115,31 @@ public class IndexNameExpressionResolver {
      * indices options in the context don't allow such a case.
      */
     public String[] concreteIndexNames(ClusterState state, IndicesOptions options, String... indexExpressions) {
-        Context context = new Context(state, options, getSystemIndexAccessPredicate());
+        Context context = new Context(state, options, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndexNames(context, indexExpressions);
     }
 
     public String[] concreteIndexNames(ClusterState state, IndicesOptions options, boolean includeDataStreams, String... indexExpressions) {
-        Context context = new Context(state, options, false, false, includeDataStreams, getSystemIndexAccessPredicate());
+        Context context = new Context(state, options, false, false, includeDataStreams, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndexNames(context, indexExpressions);
     }
 
     public String[] concreteIndexNames(ClusterState state, IndicesOptions options, IndicesRequest request) {
-        Context context = new Context(state, options, false, false, request.includeDataStreams(), getSystemIndexAccessPredicate());
+        Context context = new Context(state, options, false, false, request.includeDataStreams(),
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndexNames(context, request.indices());
     }
 
     public String[] concreteIndexNamesWithSystemIndexAccess(ClusterState state, IndicesOptions options, String... indexExpressions) {
-        Context context = new Context(state, options, name -> true);
+        Context context = new Context(state, options, SystemIndexAccessLevel.ALL, name -> true, name -> false);
         return concreteIndexNames(context, indexExpressions);
     }
 
     public List<String> dataStreamNames(ClusterState state, IndicesOptions options, String... indexExpressions) {
-        Context context = new Context(state, options, false, false, true, true, getSystemIndexAccessPredicate());
+        Context context = new Context(state, options, false, false, true, true, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         if (indexExpressions == null || indexExpressions.length == 0) {
             indexExpressions = new String[]{"*"};
         }
@@ -171,7 +175,7 @@ public class IndexNameExpressionResolver {
 
     public Index[] concreteIndices(ClusterState state, IndicesOptions options, boolean includeDataStreams, String... indexExpressions) {
         Context context = new Context(state, options, false, false, includeDataStreams,
-            getSystemIndexAccessPredicate());
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndices(context, indexExpressions);
     }
 
@@ -189,7 +193,7 @@ public class IndexNameExpressionResolver {
      */
     public Index[] concreteIndices(ClusterState state, IndicesRequest request, long startTime) {
         Context context = new Context(state, request.indicesOptions(), startTime, false, false, request.includeDataStreams(), false,
-            getSystemIndexAccessPredicate());
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         return concreteIndices(context, request.indices());
     }
 
@@ -327,12 +331,15 @@ public class IndexNameExpressionResolver {
         }
 
         final List<String> resolvedSystemIndices = new ArrayList<>();
+        final List<String> resolvedNetNewSystemIndices = new ArrayList<>();
         final Set<String> resolvedSystemDataStreams = new HashSet<>();
         final SortedMap<String, IndexAbstraction> indicesLookup = metadata.getIndicesLookup();
         for (IndexMetadata idxMetadata : systemIndicesThatShouldNotBeAccessed) {
             IndexAbstraction abstraction = indicesLookup.get(idxMetadata.getIndex().getName());
             if (abstraction.getParentDataStream() != null) {
                 resolvedSystemDataStreams.add(abstraction.getParentDataStream().getName());
+            } else if (systemIndices.isNetNewSystemIndex(idxMetadata.getIndex().getName())) {
+                resolvedNetNewSystemIndices.add(idxMetadata.getIndex().getName());
             } else {
                 resolvedSystemIndices.add(idxMetadata.getIndex().getName());
             }
@@ -347,6 +354,9 @@ public class IndexNameExpressionResolver {
         if (resolvedSystemDataStreams.isEmpty() == false) {
             throw systemIndices.dataStreamAccessException(threadContext, resolvedSystemDataStreams);
         }
+        if (resolvedNetNewSystemIndices.isEmpty() == false) {
+            throw systemIndices.netNewSystemIndexAccessException(threadContext, resolvedNetNewSystemIndices);
+        }
     }
 
     private static boolean shouldTrackConcreteIndex(Context context, IndicesOptions options, IndexMetadata index) {
@@ -433,7 +443,8 @@ public class IndexNameExpressionResolver {
             options.allowAliasesToMultipleIndices(), options.forbidClosedIndices(), options.ignoreAliases(),
             options.ignoreThrottled());
 
-        Context context = new Context(state, combinedOptions, false, true, includeDataStreams, getSystemIndexAccessPredicate());
+        Context context = new Context(state, combinedOptions, false, true, includeDataStreams,
+            getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         Index[] indices = concreteIndices(context, index);
         if (allowNoIndices && indices.length == 0) {
             return null;
@@ -450,7 +461,8 @@ public class IndexNameExpressionResolver {
      *         If the data stream, index or alias contains date math then that is resolved too.
      */
     public boolean hasIndexAbstraction(String indexAbstraction, ClusterState state) {
-        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), false, false, true, getSystemIndexAccessPredicate());
+        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), false, false, true, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         String resolvedAliasOrIndex = DateMathExpressionResolver.resolveExpression(indexAbstraction, context);
         return state.metadata().getIndicesLookup().containsKey(resolvedAliasOrIndex);
     }
@@ -461,7 +473,8 @@ public class IndexNameExpressionResolver {
     public String resolveDateMathExpression(String dateExpression) {
         // The data math expression resolver doesn't rely on cluster state or indices options, because
         // it just resolves the date math to an actual date.
-        return DateMathExpressionResolver.resolveExpression(dateExpression, new Context(null, null, getSystemIndexAccessPredicate()));
+        return DateMathExpressionResolver.resolveExpression(dateExpression,
+            new Context(null, null, getSystemIndexAccessLevel(), getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate()));
     }
 
     /**
@@ -469,14 +482,16 @@ public class IndexNameExpressionResolver {
      * @return If the specified string is data math expression then this method returns the resolved expression.
      */
     public String resolveDateMathExpression(String dateExpression, long time) {
-        return DateMathExpressionResolver.resolveExpression(dateExpression, new Context(null, null, time, getSystemIndexAccessPredicate()));
+        return DateMathExpressionResolver.resolveExpression(dateExpression, new Context(null, null, time, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate()));
     }
 
     /**
      * Resolve an array of expressions to the set of indices and aliases that these expressions match.
      */
     public Set<String> resolveExpressions(ClusterState state, String... expressions) {
-        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true, false, true, getSystemIndexAccessPredicate());
+        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true, false, true, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         List<String> resolvedExpressions = Arrays.asList(expressions);
         for (ExpressionResolver expressionResolver : expressionResolvers) {
             resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions);
@@ -570,7 +585,8 @@ public class IndexNameExpressionResolver {
      */
     public Map<String, Set<String>> resolveSearchRouting(ClusterState state, @Nullable String routing, String... expressions) {
         List<String> resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.emptyList();
-        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), false, false, true, getSystemIndexAccessPredicate());
+        Context context = new Context(state, IndicesOptions.lenientExpandOpen(), false, false, true, getSystemIndexAccessLevel(),
+            getSystemIndexAccessPredicate(), getNetNewSystemIndexPredicate());
         for (ExpressionResolver expressionResolver : expressionResolvers) {
             resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions);
         }
@@ -739,6 +755,10 @@ public class IndexNameExpressionResolver {
         return systemIndexAccessLevelPredicate;
     }
 
+    public Predicate<String> getNetNewSystemIndexPredicate() {
+        return systemIndices::isNetNewSystemIndex;
+    }
+
     public static class Context {
 
         private final ClusterState state;
@@ -748,30 +768,44 @@ public class IndexNameExpressionResolver {
         private final boolean resolveToWriteIndex;
         private final boolean includeDataStreams;
         private final boolean preserveDataStreams;
+        private final SystemIndexAccessLevel systemIndexAccessLevel;
         private final Predicate<String> systemIndexAccessPredicate;
+        private final Predicate<String> netNewSystemIndexPredicate;
 
-        Context(ClusterState state, IndicesOptions options, Predicate<String> systemIndexAccessPredicate) {
-            this(state, options, System.currentTimeMillis(), systemIndexAccessPredicate);
+        Context(ClusterState state, IndicesOptions options, SystemIndexAccessLevel systemIndexAccessLevel) {
+            this(state, options, systemIndexAccessLevel, s -> true, s -> false);
+        }
+
+        Context(ClusterState state, IndicesOptions options, SystemIndexAccessLevel systemIndexAccessLevel,
+                Predicate<String> systemIndexAccessPredicate, Predicate<String> netNewSystemIndexPredicate) {
+            this(state, options, System.currentTimeMillis(), systemIndexAccessLevel, systemIndexAccessPredicate,
+                netNewSystemIndexPredicate);
         }
 
         Context(ClusterState state, IndicesOptions options, boolean preserveAliases, boolean resolveToWriteIndex,
-                boolean includeDataStreams, Predicate<String> systemIndexAccessPredicate) {
+                boolean includeDataStreams, SystemIndexAccessLevel systemIndexAccessLevel, Predicate<String> systemIndexAccessPredicate,
+                Predicate<String> netNewSystemIndexPredicate) {
             this(state, options, System.currentTimeMillis(), preserveAliases, resolveToWriteIndex, includeDataStreams, false,
-                systemIndexAccessPredicate);
+                systemIndexAccessLevel, systemIndexAccessPredicate, netNewSystemIndexPredicate);
         }
 
         Context(ClusterState state, IndicesOptions options, boolean preserveAliases, boolean resolveToWriteIndex,
-                boolean includeDataStreams, boolean preserveDataStreams, Predicate<String> systemIndexAccessPredicate) {
+                boolean includeDataStreams, boolean preserveDataStreams, SystemIndexAccessLevel systemIndexAccessLevel,
+                Predicate<String> systemIndexAccessPredicate, Predicate<String> netNewSystemIndexPredicate) {
             this(state, options, System.currentTimeMillis(), preserveAliases, resolveToWriteIndex, includeDataStreams, preserveDataStreams,
-                systemIndexAccessPredicate);
+                systemIndexAccessLevel, systemIndexAccessPredicate, netNewSystemIndexPredicate);
         }
 
-        Context(ClusterState state, IndicesOptions options, long startTime, Predicate<String> systemIndexAccessPredicate) {
-           this(state, options, startTime, false, false, false, false, systemIndexAccessPredicate);
+        Context(ClusterState state, IndicesOptions options, long startTime, SystemIndexAccessLevel systemIndexAccessLevel,
+                Predicate<String> systemIndexAccessPredicate, Predicate<String> netNewSystemIndexPredicate) {
+           this(state, options, startTime, false, false, false, false, systemIndexAccessLevel, systemIndexAccessPredicate,
+               netNewSystemIndexPredicate);
         }
 
         protected Context(ClusterState state, IndicesOptions options, long startTime, boolean preserveAliases, boolean resolveToWriteIndex,
-                          boolean includeDataStreams, boolean preserveDataStreams, Predicate<String> systemIndexAccessPredicate) {
+                          boolean includeDataStreams, boolean preserveDataStreams, SystemIndexAccessLevel systemIndexAccessLevel,
+                          Predicate<String> systemIndexAccessPredicate,
+                          Predicate<String> netNewSystemIndexPredicate) {
             this.state = state;
             this.options = options;
             this.startTime = startTime;
@@ -779,7 +813,9 @@ public class IndexNameExpressionResolver {
             this.resolveToWriteIndex = resolveToWriteIndex;
             this.includeDataStreams = includeDataStreams;
             this.preserveDataStreams = preserveDataStreams;
+            this.systemIndexAccessLevel = systemIndexAccessLevel;
             this.systemIndexAccessPredicate = systemIndexAccessPredicate;
+            this.netNewSystemIndexPredicate = netNewSystemIndexPredicate;
         }
 
         public ClusterState getState() {
@@ -855,20 +891,7 @@ public class IndexNameExpressionResolver {
             }
 
             if (isEmptyOrTrivialWildcard(expressions)) {
-                List<String> resolvedExpressions = resolveEmptyOrTrivialWildcard(options, metadata).stream()
-                    .filter(expression -> {
-                        IndexAbstraction abstraction = metadata.getIndicesLookup().get(expression);
-                        if (abstraction != null && abstraction.isSystem()) {
-                            if (abstraction.getType() == Type.DATA_STREAM || abstraction.getParentDataStream() != null) {
-                                return context.systemIndexAccessPredicate.test(abstraction.getName());
-                            } else {
-                                return true;
-                            }
-                        } else {
-                            return true;
-                        }
-                    })
-                    .collect(Collectors.toList());
+                List<String> resolvedExpressions = resolveEmptyOrTrivialWildcard(context);
                 if (context.includeDataStreams()) {
                     final IndexMetadata.State excludeState = excludeState(options);
                     final Map<String, IndexAbstraction> dataStreamsAbstractions = metadata.getIndicesLookup().entrySet()
@@ -1068,11 +1091,16 @@ public class IndexNameExpressionResolver {
                 String aliasOrIndexName = entry.getKey();
                 IndexAbstraction indexAbstraction = entry.getValue();
 
-                if (indexAbstraction.isSystem() &&
-                    (indexAbstraction.getType() == Type.DATA_STREAM || indexAbstraction.getParentDataStream() != null)) {
-                    if (context.systemIndexAccessPredicate.test(indexAbstraction.getName()) == false) {
+                if (indexAbstraction.isSystem()) {
+                    if (context.netNewSystemIndexPredicate.test(indexAbstraction.getName()) &&
+                        context.systemIndexAccessPredicate.test(indexAbstraction.getName()) == false) {
                         continue;
                     }
+                    if (indexAbstraction.getType() == Type.DATA_STREAM || indexAbstraction.getParentDataStream() != null) {
+                        if (context.systemIndexAccessPredicate.test(indexAbstraction.getName()) == false) {
+                            continue;
+                        }
+                    }
                 }
 
                 if (indexAbstraction.isHidden() == false || includeHidden || implicitHiddenMatch(aliasOrIndexName, expression)) {
@@ -1102,21 +1130,52 @@ public class IndexNameExpressionResolver {
                 Regex.isMatchAllPattern(expressions.get(0))));
         }
 
-        private static List<String> resolveEmptyOrTrivialWildcard(IndicesOptions options, Metadata metadata) {
+        private static List<String> resolveEmptyOrTrivialWildcard(Context context) {
+            final String[] allIndices = resolveEmptyOrTrivialWildcardToAllIndices(context.getOptions(), context.getState().metadata());
+            if (context.systemIndexAccessLevel == SystemIndexAccessLevel.ALL) {
+                return List.of(allIndices);
+            } else {
+                return resolveEmptyOrTrivialWildcardWithAllowedSystemIndices(context, allIndices);
+            }
+        }
+
+        private static List<String> resolveEmptyOrTrivialWildcardWithAllowedSystemIndices(Context context, String[] allIndices) {
+            return Arrays.stream(allIndices)
+                .filter(name -> {
+                        if (name.startsWith(".")) {
+                            IndexAbstraction abstraction = context.state.metadata().getIndicesLookup().get(name);
+                            assert abstraction != null : "null abstraction for " + name + " but was in array of all indices";
+                            if (abstraction.isSystem()) {
+                                if (context.netNewSystemIndexPredicate.test(name)) {
+                                    return context.systemIndexAccessPredicate.test(name);
+                                } else if (abstraction.getType() == Type.DATA_STREAM || abstraction.getParentDataStream() != null) {
+                                    return context.systemIndexAccessPredicate.test(name);
+                                }
+                            } else {
+                                return true;
+                            }
+                        }
+                        return true;
+                    }
+                )
+                .collect(Collectors.toUnmodifiableList());
+        }
+
+        private static String[] resolveEmptyOrTrivialWildcardToAllIndices(IndicesOptions options, Metadata metadata) {
             if (options.expandWildcardsOpen() && options.expandWildcardsClosed() && options.expandWildcardsHidden()) {
-                return Arrays.asList(metadata.getConcreteAllIndices());
+                return metadata.getConcreteAllIndices();
             } else if (options.expandWildcardsOpen() && options.expandWildcardsClosed()) {
-                return Arrays.asList(metadata.getConcreteVisibleIndices());
+                return metadata.getConcreteVisibleIndices();
             } else if (options.expandWildcardsOpen() && options.expandWildcardsHidden()) {
-                return Arrays.asList(metadata.getConcreteAllOpenIndices());
+                return metadata.getConcreteAllOpenIndices();
             } else if (options.expandWildcardsOpen()) {
-                return Arrays.asList(metadata.getConcreteVisibleOpenIndices());
+                return metadata.getConcreteVisibleOpenIndices();
             } else if (options.expandWildcardsClosed() && options.expandWildcardsHidden()) {
-                return Arrays.asList(metadata.getConcreteAllClosedIndices());
+                return metadata.getConcreteAllClosedIndices();
             } else if (options.expandWildcardsClosed()) {
-                return Arrays.asList(metadata.getConcreteVisibleClosedIndices());
+                return metadata.getConcreteVisibleClosedIndices();
             } else {
-                return Collections.emptyList();
+                return Strings.EMPTY_ARRAY;
             }
         }
     }

+ 10 - 0
server/src/main/java/org/elasticsearch/indices/SystemDataStreamDescriptor.java

@@ -48,6 +48,16 @@ public class SystemDataStreamDescriptor {
                                       List<String> allowedElasticProductOrigins,
                                       ExecutorNames executorNames) {
         this.dataStreamName = Objects.requireNonNull(dataStreamName, "dataStreamName must be specified");
+        if (dataStreamName.length() < 2) {
+            throw new IllegalArgumentException(
+                "system data stream name [" + dataStreamName + "] but must at least 2 characters in length"
+            );
+        }
+        if (dataStreamName.charAt(0) != '.') {
+            throw new IllegalArgumentException(
+                "system data stream name [" + dataStreamName + "] but must start with the character [.]"
+            );
+        }
         this.description = Objects.requireNonNull(description, "description must be specified");
         this.type = Objects.requireNonNull(type, "type must be specified");
         this.composableIndexTemplate = Objects.requireNonNull(composableIndexTemplate, "composableIndexTemplate must be provided");

+ 19 - 4
server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java

@@ -96,6 +96,8 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
      */
     private final List<SystemIndexDescriptor> priorSystemIndexDescriptors;
 
+    private final boolean isNetNew;
+
     /**
      * The thread pools that actions will use to operate on this descriptor's system indices
      */
@@ -109,7 +111,7 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
      */
     public SystemIndexDescriptor(String indexPattern, String description) {
         this(indexPattern, null, description, null, null, null, 0, null, null, Version.CURRENT.minimumCompatibilityVersion(),
-            Type.INTERNAL_UNMANAGED, List.of(), List.of(), null);
+            Type.INTERNAL_UNMANAGED, List.of(), List.of(), null, false);
     }
 
     /**
@@ -123,7 +125,7 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
      */
     public SystemIndexDescriptor(String indexPattern, String description, Type type, List<String> allowedElasticProductOrigins) {
         this(indexPattern, null, description, null, null, null, 0, null, null, Version.CURRENT.minimumCompatibilityVersion(), type,
-            allowedElasticProductOrigins, List.of(), null);
+            allowedElasticProductOrigins, List.of(), null, false);
     }
 
     /**
@@ -161,7 +163,8 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
         Type type,
         List<String> allowedElasticProductOrigins,
         List<SystemIndexDescriptor> priorSystemIndexDescriptors,
-        ExecutorNames executorNames
+        ExecutorNames executorNames,
+        boolean isNetNew
     ) {
         Objects.requireNonNull(indexPattern, "system index pattern must not be null");
         if (indexPattern.length() < 2) {
@@ -305,6 +308,7 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
         this.executorNames = Objects.nonNull(executorNames)
             ? executorNames
             : ExecutorNames.DEFAULT_SYSTEM_INDEX_THREAD_POOLS;
+        this.isNetNew = isNetNew;
     }
 
 
@@ -414,6 +418,10 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
         return allowedElasticProductOrigins;
     }
 
+    public boolean isNetNew() {
+        return isNetNew;
+    }
+
     public Version getMappingVersion() {
         if (type.isManaged() == false) {
             throw new IllegalStateException(this + " is not managed so there are no mappings or version");
@@ -537,6 +545,7 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
         private List<String> allowedElasticProductOrigins = List.of();
         private List<SystemIndexDescriptor> priorSystemIndexDescriptors = List.of();
         private ExecutorNames executorNames;
+        private boolean isNetNew = false;
 
         private Builder() {}
 
@@ -615,6 +624,11 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
             return this;
         }
 
+        public Builder setNetNew() {
+            this.isNetNew = true;
+            return this;
+        }
+
         /**
          * Builds a {@link SystemIndexDescriptor} using the fields supplied to this builder.
          * @return a populated descriptor.
@@ -635,7 +649,8 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
                 type,
                 allowedElasticProductOrigins,
                 priorSystemIndexDescriptors,
-                executorNames
+                executorNames,
+                isNetNew
             );
         }
     }

+ 26 - 0
server/src/main/java/org/elasticsearch/indices/SystemIndices.java

@@ -68,6 +68,7 @@ public class SystemIndices {
 
     private final CharacterRunAutomaton systemIndexAutomaton;
     private final CharacterRunAutomaton systemDataStreamIndicesAutomaton;
+    private final CharacterRunAutomaton netNewSystemIndexAutomaton;
     private final Predicate<String> systemDataStreamAutomaton;
     private final Map<String, Feature> featureDescriptors;
     private final Map<String, CharacterRunAutomaton> productToSystemIndicesMatcher;
@@ -83,6 +84,7 @@ public class SystemIndices {
         checkForOverlappingPatterns(featureDescriptors);
         checkForDuplicateAliases(this.getSystemIndexDescriptors());
         this.systemIndexAutomaton = buildIndexCharacterRunAutomaton(featureDescriptors);
+        this.netNewSystemIndexAutomaton = buildNetNewIndexCharacterRunAutomaton(featureDescriptors);
         this.systemDataStreamIndicesAutomaton = buildDataStreamBackingIndicesAutomaton(featureDescriptors);
         this.systemDataStreamAutomaton = buildDataStreamNamePredicate(featureDescriptors);
         this.productToSystemIndicesMatcher = getProductToSystemIndicesMap(featureDescriptors);
@@ -185,6 +187,10 @@ public class SystemIndices {
         return systemDataStreamIndicesAutomaton.run(name);
     }
 
+    public boolean isNetNewSystemIndex(String indexName) {
+        return netNewSystemIndexAutomaton.run(indexName);
+    }
+
     /**
      * Finds a single matching {@link SystemIndexDescriptor}, if any, for the given index name.
      * @param name the name of the index
@@ -294,6 +300,15 @@ public class SystemIndices {
         return new CharacterRunAutomaton(MinimizationOperations.minimize(automaton.orElse(EMPTY), Integer.MAX_VALUE));
     }
 
+    private static CharacterRunAutomaton buildNetNewIndexCharacterRunAutomaton(Map<String, Feature> featureDescriptors) {
+        Optional<Automaton> automaton = featureDescriptors.values().stream()
+            .flatMap(feature -> feature.getIndexDescriptors().stream())
+            .filter(SystemIndexDescriptor::isNetNew)
+            .map(descriptor -> SystemIndexDescriptor.buildAutomaton(descriptor.getIndexPattern(), descriptor.getAliasName()))
+            .reduce(Operations::union);
+        return new CharacterRunAutomaton(MinimizationOperations.minimize(automaton.orElse(EMPTY), Integer.MAX_VALUE));
+    }
+
     private static Automaton featureToIndexAutomaton(Feature feature) {
         Optional<Automaton> systemIndexAutomaton = feature.getIndexDescriptors().stream()
             .map(descriptor -> SystemIndexDescriptor.buildAutomaton(descriptor.getIndexPattern(), descriptor.getAliasName()))
@@ -365,6 +380,17 @@ public class SystemIndices {
         );
     }
 
+    public IllegalArgumentException netNewSystemIndexAccessException(ThreadContext threadContext, Collection<String> names) {
+        final String product = threadContext.getHeader(EXTERNAL_SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY);
+        if (product == null) {
+            return new IllegalArgumentException("Indices " + Arrays.toString(names.toArray(Strings.EMPTY_ARRAY)) +
+                " use and access is reserved for system operations");
+        } else {
+            return new IllegalArgumentException("Indices " + Arrays.toString(names.toArray(Strings.EMPTY_ARRAY)) +
+                " use and access is reserved for system operations");
+        }
+    }
+
     IllegalArgumentException dataStreamAccessException(@Nullable String product, String... dataStreamNames) {
         if (product == null) {
             return new IllegalArgumentException("Data stream(s) " + Arrays.toString(dataStreamNames) +

+ 1 - 1
server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesActionTests.java

@@ -187,7 +187,7 @@ public class TransportGetAliasesActionTests extends ESTestCase {
                 SystemIndexAccessLevel.NONE, null, systemIndices);
         assertThat(result.size(), equalTo(0));
         assertWarnings("this request accesses aliases with names reserved for system indices: [.y], but in a future major version, direct" +
-            "access to system indices and their aliases will not be allowed");
+            " access to system indices and their aliases will not be allowed");
     }
 
     public void testPostProcessDataStreamAliases() {

+ 4 - 2
server/src/test/java/org/elasticsearch/cluster/metadata/DateMathExpressionResolverTests.java

@@ -14,6 +14,7 @@ import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.Context;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.DateMathExpressionResolver;
+import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
 import org.elasticsearch.test.ESTestCase;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -33,7 +34,7 @@ public class DateMathExpressionResolverTests extends ESTestCase {
     private final DateMathExpressionResolver expressionResolver = new DateMathExpressionResolver();
     private final Context context = new Context(
         ClusterState.builder(new ClusterName("_name")).build(), IndicesOptions.strictExpand(),
-        name -> false
+        SystemIndexAccessLevel.NONE
     );
 
     public void testNormal() throws Exception {
@@ -136,7 +137,8 @@ public class DateMathExpressionResolverTests extends ESTestCase {
             // rounding to today 00:00
             now = DateTime.now(UTC).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
         }
-        Context context = new Context(this.context.getState(), this.context.getOptions(), now.getMillis(), name -> false);
+        Context context = new Context(this.context.getState(), this.context.getOptions(), now.getMillis(),
+            SystemIndexAccessLevel.NONE, name -> false, name -> false);
         List<String> results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{yyyy.MM.dd|" + timeZone.getID() + "}}>"));
         assertThat(results.size(), equalTo(1));
         logger.info("timezone: [{}], now [{}], name: [{}]", timeZone, now, results.get(0));

+ 84 - 67
server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java

@@ -33,6 +33,7 @@ import org.elasticsearch.indices.SystemIndexDescriptor;
 import org.elasticsearch.indices.SystemIndexDescriptor.Type;
 import org.elasticsearch.indices.SystemIndices;
 import org.elasticsearch.indices.SystemIndices.Feature;
+import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
 import org.elasticsearch.indices.TestIndexNameExpressionResolver;
 import org.elasticsearch.test.ESTestCase;
 
@@ -48,7 +49,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.backingIndexEqualTo;
@@ -73,8 +73,6 @@ import static org.hamcrest.Matchers.notNullValue;
 
 public class IndexNameExpressionResolverTests extends ESTestCase {
 
-    private static final Predicate<String> NONE = name -> false;
-
     private IndexNameExpressionResolver indexNameExpressionResolver;
     private ThreadContext threadContext;
     private long epochMillis;
@@ -106,7 +104,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         IndicesOptions[] indicesOptions = new IndicesOptions[]{ IndicesOptions.strictExpandOpen(), IndicesOptions.strictExpand()};
         for (IndicesOptions options : indicesOptions) {
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
             String[] results = indexNameExpressionResolver.concreteIndexNames(context, "foo");
             assertEquals(1, results.length);
             assertEquals("foo", results[0]);
@@ -156,26 +155,26 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         }
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(3, results.length);
 
         results = indexNameExpressionResolver.concreteIndexNames(context, (String[])null);
         assertEquals(3, results.length);
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpand(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpand(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(4, results.length);
 
         results = indexNameExpressionResolver.concreteIndexNames(context, (String[])null);
         assertEquals(4, results.length);
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foofoo*");
         assertEquals(3, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "foobar", "foofoo"));
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpand(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpand(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foofoo*");
         assertEquals(4, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "foobar", "foofoo", "foofoo-closed"));
@@ -191,7 +190,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         IndicesOptions[] indicesOptions = new IndicesOptions[]{IndicesOptions.lenientExpandOpen(), IndicesOptions.lenientExpand()};
         for (IndicesOptions options : indicesOptions) {
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
             String[] results = indexNameExpressionResolver.concreteIndexNames(context, "foo");
             assertEquals(1, results.length);
             assertEquals("foo", results[0]);
@@ -229,20 +229,20 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         }
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(3, results.length);
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpand(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpand(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(Arrays.toString(results), 4, results.length);
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context,  "foofoo*");
         assertEquals(3, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "foobar", "foofoo"));
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpand(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpand(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foofoo*");
         assertEquals(4, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "foobar", "foofoo", "foofoo-closed"));
@@ -261,7 +261,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         IndicesOptions[] indicesOptions = new IndicesOptions[]{expandOpen, expand};
 
         for (IndicesOptions options : indicesOptions) {
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
             String[] results = indexNameExpressionResolver.concreteIndexNames(context, "foo");
             assertEquals(1, results.length);
             assertEquals("foo", results[0]);
@@ -283,11 +284,12 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
             }
         }
 
-        IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, expandOpen, NONE);
+        IndexNameExpressionResolver.Context context =
+            new IndexNameExpressionResolver.Context(state, expandOpen, SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(3, results.length);
 
-        context = new IndexNameExpressionResolver.Context(state, expand, NONE);
+        context = new IndexNameExpressionResolver.Context(state, expand, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(4, results.length);
     }
@@ -305,7 +307,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // Only closed
         IndicesOptions options = IndicesOptions.fromOptions(false, true, false, true, false);
-        IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(1, results.length);
         assertEquals("foo", results[0]);
@@ -330,7 +332,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // Only open
         options = IndicesOptions.fromOptions(false, true, true, false, false);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(2, results.length);
         assertThat(results, arrayContainingInAnyOrder("bar", "foobar"));
@@ -354,7 +356,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // Open and closed
         options = IndicesOptions.fromOptions(false, true, true, true, false);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(3, results.length);
         assertThat(results, arrayContainingInAnyOrder("bar", "foobar", "foo"));
@@ -393,7 +395,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // open closed and hidden
         options = IndicesOptions.fromOptions(false, true, true, true, true);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(7, results.length);
         assertThat(results, arrayContainingInAnyOrder("bar", "foobar", "foo", "hidden", "hidden-closed", ".hidden", ".hidden-closed"));
@@ -435,7 +437,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // open and hidden
         options = IndicesOptions.fromOptions(false, true, true, false, true);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(4, results.length);
         assertThat(results, arrayContainingInAnyOrder("bar", "foobar", "hidden", ".hidden"));
@@ -454,7 +456,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // closed and hidden
         options = IndicesOptions.fromOptions(false, true, false, true, true);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertEquals(3, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "hidden-closed", ".hidden-closed"));
@@ -473,7 +475,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         // only hidden
         options = IndicesOptions.fromOptions(false, true, false, false, true);
-        context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        context = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertThat(results, emptyArray());
 
@@ -487,7 +489,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         assertThat(results, arrayContainingInAnyOrder("hidden-closed"));
 
         options = IndicesOptions.fromOptions(false, false, true, true, true);
-        IndexNameExpressionResolver.Context context2 = new IndexNameExpressionResolver.Context(state, options, NONE);
+        IndexNameExpressionResolver.Context context2 = new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndexNames(context2, "-*"));
         assertThat(infe.getResourceId().toString(), equalTo("[-*]"));
@@ -504,7 +506,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         //ignore unavailable and allow no indices
         {
             IndicesOptions noExpandLenient = IndicesOptions.fromOptions(true, true, false, false, randomBoolean());
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, noExpandLenient, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, noExpandLenient, SystemIndexAccessLevel.NONE);
             String[] results = indexNameExpressionResolver.concreteIndexNames(context, "baz*");
             assertThat(results, emptyArray());
 
@@ -526,7 +529,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         //ignore unavailable but don't allow no indices
         {
             IndicesOptions noExpandDisallowEmpty = IndicesOptions.fromOptions(true, false, false, false, randomBoolean());
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, noExpandDisallowEmpty, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, noExpandDisallowEmpty, SystemIndexAccessLevel.NONE);
 
             {
                 IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
@@ -551,7 +555,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         //error on unavailable but allow no indices
         {
             IndicesOptions noExpandErrorUnavailable = IndicesOptions.fromOptions(false, true, false, false, randomBoolean());
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, noExpandErrorUnavailable, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, noExpandErrorUnavailable, SystemIndexAccessLevel.NONE);
             {
                 String[] results = indexNameExpressionResolver.concreteIndexNames(context, "baz*");
                 assertThat(results, emptyArray());
@@ -577,7 +582,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         //error on both unavailable and no indices
         {
             IndicesOptions noExpandStrict = IndicesOptions.fromOptions(false, false, false, false, randomBoolean());
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, noExpandStrict, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, noExpandStrict, SystemIndexAccessLevel.NONE);
             IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "baz*"));
             assertThat(infe.getIndex().getName(), equalTo("baz*"));
@@ -603,48 +609,48 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         //error on both unavailable and no indices + every alias needs to expand to a single index
 
         {
-            IndexNameExpressionResolver.Context context =
-                new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+                IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
             IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "baz*"));
             assertThat(infe.getIndex().getName(), equalTo("baz*"));
         }
 
         {
-            IndexNameExpressionResolver.Context context =
-                new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+                IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
             IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "foo", "baz*"));
             assertThat(infe.getIndex().getName(), equalTo("baz*"));
         }
 
         {
-            IndexNameExpressionResolver.Context context =
-                new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+                IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
             IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "foofoobar"));
             assertThat(e.getMessage(), containsString("alias [foofoobar] has more than one index associated with it"));
         }
 
         {
-            IndexNameExpressionResolver.Context context =
-                new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+                IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
             IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "foo", "foofoobar"));
             assertThat(e.getMessage(), containsString("alias [foofoobar] has more than one index associated with it"));
         }
 
         {
-            IndexNameExpressionResolver.Context context =
-                new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+                IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
             IndexClosedException ince = expectThrows(IndexClosedException.class,
                     () -> indexNameExpressionResolver.concreteIndexNames(context, "foofoo-closed", "foofoobar"));
             assertThat(ince.getMessage(), equalTo("closed"));
             assertEquals(ince.getIndex().getName(), "foofoo-closed");
         }
 
-        IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictSingleIndexNoExpandForbidClosed(), NONE);
+        IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
+            IndicesOptions.strictSingleIndexNoExpandForbidClosed(), SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, "foo", "barbaz");
         assertEquals(2, results.length);
         assertThat(results, arrayContainingInAnyOrder("foo", "foofoo"));
@@ -654,7 +660,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(Metadata.builder().build()).build();
 
         IndicesOptions options = IndicesOptions.strictExpandOpen();
-        final IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+        final IndexNameExpressionResolver.Context context =
+            new IndexNameExpressionResolver.Context(state, options, SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, Strings.EMPTY_ARRAY);
         assertThat(results, emptyArray());
 
@@ -675,7 +682,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
 
         final IndexNameExpressionResolver.Context context2 =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context2, Strings.EMPTY_ARRAY);
         assertThat(results, emptyArray());
         results = indexNameExpressionResolver.concreteIndexNames(context2, "foo");
@@ -686,7 +693,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         assertThat(results, emptyArray());
 
         final IndexNameExpressionResolver.Context context3 =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, false, true, false), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, false, true, false),
+                SystemIndexAccessLevel.NONE);
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndexNames(context3, Strings.EMPTY_ARRAY));
         assertThat(infe.getResourceId().toString(), equalTo("[_all]"));
@@ -712,7 +720,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                 .put(indexBuilder("kuku"));
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), SystemIndexAccessLevel.NONE);
 
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndexNames(context, "testZZZ"));
@@ -725,7 +733,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                 .put(indexBuilder("kuku"));
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
 
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testXXX", "testZZZ")),
             equalTo(newHashSet("testXXX")));
@@ -737,7 +745,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                 .put(indexBuilder("kuku"));
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), SystemIndexAccessLevel.NONE);
 
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndexNames(context, "testMo", "testMahdy"));
@@ -750,7 +758,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                 .put(indexBuilder("kuku"));
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, new String[]{})),
             equalTo(newHashSet("kuku", "testXXX")));
     }
@@ -758,7 +766,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         Metadata.Builder mdBuilder = Metadata.builder();
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
-            IndicesOptions.fromOptions(false, false, true, true), NONE);
+            IndicesOptions.fromOptions(false, false, true, true), SystemIndexAccessLevel.NONE);
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndices(context, new String[]{}));
         assertThat(infe.getMessage(), is("no such index [null] and no indices exist"));
@@ -768,7 +776,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         Metadata.Builder mdBuilder = Metadata.builder();
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
-            IndicesOptions.fromOptions(false, false, false, false), NONE);
+            IndicesOptions.fromOptions(false, false, false, false), SystemIndexAccessLevel.NONE);
         IndexNotFoundException infe = expectThrows(IndexNotFoundException.class,
                 () -> indexNameExpressionResolver.concreteIndices(context, new String[]{}));
         assertThat(infe.getMessage(), is("no such index [_all] and no indices exist"));
@@ -784,16 +792,20 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, false), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, false),
+                SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testX*")),
             equalTo(new HashSet<String>()));
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, false), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, false),
+            SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testX*")),
             equalTo(newHashSet("testXXX", "testXXY")));
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, true), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, true),
+            SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testX*")),
             equalTo(newHashSet("testXYY")));
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, true), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, true),
+            SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testX*")),
             equalTo(newHashSet("testXXX", "testXXY", "testXYY")));
     }
@@ -811,7 +823,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
 
         IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
-                IndicesOptions.fromOptions(true, true, true, true), NONE);
+                IndicesOptions.fromOptions(true, true, true, true), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testX*")),
                 equalTo(newHashSet("testXXX", "testXXY", "testXYY")));
 
@@ -1099,7 +1111,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
             {
                 ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(Metadata.builder().build()).build();
-                IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, indicesOptions, NONE);
+                IndexNameExpressionResolver.Context context =
+                    new IndexNameExpressionResolver.Context(state, indicesOptions, SystemIndexAccessLevel.NONE);
 
                 // with no indices, asking for all indices should return empty list or exception, depending on indices options
                 if (indicesOptions.allowNoIndices()) {
@@ -1118,7 +1131,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                         .put(indexBuilder("bbb").state(State.OPEN).putAlias(AliasMetadata.builder("bbb_alias1")))
                         .put(indexBuilder("ccc").state(State.CLOSE).putAlias(AliasMetadata.builder("ccc_alias1")));
                 ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
-                IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, indicesOptions, NONE);
+                IndexNameExpressionResolver.Context context =
+                    new IndexNameExpressionResolver.Context(state, indicesOptions, SystemIndexAccessLevel.NONE);
                 if (indicesOptions.expandWildcardsOpen() || indicesOptions.expandWildcardsClosed() || indicesOptions.allowNoIndices()) {
                     String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(context, allIndices);
                     assertThat(concreteIndices, notNullValue());
@@ -1148,7 +1162,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
                     .put(indexBuilder("bbb").state(State.OPEN).putAlias(AliasMetadata.builder("bbb_alias1")))
                     .put(indexBuilder("ccc").state(State.CLOSE).putAlias(AliasMetadata.builder("ccc_alias1")));
             ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, indicesOptions, NONE);
+            IndexNameExpressionResolver.Context context =
+                new IndexNameExpressionResolver.Context(state, indicesOptions, SystemIndexAccessLevel.NONE);
 
             // asking for non existing wildcard pattern should return empty list or exception
             if (indicesOptions.allowNoIndices()) {
@@ -1277,20 +1292,20 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
 
         IndexNameExpressionResolver.Context contextICE =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpenAndForbidClosed(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpenAndForbidClosed(), SystemIndexAccessLevel.NONE);
         expectThrows(IndexClosedException.class, () -> indexNameExpressionResolver.concreteIndexNames(contextICE, "foo1-closed"));
         expectThrows(IndexClosedException.class, () -> indexNameExpressionResolver.concreteIndexNames(contextICE, "foobar1-closed"));
 
         IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true,
                 contextICE.getOptions().allowNoIndices(), contextICE.getOptions().expandWildcardsOpen(),
-            contextICE.getOptions().expandWildcardsClosed(), contextICE.getOptions()), NONE);
+            contextICE.getOptions().expandWildcardsClosed(), contextICE.getOptions()), SystemIndexAccessLevel.NONE);
         String[] results = indexNameExpressionResolver.concreteIndexNames(context, "foo1-closed");
         assertThat(results, emptyArray());
 
         results = indexNameExpressionResolver.concreteIndexNames(context, "foobar1-closed");
         assertThat(results, emptyArray());
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foo1-closed");
         assertThat(results, arrayWithSize(1));
         assertThat(results, arrayContaining("foo1-closed"));
@@ -1300,7 +1315,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         assertThat(results, arrayContaining("foo1-closed"));
 
         // testing an alias pointing to three indices:
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpenAndForbidClosed(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpenAndForbidClosed(),
+            SystemIndexAccessLevel.NONE);
         try {
             indexNameExpressionResolver.concreteIndexNames(context, "foobar2-closed");
             fail("foo2-closed should be closed, but it is open");
@@ -1310,12 +1326,12 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
 
         context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true,
             context.getOptions().allowNoIndices(), context.getOptions().expandWildcardsOpen(),
-            context.getOptions().expandWildcardsClosed(), context.getOptions()), NONE);
+            context.getOptions().expandWildcardsClosed(), context.getOptions()), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foobar2-closed");
         assertThat(results, arrayWithSize(1));
         assertThat(results, arrayContaining("foo3"));
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         results = indexNameExpressionResolver.concreteIndexNames(context, "foobar2-closed");
         assertThat(results, arrayWithSize(3));
         assertThat(results, arrayContainingInAnyOrder("foo1-closed", "foo2-closed", "foo3"));
@@ -1328,7 +1344,8 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         IndicesOptions[] indicesOptions = new IndicesOptions[]{ IndicesOptions.strictExpandOpen(), IndicesOptions.strictExpand(),
                 IndicesOptions.lenientExpandOpen(), IndicesOptions.strictExpandOpenAndForbidClosed()};
         for (IndicesOptions options : indicesOptions) {
-            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options, NONE);
+            IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options,
+                SystemIndexAccessLevel.NONE);
             String[] results = indexNameExpressionResolver.concreteIndexNames(context, "index1", "index1", "alias1");
             assertThat(results, equalTo(new String[]{"index1"}));
         }
@@ -1349,11 +1366,11 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         String[] strings = indexNameExpressionResolver.concreteIndexNames(context, "alias-*");
         assertArrayEquals(new String[] {"test-0"}, strings);
 
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen(), SystemIndexAccessLevel.NONE);
         strings = indexNameExpressionResolver.concreteIndexNames(context, "alias-*");
 
         assertArrayEquals(new String[] {"test-0"}, strings);
@@ -1764,7 +1781,7 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
         Metadata.Builder mdBuilder = Metadata.builder().put(indexBuilder("test"));
         ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
 
         InvalidIndexNameException iine = expectThrows(InvalidIndexNameException.class,
             () -> indexNameExpressionResolver.concreteIndexNames(context, "_foo"));

+ 19 - 15
server/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java

@@ -14,6 +14,7 @@ import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata.State;
 import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.Arrays;
@@ -42,7 +43,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("testXXX"))), equalTo(newHashSet("testXXX")));
         assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "testYYY"))), equalTo(newHashSet("testXXX", "testYYY")));
         assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "ku*"))), equalTo(newHashSet("testXXX", "kuku")));
@@ -71,7 +72,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYY*", "alias*"))),
             equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("-kuku"))), equalTo(newHashSet("-kuku")));
@@ -94,12 +95,14 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
 
         IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state,
-            IndicesOptions.fromOptions(true, true, true, true), NONE);
+            IndicesOptions.fromOptions(true, true, true, true), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("testX*"))),
             equalTo(newHashSet("testXXX", "testXXY", "testXYY")));
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, true), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, true),
+            SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("testX*"))), equalTo(newHashSet("testXYY")));
-        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, false), NONE);
+        context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, false),
+            SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("testX*"))), equalTo(newHashSet("testXXX", "testXXY")));
     }
 
@@ -117,7 +120,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("test*X*"))),
             equalTo(newHashSet("testXXX", "testXXY", "testXYY")));
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("test*X*Y"))), equalTo(newHashSet("testXXY", "testXYY")));
@@ -137,7 +140,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver();
 
         IndexNameExpressionResolver.Context context =
-            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), NONE);
+            new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen(), SystemIndexAccessLevel.NONE);
         assertThat(newHashSet(resolver.resolve(context, Collections.singletonList("_all"))),
             equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
     }
@@ -155,15 +158,15 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         IndicesOptions indicesAndAliasesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false, true, false,
             false, false);
         IndexNameExpressionResolver.Context indicesAndAliasesContext =
-            new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, NONE);
+            new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, SystemIndexAccessLevel.NONE);
         // ignoreAliases option is set, WildcardExpressionResolver throws error when
         IndicesOptions skipAliasesIndicesOptions = IndicesOptions.fromOptions(true, true, true, false, true, false, true, false);
         IndexNameExpressionResolver.Context skipAliasesLenientContext =
-            new IndexNameExpressionResolver.Context(state, skipAliasesIndicesOptions, NONE);
+            new IndexNameExpressionResolver.Context(state, skipAliasesIndicesOptions, SystemIndexAccessLevel.NONE);
         // ignoreAliases option is set, WildcardExpressionResolver resolves the provided expressions only against the defined indices
         IndicesOptions errorOnAliasIndicesOptions = IndicesOptions.fromOptions(false, false, true, false, true, false, true, false);
         IndexNameExpressionResolver.Context skipAliasesStrictContext =
-            new IndexNameExpressionResolver.Context(state, errorOnAliasIndicesOptions, NONE);
+            new IndexNameExpressionResolver.Context(state, errorOnAliasIndicesOptions, SystemIndexAccessLevel.NONE);
 
         {
             List<String> indices = resolver.resolve(indicesAndAliasesContext, Collections.singletonList("foo_a*"));
@@ -230,7 +233,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
             IndicesOptions indicesAndAliasesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false, true, false,
                 false, false);
             IndexNameExpressionResolver.Context indicesAndAliasesContext =
-                new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, NONE);
+                new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, SystemIndexAccessLevel.NONE);
 
             // data streams are not included but expression matches the data stream
             List<String> indices = resolver.resolve(indicesAndAliasesContext, Collections.singletonList("foo_*"));
@@ -245,7 +248,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
             IndicesOptions indicesAndAliasesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false, true, false,
                 false, false);
             IndexNameExpressionResolver.Context indicesAliasesAndDataStreamsContext = new IndexNameExpressionResolver.Context(state,
-                indicesAndAliasesOptions, false, false, true, NONE);
+                indicesAndAliasesOptions, false, false, true, SystemIndexAccessLevel.NONE, NONE, NONE);
 
             // data stream's corresponding backing indices are resolved
             List<String> indices = resolver.resolve(indicesAliasesAndDataStreamsContext, Collections.singletonList("foo_*"));
@@ -264,7 +267,7 @@ public class WildcardExpressionResolverTests extends ESTestCase {
             IndicesOptions indicesAliasesAndExpandHiddenOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false,
                 true, true, false, false, false);
             IndexNameExpressionResolver.Context indicesAliasesDataStreamsAndHiddenIndices = new IndexNameExpressionResolver.Context(state,
-                indicesAliasesAndExpandHiddenOptions, false, false, true, NONE);
+                indicesAliasesAndExpandHiddenOptions, false, false, true, SystemIndexAccessLevel.NONE, NONE, NONE);
 
             // data stream's corresponding backing indices are resolved
             List<String> indices = resolver.resolve(indicesAliasesDataStreamsAndHiddenIndices, Collections.singletonList("foo_*"));
@@ -292,12 +295,13 @@ public class WildcardExpressionResolverTests extends ESTestCase {
         // expressions against the defined indices and aliases
         IndicesOptions indicesAndAliasesOptions = IndicesOptions.fromOptions(false, false, true, false, true, false, false, false);
         IndexNameExpressionResolver.Context indicesAndAliasesContext =
-            new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, NONE);
+            new IndexNameExpressionResolver.Context(state, indicesAndAliasesOptions, SystemIndexAccessLevel.NONE);
 
         // ignoreAliases option is set, WildcardExpressionResolver resolves the provided expressions
         // only against the defined indices
         IndicesOptions onlyIndicesOptions = IndicesOptions.fromOptions(false, false, true, false, true, false, true, false);
-        IndexNameExpressionResolver.Context onlyIndicesContext = new IndexNameExpressionResolver.Context(state, onlyIndicesOptions, NONE);
+        IndexNameExpressionResolver.Context onlyIndicesContext =
+            new IndexNameExpressionResolver.Context(state, onlyIndicesOptions, SystemIndexAccessLevel.NONE);
 
         {
             Set<String> matches = IndexNameExpressionResolver.WildcardExpressionResolver.matches(indicesAndAliasesContext,

+ 2 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/GenerateSnapshotNameStep.java

@@ -18,6 +18,7 @@ import org.elasticsearch.core.Nullable;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.UUIDs;
 import org.elasticsearch.index.Index;
+import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
 
 import java.util.Collections;
 import java.util.List;
@@ -139,7 +140,7 @@ public class GenerateSnapshotNameStep extends ClusterStateActionStep {
         }
 
         public ResolverContext(long startTime) {
-            super(null, null, startTime, false, false, false, false, name -> false);
+            super(null, null, startTime, false, false, false, false, SystemIndexAccessLevel.ALL, name -> false, name -> false);
         }
 
         @Override

+ 4 - 3
x-pack/plugin/fleet/src/javaRestTest/java/org/elasticsearch/xpack/fleet/FleetDataStreamIT.java

@@ -17,7 +17,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.test.SecuritySettingsSourceField;
 import org.elasticsearch.test.rest.ESRestTestCase;
 
-import java.util.Collections;
 import java.util.List;
 
 import static java.util.Collections.emptyList;
@@ -110,9 +109,11 @@ public class FleetDataStreamIT extends ESRestTestCase {
         // The rest of these produce a warning
         RequestOptions consumeWarningsOptions = RequestOptions.DEFAULT.toBuilder()
             .setWarningsHandler(
-                warnings -> Collections.singletonList(
+                warnings -> List.of(
                     "this request accesses system indices: [.fleet-artifacts-7], but "
-                        + "in a future major version, direct access to system indices will be prevented by default"
+                        + "in a future major version, direct access to system indices will be prevented by default",
+                    "this request accesses aliases with names reserved for system indices: [.fleet-artifacts], but in a future major "
+                        + "version, direct access to system indices and their aliases will not be allowed"
                 ).equals(warnings) == false
             )
             .build();

+ 8 - 3
x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java

@@ -231,11 +231,16 @@ public class RestSqlSecurityIT extends SqlSecurityTestCase {
                     if (warnings.isEmpty()) {
                         // No warnings is OK
                         return false;
-                    } else if (warnings.size() > 1) {
+                    } else if (warnings.size() > 2) {
                         return true;
                     } else {
-                        String warning = warnings.get(0);
-                        return warning.startsWith("this request accesses system indices: ") == false;
+                        for (String warning : warnings) {
+                            if (warning.startsWith("this request accesses system indices: ") == false
+                                && warning.startsWith("this request accesses aliases with names reserved for system indices: ") == false) {
+                                return true;
+                            }
+                        }
+                        return false;
                     }
                 });
             }