diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index 0ccd1aabeba..c6dc6910d97 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -315,9 +315,11 @@ public final class Role { public static final String MANAGE_PIPELINE = "manage_pipeline"; public static final String MANAGE_CCR = "manage_ccr"; 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_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT, - MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR }; + MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM }; } /** @@ -338,8 +340,9 @@ public final class Role { public static final String CREATE_INDEX = "create_index"; public static final String VIEW_INDEX_METADATA = "view_index_metadata"; public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index"; + public static final String MANAGE_ILM = "manage_ilm"; public static final String[] ALL_ARRAY = new String[] { NONE, ALL, READ, READ_CROSS, CREATE, INDEX, DELETE, WRITE, MONITOR, MANAGE, - DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX }; + DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX, MANAGE_ILM }; } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java index 9c03941b031..fba595e7a09 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java @@ -9,6 +9,8 @@ import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.GetStatusAction; import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction; import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction; import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction; @@ -47,6 +49,8 @@ public final class ClusterPrivilege extends Privilege { private static final Automaton MANAGE_CCR_AUTOMATON = patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME); private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME); + private static final Automaton MANAGE_ILM_AUTOMATON = patterns("cluster:admin/ilm/*"); + private static final Automaton READ_ILM_AUTOMATON = patterns(GetLifecycleAction.NAME, GetStatusAction.NAME); public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY); public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON); @@ -69,6 +73,8 @@ public final class ClusterPrivilege extends Privilege { public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*"); public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON); public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON); + public static final ClusterPrivilege MANAGE_ILM = new ClusterPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON); + public static final ClusterPrivilege READ_ILM = new ClusterPrivilege("read_ilm", READ_ILM_AUTOMATON); public static final Predicate ACTION_MATCHER = ClusterPrivilege.ALL.predicate(); @@ -92,6 +98,8 @@ public final class ClusterPrivilege extends Privilege { .put("manage_rollup", MANAGE_ROLLUP) .put("manage_ccr", MANAGE_CCR) .put("read_ccr", READ_CCR) + .put("manage_ilm", MANAGE_ILM) + .put("read_ilm", READ_ILM) .immutableMap(); private static final ConcurrentHashMap, ClusterPrivilege> CACHE = new ConcurrentHashMap<>(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java index 6281fbb2c8f..d6549df5f9d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; import org.elasticsearch.xpack.core.ccr.action.UnfollowAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.security.support.Automatons; import java.util.Arrays; @@ -57,9 +58,11 @@ public final class IndexPrivilege extends Privilege { private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME); private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME, GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME, - ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME); + ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME, + ExplainLifecycleAction.NAME); private static final Automaton MANAGE_FOLLOW_INDEX_AUTOMATON = patterns(PutFollowAction.NAME, UnfollowAction.NAME, CloseIndexAction.NAME); + private static final Automaton MANAGE_ILM_AUTOMATON = patterns("indices:admin/ilm/*"); public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY); public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON); @@ -75,6 +78,7 @@ public final class IndexPrivilege extends Privilege { public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON); public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON); public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", MANAGE_FOLLOW_INDEX_AUTOMATON); + public static final IndexPrivilege MANAGE_ILM = new IndexPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON); private static final Map VALUES = MapBuilder.newMapBuilder() .put("none", NONE) @@ -91,6 +95,7 @@ public final class IndexPrivilege extends Privilege { .put("view_index_metadata", VIEW_METADATA) .put("read_cross_cluster", READ_CROSS_CLUSTER) .put("manage_follow_index", MANAGE_FOLLOW_INDEX) + .put("manage_ilm", MANAGE_ILM) .immutableMap(); public static final Predicate ACTION_MATCHER = ALL.predicate(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java index f2bfd8d2d8e..58432cdf6c7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java @@ -138,4 +138,57 @@ public class PrivilegeTests extends ESTestCase { assertThat(predicate.test("cluster:admin/xpack/whatever"), is(false)); } + public void testIlmPrivileges() { + { + Predicate predicate = ClusterPrivilege.MANAGE_ILM.predicate(); + // check cluster actions + assertThat(predicate.test("cluster:admin/ilm/delete"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/put"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/start"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/stop"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/get"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true)); + // check non-ilm action + assertThat(predicate.test("cluster:admin/whatever"), is(false)); + } + + { + Predicate predicate = ClusterPrivilege.READ_ILM.predicate(); + // check cluster actions + assertThat(predicate.test("cluster:admin/ilm/delete"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/put"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/start"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/stop"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/get"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true)); + // check non-ilm action + assertThat(predicate.test("cluster:admin/whatever"), is(false)); + } + + { + Predicate predicate = IndexPrivilege.MANAGE_ILM.predicate(); + // check indices actions + assertThat(predicate.test("indices:admin/ilm/retry"), is(true)); + assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(true)); + assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(true)); + assertThat(predicate.test("indices:admin/ilm/explain"), is(true)); + // check non-ilm action + assertThat(predicate.test("indices:admin/whatever"), is(false)); + } + + { + Predicate predicate = IndexPrivilege.VIEW_METADATA.predicate(); + // check indices actions + assertThat(predicate.test("indices:admin/ilm/retry"), is(false)); + assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(false)); + assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(false)); + assertThat(predicate.test("indices:admin/ilm/explain"), is(true)); + // check non-ilm action + assertThat(predicate.test("indices:admin/whatever"), is(false)); + } + } } diff --git a/x-pack/plugin/ilm/qa/with-security/roles.yml b/x-pack/plugin/ilm/qa/with-security/roles.yml index baf89bea345..64d437bbfd2 100644 --- a/x-pack/plugin/ilm/qa/with-security/roles.yml +++ b/x-pack/plugin/ilm/qa/with-security/roles.yml @@ -1,8 +1,11 @@ ilm: cluster: - monitor - - manage + - manage_ilm indices: + - names: [ 'view-only-*' ] + privileges: + - view_index_metadata - names: [ 'ilm-*' ] privileges: - monitor diff --git a/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java b/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java index 01eb07bb35b..a0c21f4614d 100644 --- a/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java +++ b/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java @@ -119,6 +119,13 @@ public class PermissionsIT extends ESRestTestCase { }); } + public void testCanViewExplainOnUnmanagedIndex() throws Exception { + createIndexAsAdmin("view-only-ilm", indexSettingsWithPolicy, ""); + Request request = new Request("GET", "/view-only-ilm/_ilm/explain"); + // test_ilm user has permissions to view + assertOK(client().performRequest(request)); + } + private void createNewSingletonPolicy(String policy, String phaseName, LifecycleAction action) throws IOException { Phase phase = new Phase(phaseName, TimeValue.ZERO, singletonMap(action.getWriteableName(), action)); LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase));