فهرست منبع

[Transform] introduce new roles and deprecate old ones (#47780)

deprecate data_frame_transforms_{user,admin} roles and introduce transform_{user,admin} roles as replacement
Hendrik Muhs 6 سال پیش
والد
کامیت
d680ca3b3c
12فایلهای تغییر یافته به همراه213 افزوده شده و 103 حذف شده
  1. 7 5
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java
  2. 6 5
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java
  3. 2 0
      x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc
  4. 14 8
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java
  5. 27 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java
  6. 5 1
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/persistence/TransformInternalIndexConstants.java
  7. 95 67
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java
  8. 1 1
      x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/11_builtin.yml
  9. 9 2
      x-pack/plugin/transform/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java
  10. 7 1
      x-pack/plugin/transform/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java
  11. 35 6
      x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java
  12. 5 5
      x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestConstants.java

+ 7 - 5
client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java

@@ -24,8 +24,8 @@ import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.collect.Tuple;
 import org.elasticsearch.common.xcontent.ConstructingObjectParser;
-import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
+import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -299,12 +299,14 @@ public final class Role {
         public static final String NONE = "none";
         public static final String ALL = "all";
         public static final String MONITOR = "monitor";
-        public static final String MONITOR_DATA_FRAME_TRANSFORMS = "monitor_data_frame_transforms";
+        public static final String MONITOR_TRANSFORM_DEPRECATED = "monitor_data_frame_transforms";
+        public static final String MONITOR_TRANSFORM = "monitor_transform";
         public static final String MONITOR_ML = "monitor_ml";
         public static final String MONITOR_WATCHER = "monitor_watcher";
         public static final String MONITOR_ROLLUP = "monitor_rollup";
         public static final String MANAGE = "manage";
-        public static final String MANAGE_DATA_FRAME_TRANSFORMS = "manage_data_frame_transforms";
+        public static final String MANAGE_TRANSFORM_DEPRECATED = "manage_data_frame_transforms";
+        public static final String MANAGE_TRANSFORM = "manage_transform";
         public static final String MANAGE_ML = "manage_ml";
         public static final String MANAGE_WATCHER = "manage_watcher";
         public static final String MANAGE_ROLLUP = "manage_rollup";
@@ -320,8 +322,8 @@ public final class Role {
         public static final String READ_CCR = "read_ccr";
         public static final String MANAGE_ILM = "manage_ilm";
         public static final String READ_ILM = "read_ilm";
-        public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_DATA_FRAME_TRANSFORMS, MONITOR_ML,
-            MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, MANAGE_DATA_FRAME_TRANSFORMS,
+        public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_TRANSFORM_DEPRECATED, MONITOR_TRANSFORM,
+            MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, MANAGE_TRANSFORM_DEPRECATED, MANAGE_TRANSFORM,
             MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT,
             MANAGE_SECURITY, MANAGE_SAML, MANAGE_OIDC, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM};
     }

+ 6 - 5
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java

@@ -28,6 +28,7 @@ import org.elasticsearch.client.ESRestHighLevelClientTestCase;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.RestHighLevelClient;
 import org.elasticsearch.client.security.AuthenticateResponse;
+import org.elasticsearch.client.security.AuthenticateResponse.RealmInfo;
 import org.elasticsearch.client.security.ChangePasswordRequest;
 import org.elasticsearch.client.security.ClearRealmCacheRequest;
 import org.elasticsearch.client.security.ClearRealmCacheResponse;
@@ -79,7 +80,6 @@ import org.elasticsearch.client.security.PutUserRequest;
 import org.elasticsearch.client.security.PutUserResponse;
 import org.elasticsearch.client.security.RefreshPolicy;
 import org.elasticsearch.client.security.TemplateRoleName;
-import org.elasticsearch.client.security.AuthenticateResponse.RealmInfo;
 import org.elasticsearch.client.security.support.ApiKey;
 import org.elasticsearch.client.security.support.CertificateInfo;
 import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;
@@ -99,8 +99,6 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.util.set.Sets;
 import org.hamcrest.Matchers;
 
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
@@ -121,6 +119,9 @@ import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
@@ -681,8 +682,8 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
 
             List<Role> roles = response.getRoles();
             assertNotNull(response);
-            // 27 system roles plus the three we created
-            assertThat(roles.size(), equalTo(30));
+            // 29 system roles plus the three we created
+            assertThat(roles.size(), equalTo(32));
         }
 
         {

+ 2 - 0
x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc

@@ -80,11 +80,13 @@ A successful call returns an object with "cluster" and "index" fields.
     "manage_security",
     "manage_slm",
     "manage_token",
+    "manage_transform",
     "manage_watcher",
     "monitor",
     "monitor_data_frame_transforms",
     "monitor_ml",
     "monitor_rollup",
+    "monitor_transform",
     "monitor_watcher",
     "none",
     "read_ccr",

+ 14 - 8
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java

@@ -50,12 +50,12 @@ public class ClusterPrivilegeResolver {
     private static final Set<String> MANAGE_API_KEY_PATTERN = Set.of("cluster:admin/xpack/security/api_key/*");
     private static final Set<String> MONITOR_PATTERN = Set.of("cluster:monitor/*");
     private static final Set<String> MONITOR_ML_PATTERN = Set.of("cluster:monitor/xpack/ml/*");
-    private static final Set<String> MONITOR_DATA_FRAME_PATTERN = Set.of("cluster:monitor/data_frame/*", "cluster:monitor/transform/*");
+    private static final Set<String> MONITOR_TRANSFORM_PATTERN = Set.of("cluster:monitor/data_frame/*", "cluster:monitor/transform/*");
     private static final Set<String> MONITOR_WATCHER_PATTERN = Set.of("cluster:monitor/xpack/watcher/*");
     private static final Set<String> MONITOR_ROLLUP_PATTERN = Set.of("cluster:monitor/xpack/rollup/*");
     private static final Set<String> ALL_CLUSTER_PATTERN = Set.of("cluster:*", "indices:admin/template/*");
     private static final Set<String> MANAGE_ML_PATTERN = Set.of("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*");
-    private static final Set<String> MANAGE_DATA_FRAME_PATTERN = Set.of("cluster:admin/data_frame/*", "cluster:monitor/data_frame/*",
+    private static final Set<String> MANAGE_TRANSFORM_PATTERN = Set.of("cluster:admin/data_frame/*", "cluster:monitor/data_frame/*",
             "cluster:monitor/transform/*", "cluster:admin/transform/*");
     private static final Set<String> MANAGE_WATCHER_PATTERN = Set.of("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*");
     private static final Set<String> TRANSPORT_CLIENT_PATTERN = Set.of("cluster:monitor/nodes/liveness", "cluster:monitor/state");
@@ -77,14 +77,18 @@ public class ClusterPrivilegeResolver {
     public static final NamedClusterPrivilege ALL = new ActionClusterPrivilege("all", ALL_CLUSTER_PATTERN);
     public static final NamedClusterPrivilege MONITOR = new ActionClusterPrivilege("monitor", MONITOR_PATTERN);
     public static final NamedClusterPrivilege MONITOR_ML = new ActionClusterPrivilege("monitor_ml", MONITOR_ML_PATTERN);
-    public static final NamedClusterPrivilege MONITOR_DATA_FRAME =
-        new ActionClusterPrivilege("monitor_data_frame_transforms", MONITOR_DATA_FRAME_PATTERN);
+    public static final NamedClusterPrivilege MONITOR_TRANSFORM_DEPRECATED =
+        new ActionClusterPrivilege("monitor_data_frame_transforms", MONITOR_TRANSFORM_PATTERN);
+    public static final NamedClusterPrivilege MONITOR_TRANSFORM =
+            new ActionClusterPrivilege("monitor_transform", MONITOR_TRANSFORM_PATTERN);
     public static final NamedClusterPrivilege MONITOR_WATCHER = new ActionClusterPrivilege("monitor_watcher", MONITOR_WATCHER_PATTERN);
     public static final NamedClusterPrivilege MONITOR_ROLLUP = new ActionClusterPrivilege("monitor_rollup", MONITOR_ROLLUP_PATTERN);
     public static final NamedClusterPrivilege MANAGE = new ActionClusterPrivilege("manage", ALL_CLUSTER_PATTERN, ALL_SECURITY_PATTERN);
     public static final NamedClusterPrivilege MANAGE_ML = new ActionClusterPrivilege("manage_ml", MANAGE_ML_PATTERN);
-    public static final NamedClusterPrivilege MANAGE_DATA_FRAME =
-        new ActionClusterPrivilege("manage_data_frame_transforms", MANAGE_DATA_FRAME_PATTERN);
+    public static final NamedClusterPrivilege MANAGE_TRANSFORM_DEPRECATED =
+        new ActionClusterPrivilege("manage_data_frame_transforms", MANAGE_TRANSFORM_PATTERN);
+    public static final NamedClusterPrivilege MANAGE_TRANSFORM =
+            new ActionClusterPrivilege("manage_transform", MANAGE_TRANSFORM_PATTERN);
     public static final NamedClusterPrivilege MANAGE_TOKEN = new ActionClusterPrivilege("manage_token", MANAGE_TOKEN_PATTERN);
     public static final NamedClusterPrivilege MANAGE_WATCHER = new ActionClusterPrivilege("manage_watcher", MANAGE_WATCHER_PATTERN);
     public static final NamedClusterPrivilege MANAGE_ROLLUP = new ActionClusterPrivilege("manage_rollup", MANAGE_ROLLUP_PATTERN);
@@ -118,12 +122,14 @@ public class ClusterPrivilegeResolver {
         ALL,
         MONITOR,
         MONITOR_ML,
-        MONITOR_DATA_FRAME,
+        MONITOR_TRANSFORM_DEPRECATED,
+        MONITOR_TRANSFORM,
         MONITOR_WATCHER,
         MONITOR_ROLLUP,
         MANAGE,
         MANAGE_ML,
-        MANAGE_DATA_FRAME,
+        MANAGE_TRANSFORM_DEPRECATED,
+        MANAGE_TRANSFORM,
         MANAGE_TOKEN,
         MANAGE_WATCHER,
         MANAGE_IDX_TEMPLATES,

+ 27 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java

@@ -17,6 +17,7 @@ import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableCluster
 import org.elasticsearch.xpack.core.security.support.MetadataUtils;
 import org.elasticsearch.xpack.core.security.user.KibanaUser;
 import org.elasticsearch.xpack.core.security.user.UsernamesField;
+import org.elasticsearch.xpack.core.transform.transforms.persistence.TransformInternalIndexConstants;
 import org.elasticsearch.xpack.core.watcher.execution.TriggeredWatchStoreField;
 import org.elasticsearch.xpack.core.watcher.history.HistoryStoreField;
 import org.elasticsearch.xpack.core.watcher.watch.Watch;
@@ -179,28 +180,52 @@ public class ReservedRolesStore implements BiConsumer<Set<String>, ActionListene
                                 .application("kibana-*").resources("*").privileges("reserved_ml").build()
                         },
                         null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
+                // DEPRECATED: to be removed in 9.0.0
                 .put("data_frame_transforms_admin", new RoleDescriptor("data_frame_transforms_admin",
                         new String[] { "manage_data_frame_transforms" },
                         new RoleDescriptor.IndicesPrivileges[]{
                             RoleDescriptor.IndicesPrivileges.builder()
-                                .indices(".data-frame-notifications*")
+                                .indices(TransformInternalIndexConstants.AUDIT_INDEX_PATTERN,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS)
                                 .privileges("view_index_metadata", "read").build()
                         },
                         new RoleDescriptor.ApplicationResourcePrivileges[] {
                             RoleDescriptor.ApplicationResourcePrivileges.builder()
                                 .application("kibana-*").resources("*").privileges("reserved_ml").build()
                         }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
+                // DEPRECATED: to be removed in 9.0.0
                 .put("data_frame_transforms_user", new RoleDescriptor("data_frame_transforms_user",
                         new String[] { "monitor_data_frame_transforms" },
                         new RoleDescriptor.IndicesPrivileges[]{
                             RoleDescriptor.IndicesPrivileges.builder()
-                                .indices(".data-frame-notifications*")
+                                .indices(TransformInternalIndexConstants.AUDIT_INDEX_PATTERN,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS)
                                 .privileges("view_index_metadata", "read").build()
                         },
                         new RoleDescriptor.ApplicationResourcePrivileges[] {
                             RoleDescriptor.ApplicationResourcePrivileges.builder()
                                 .application("kibana-*").resources("*").privileges("reserved_ml").build()
                         }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
+                .put("transform_admin", new RoleDescriptor("transform_admin",
+                        new String[] { "manage_transform" },
+                        new RoleDescriptor.IndicesPrivileges[]{
+                            RoleDescriptor.IndicesPrivileges.builder()
+                                .indices(TransformInternalIndexConstants.AUDIT_INDEX_PATTERN,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED,
+                                         TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS)
+                                .privileges("view_index_metadata", "read").build()
+                        }, null, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
+                .put("transform_user", new RoleDescriptor("transform_user",
+                        new String[] { "monitor_transform" },
+                        new RoleDescriptor.IndicesPrivileges[]{
+                            RoleDescriptor.IndicesPrivileges.builder()
+                                 .indices(TransformInternalIndexConstants.AUDIT_INDEX_PATTERN,
+                                          TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED,
+                                          TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS)
+                                .privileges("view_index_metadata", "read").build()
+                        }, null, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
                 .put("watcher_admin", new RoleDescriptor("watcher_admin", new String[] { "manage_watcher" },
                         new RoleDescriptor.IndicesPrivileges[] {
                                 RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX, TriggeredWatchStoreField.INDEX_NAME,

+ 5 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/persistence/TransformInternalIndexConstants.java

@@ -30,7 +30,11 @@ public final class TransformInternalIndexConstants {
 
     // audit index
     public static final String AUDIT_TEMPLATE_VERSION = "1";
-    public static final String AUDIT_INDEX_PREFIX = ".data-frame-notifications-";
+    public static final String AUDIT_INDEX_PREFIX = ".transform-notifications-";
+    public static final String AUDIT_INDEX_PATTERN = AUDIT_INDEX_PREFIX + "*";
+    public static final String AUDIT_INDEX_PATTERN_DEPRECATED = ".data-frame-notifications-*";
+
+    public static final String AUDIT_INDEX_READ_ALIAS = ".transform-notifications-read";
     public static final String AUDIT_INDEX = AUDIT_INDEX_PREFIX + AUDIT_TEMPLATE_VERSION;
 
     private TransformInternalIndexConstants() {

+ 95 - 67
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java

@@ -186,6 +186,8 @@ public class ReservedRolesStoreTests extends ESTestCase {
         assertThat(ReservedRolesStore.isReserved("machine_learning_admin"), is(true));
         assertThat(ReservedRolesStore.isReserved("data_frame_transforms_user"), is(true));
         assertThat(ReservedRolesStore.isReserved("data_frame_transforms_admin"), is(true));
+        assertThat(ReservedRolesStore.isReserved("transform_user"), is(true));
+        assertThat(ReservedRolesStore.isReserved("transform_admin"), is(true));
         assertThat(ReservedRolesStore.isReserved("watcher_user"), is(true));
         assertThat(ReservedRolesStore.isReserved("watcher_admin"), is(true));
         assertThat(ReservedRolesStore.isReserved("kibana_dashboard_only_user"), is(true));
@@ -1121,82 +1123,108 @@ public class ReservedRolesStoreTests extends ESTestCase {
             new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml"), "*"), is(false));
     }
 
-    public void testDataFrameTransformsAdminRole() {
+    public void testTransformAdminRole() {
         final TransportRequest request = mock(TransportRequest.class);
         final Authentication authentication = mock(Authentication.class);
 
-        RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("data_frame_transforms_admin");
-        assertNotNull(roleDescriptor);
-        assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
-
-        Role role = Role.builder(roleDescriptor, null).build();
-        assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(PreviewTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(PutTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(StartTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(StopTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));
-
-        assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
-
-        assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX);
-        assertNoAccessAllowed(role, "foo");
-        assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only
-
-        assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
-
-        final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false));
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml"), "*"), is(true));
-
-        final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24);
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false));
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml"), "*"), is(false));
+        RoleDescriptor[] roleDescriptors = {
+            new ReservedRolesStore().roleDescriptor("data_frame_transforms_admin"),
+            new ReservedRolesStore().roleDescriptor("transform_admin")
+        };
+
+        for (RoleDescriptor roleDescriptor : roleDescriptors) {
+            assertNotNull(roleDescriptor);
+            assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
+
+            Role role = Role.builder(roleDescriptor, null).build();
+            assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(PreviewTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(PutTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(StartTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(StopTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));
+
+            assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
+
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS);
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_PATTERN);
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED);
+            assertNoAccessAllowed(role, "foo");
+            assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only
+
+            assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
+
+            final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
+            assertThat(role.application().grants(
+                new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false));
+
+            if (roleDescriptor.getName().equals("data_frame_transforms_admin")) {
+                assertThat(role.application()
+                    .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml"), "*"), is(true));
+            }
+
+            final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24);
+            assertThat(role.application().grants(
+                new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false));
+            if (roleDescriptor.getName().equals("data_frame_transforms_admin")) {
+                assertThat(role.application().grants(
+                        new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml"), "*"), is(false));
+            }
+        }
     }
 
     public void testDataFrameTransformsUserRole() {
         final TransportRequest request = mock(TransportRequest.class);
         final Authentication authentication = mock(Authentication.class);
 
-        RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("data_frame_transforms_user");
-        assertNotNull(roleDescriptor);
-        assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
-
-        Role role = Role.builder(roleDescriptor, null).build();
-        assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(false));
-        assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true));
-        assertThat(role.cluster().check(PreviewTransformAction.NAME, request, authentication), is(false));
-        assertThat(role.cluster().check(PutTransformAction.NAME, request, authentication), is(false));
-        assertThat(role.cluster().check(StartTransformAction.NAME, request, authentication), is(false));
-        assertThat(role.cluster().check(StopTransformAction.NAME, request, authentication), is(false));
-        assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));
-
-        assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
-
-        assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX);
-        assertNoAccessAllowed(role, "foo");
-        assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME);
-
-        assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
-
-        final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false));
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml"), "*"), is(true));
-
-        final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24);
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false));
-        assertThat(role.application().grants(
-            new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml"), "*"), is(false));
+        RoleDescriptor[] roleDescriptors = {
+            new ReservedRolesStore().roleDescriptor("data_frame_transforms_user"),
+            new ReservedRolesStore().roleDescriptor("transform_user")
+        };
+
+        for (RoleDescriptor roleDescriptor : roleDescriptors) {
+            assertNotNull(roleDescriptor);
+            assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
+
+            Role role = Role.builder(roleDescriptor, null).build();
+            assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(false));
+            assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true));
+            assertThat(role.cluster().check(PreviewTransformAction.NAME, request, authentication), is(false));
+            assertThat(role.cluster().check(PutTransformAction.NAME, request, authentication), is(false));
+            assertThat(role.cluster().check(StartTransformAction.NAME, request, authentication), is(false));
+            assertThat(role.cluster().check(StopTransformAction.NAME, request, authentication), is(false));
+            assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));
+
+            assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
+
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_READ_ALIAS);
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_PATTERN);
+            assertOnlyReadAllowed(role, TransformInternalIndexConstants.AUDIT_INDEX_PATTERN_DEPRECATED);
+            assertNoAccessAllowed(role, "foo");
+            assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME);
+
+            assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
+
+            final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
+            assertThat(role.application().grants(
+                new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false));
+
+            if (roleDescriptor.getName().equals("data_frame_transforms_user")) {
+                assertThat(role.application().grants(
+                    new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml"), "*"), is(true));
+            }
+
+            final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24);
+            assertThat(role.application().grants(
+                new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false));
+            if (roleDescriptor.getName().equals("data_frame_transforms_user")) {
+                assertThat(role.application().grants(
+                    new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml"), "*"), is(false));
+            }
+        }
     }
 
     public void testWatcherAdminRole() {

+ 1 - 1
x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/11_builtin.yml

@@ -15,5 +15,5 @@ setup:
   # This is fragile - it needs to be updated every time we add a new cluster/index privilege
   # I would much prefer we could just check that specific entries are in the array, but we don't have
   # an assertion for that
-  - length: { "cluster" : 30 }
+  - length: { "cluster" : 32 }
   - length: { "index" : 17 }

+ 9 - 2
x-pack/plugin/transform/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java

@@ -53,8 +53,15 @@ public class TransformGetAndGetStatsIT extends TransformRestTestCase {
 
         createReviewsIndex();
         indicesCreated = true;
-        setupUser(TEST_USER_NAME, Collections.singletonList("data_frame_transforms_user"));
-        setupUser(TEST_ADMIN_USER_NAME, Collections.singletonList("data_frame_transforms_admin"));
+
+        // at random test the old deprecated roles, to be removed in 9.0.0
+        if (randomBoolean()) {
+            setupUser(TEST_USER_NAME, Collections.singletonList("transform_user"));
+            setupUser(TEST_ADMIN_USER_NAME, Collections.singletonList("transform_admin"));
+        } else {
+            setupUser(TEST_USER_NAME, Collections.singletonList("data_frame_transforms_user"));
+            setupUser(TEST_ADMIN_USER_NAME, Collections.singletonList("data_frame_transforms_admin"));
+        }
     }
 
     @After

+ 7 - 1
x-pack/plugin/transform/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java

@@ -50,7 +50,13 @@ public class TransformPivotRestIT extends TransformRestTestCase {
         createReviewsIndex();
         indicesCreated = true;
         setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME);
-        setupUser(TEST_USER_NAME, Arrays.asList("data_frame_transforms_admin", DATA_ACCESS_ROLE));
+
+        // at random test the old deprecated roles, to be removed in 9.0.0
+        if (randomBoolean()) {
+            setupUser(TEST_USER_NAME, Arrays.asList("transform_admin", DATA_ACCESS_ROLE));
+        } else {
+            setupUser(TEST_USER_NAME, Arrays.asList("data_frame_transforms_admin", DATA_ACCESS_ROLE));
+        }
     }
 
     public void testSimplePivot() throws Exception {

+ 35 - 6
x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java

@@ -25,6 +25,7 @@ import org.elasticsearch.client.transform.transforms.pivot.PivotConfig;
 import org.elasticsearch.client.transform.transforms.pivot.TermsGroupSource;
 import org.elasticsearch.common.Booleans;
 import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.io.Streams;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.DeprecationHandler;
@@ -35,20 +36,25 @@ import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.common.xcontent.support.XContentMapValues;
 import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.search.aggregations.AggregatorFactories;
-import org.elasticsearch.xpack.test.rest.XPackRestTestConstants;
+import org.junit.Before;
 
 import java.io.IOException;
 import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.xpack.test.rest.XPackRestTestConstants.TRANSFORM_INTERNAL_INDEX_PREFIX;
+import static org.elasticsearch.xpack.test.rest.XPackRestTestConstants.TRANSFORM_INTERNAL_INDEX_PREFIX_DEPRECATED;
+import static org.elasticsearch.xpack.test.rest.XPackRestTestConstants.TRANSFORM_NOTIFICATIONS_INDEX_PREFIX;
+import static org.elasticsearch.xpack.test.rest.XPackRestTestConstants.TRANSFORM_NOTIFICATIONS_INDEX_PREFIX_DEPRECATED;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@@ -71,10 +77,33 @@ public class TransformSurvivesUpgradeIT extends AbstractUpgradeTestCase {
         .map(TimeValue::timeValueMinutes)
         .collect(Collectors.toList());
 
-    @Override
-    protected Collection<String> templatesToWaitFor() {
-        return Stream.concat(XPackRestTestConstants.DATA_FRAME_TEMPLATES.stream(),
-            super.templatesToWaitFor().stream()).collect(Collectors.toSet());
+    @Before
+    public void waitForTemplates() throws Exception {
+        assertBusy(() -> {
+            final Request catRequest = new Request("GET", "_cat/templates?h=n&s=n");
+            final Response catResponse = adminClient().performRequest(catRequest);
+
+            final SortedSet<String> templates = new TreeSet<>(Streams.readAllLines(catResponse.getEntity().getContent()));
+
+            // match templates, independent of the version, at least 2 should exist
+            SortedSet<String> internalDeprecated = templates.tailSet(TRANSFORM_INTERNAL_INDEX_PREFIX_DEPRECATED);
+            SortedSet<String> internal = templates.tailSet(TRANSFORM_INTERNAL_INDEX_PREFIX);
+            SortedSet<String> notificationsDeprecated = templates
+                    .tailSet(TRANSFORM_NOTIFICATIONS_INDEX_PREFIX_DEPRECATED);
+            SortedSet<String> notifications = templates.tailSet(TRANSFORM_NOTIFICATIONS_INDEX_PREFIX);
+
+            int foundTemplates = 0;
+            foundTemplates += internalDeprecated.isEmpty() ? 0
+                    : internalDeprecated.first().startsWith(TRANSFORM_INTERNAL_INDEX_PREFIX_DEPRECATED) ? 1 : 0;
+            foundTemplates += internal.isEmpty() ? 0 : internal.first().startsWith(TRANSFORM_INTERNAL_INDEX_PREFIX) ? 1 : 0;
+            foundTemplates += notificationsDeprecated.isEmpty() ? 0
+                    : notificationsDeprecated.first().startsWith(TRANSFORM_NOTIFICATIONS_INDEX_PREFIX_DEPRECATED) ? 1 : 0;
+            foundTemplates += notifications.isEmpty() ? 0 : notifications.first().startsWith(TRANSFORM_NOTIFICATIONS_INDEX_PREFIX) ? 1 : 0;
+
+            if (foundTemplates < 2) {
+                fail("Transform index templates not found. The templates that exist are: " + templates);
+            }
+        });
     }
 
     protected static void waitForPendingDataFrameTasks() throws Exception {

+ 5 - 5
x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestConstants.java

@@ -33,11 +33,11 @@ public final class XPackRestTestConstants {
             RESULTS_INDEX_PREFIX,
             CONFIG_INDEX);
 
-    // Data Frame constants:
-    public static final String DATA_FRAME_INTERNAL_INDEX = ".data-frame-internal-2";
-    public static final String DATA_FRAME_NOTIFICATIONS_INDEX = ".data-frame-notifications-1";
-
-    public static final List<String> DATA_FRAME_TEMPLATES = List.of(DATA_FRAME_INTERNAL_INDEX, DATA_FRAME_NOTIFICATIONS_INDEX);
+    // Transform constants:
+    public static final String TRANSFORM_INTERNAL_INDEX_PREFIX = ".transform-internal-";
+    public static final String TRANSFORM_NOTIFICATIONS_INDEX_PREFIX = ".transform-notifications-";
+    public static final String TRANSFORM_INTERNAL_INDEX_PREFIX_DEPRECATED = ".data-frame-internal-";
+    public static final String TRANSFORM_NOTIFICATIONS_INDEX_PREFIX_DEPRECATED = ".data-frame-notifications-";
 
     private XPackRestTestConstants() {
     }