From 9ae3cd2a78bc1fd1a2728910f2408965279c313a Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 6 Jan 2020 13:15:55 +0200 Subject: [PATCH] Add 'monitor_snapshot' cluster privilege (#50489) (#50647) This adds a new cluster privilege `monitor_snapshot` which is a restricted version of `create_snapshot`, granting the same privileges to view snapshot and repository info and status but not granting the actual privilege to create a snapshot. Co-authored-by: j-bean --- .../security/get-builtin-privileges.asciidoc | 1 + .../authorization/privileges.asciidoc | 3 ++ .../privilege/ClusterPrivilegeResolver.java | 4 +++ .../integration/ClusterPrivilegeTests.java | 33 +++++++++++++++++-- .../test/privileges/11_builtin.yml | 2 +- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc b/x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc index cedffe7a6f9..e5af329b638 100644 --- a/x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc +++ b/x-pack/docs/en/rest-api/security/get-builtin-privileges.asciidoc @@ -87,6 +87,7 @@ A successful call returns an object with "cluster" and "index" fields. "monitor_data_frame_transforms", "monitor_ml", "monitor_rollup", + "monitor_snapshot", "monitor_transform", "monitor_watcher", "none", diff --git a/x-pack/docs/en/security/authorization/privileges.asciidoc b/x-pack/docs/en/security/authorization/privileges.asciidoc index 5808486a98b..af73d0404b1 100644 --- a/x-pack/docs/en/security/authorization/privileges.asciidoc +++ b/x-pack/docs/en/security/authorization/privileges.asciidoc @@ -16,6 +16,9 @@ settings update, rerouting, or managing users and roles. Privileges to create snapshots for existing repositories. Can also list and view details on existing repositories and snapshots. +`monitor_snapshot`:: +Privileges to list and view details on existing repositories and snapshots. + `manage`:: Builds on `monitor` and adds cluster operations that change values in the cluster. This includes snapshotting, updating settings, and rerouting. It also includes diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 920d0e6e22f..3276cb72b98 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -75,6 +75,8 @@ public class ClusterPrivilegeResolver { private static final Set CREATE_SNAPSHOT_PATTERN = Collections.unmodifiableSet( Sets.newHashSet(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*", GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME)); + private static final Set MONITOR_SNAPSHOT_PATTERN = Collections.unmodifiableSet(Sets.newHashSet( + SnapshotsStatusAction.NAME + "*", GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME)); private static final Set READ_CCR_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(ClusterStateAction.NAME, HasPrivilegesAction.NAME)); private static final Set MANAGE_ILM_PATTERN = Collections.singleton("cluster:admin/ilm/*"); @@ -121,6 +123,7 @@ public class ClusterPrivilegeResolver { public static final NamedClusterPrivilege MANAGE_CCR = new ActionClusterPrivilege("manage_ccr", MANAGE_CCR_PATTERN); public static final NamedClusterPrivilege READ_CCR = new ActionClusterPrivilege("read_ccr", READ_CCR_PATTERN); public static final NamedClusterPrivilege CREATE_SNAPSHOT = new ActionClusterPrivilege("create_snapshot", CREATE_SNAPSHOT_PATTERN); + public static final NamedClusterPrivilege MONITOR_SNAPSHOT = new ActionClusterPrivilege("monitor_snapshot", MONITOR_SNAPSHOT_PATTERN); public static final NamedClusterPrivilege MANAGE_ILM = new ActionClusterPrivilege("manage_ilm", MANAGE_ILM_PATTERN); public static final NamedClusterPrivilege READ_ILM = new ActionClusterPrivilege("read_ilm", READ_ILM_PATTERN); public static final NamedClusterPrivilege MANAGE_SLM = new ActionClusterPrivilege("manage_slm", MANAGE_SLM_PATTERN); @@ -159,6 +162,7 @@ public class ClusterPrivilegeResolver { MANAGE_CCR, READ_CCR, CREATE_SNAPSHOT, + MONITOR_SNAPSHOT, MANAGE_ILM, READ_ILM, MANAGE_SLM, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/ClusterPrivilegeTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/ClusterPrivilegeTests.java index 384401edaf5..42f20c06bc6 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/ClusterPrivilegeTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/ClusterPrivilegeTests.java @@ -36,13 +36,17 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { " - names: 'someindex'\n" + " privileges: [ all ]\n" + "role_d:\n" + - " cluster: [ create_snapshot ]\n"; + " cluster: [ create_snapshot ]\n" + + "\n" + + "role_e:\n" + + " cluster: [ monitor_snapshot]\n"; private static final String USERS_ROLES = "role_a:user_a\n" + "role_b:user_b\n" + "role_c:user_c\n" + - "role_d:user_d\n"; + "role_d:user_d\n" + + "role_e:user_e\n"; private static Path repositoryLocation; @@ -81,7 +85,8 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { "user_a:" + usersPasswdHashed + "\n" + "user_b:" + usersPasswdHashed + "\n" + "user_c:" + usersPasswdHashed + "\n" + - "user_d:" + usersPasswdHashed + "\n"; + "user_d:" + usersPasswdHashed + "\n" + + "user_e:" + usersPasswdHashed + "\n"; } @Override @@ -139,6 +144,19 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { assertAccessIsDenied("user_d", "GET", "/_nodes/infos"); assertAccessIsDenied("user_d", "POST", "/_cluster/reroute"); assertAccessIsDenied("user_d", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); + + // user_e can view repos and snapshots on existing repos, everything else is DENIED + assertAccessIsDenied("user_e", "GET", "/_cluster/state"); + assertAccessIsDenied("user_e", "GET", "/_cluster/health"); + assertAccessIsDenied("user_e", "GET", "/_cluster/settings"); + assertAccessIsDenied("user_e", "GET", "/_cluster/stats"); + assertAccessIsDenied("user_e", "GET", "/_cluster/pending_tasks"); + assertAccessIsDenied("user_e", "GET", "/_nodes/stats"); + assertAccessIsDenied("user_e", "GET", "/_nodes/hot_threads"); + assertAccessIsDenied("user_e", "GET", "/_nodes/infos"); + assertAccessIsDenied("user_e", "POST", "/_cluster/reroute"); + assertAccessIsDenied("user_e", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); + } public void testThatSnapshotAndRestore() throws Exception { @@ -147,6 +165,7 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsDenied("user_c", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsDenied("user_d", "PUT", "/_snapshot/my-repo", repoJson); + assertAccessIsDenied("user_e", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsAllowed("user_a", "PUT", "/_snapshot/my-repo", repoJson); Request createBar = new Request("PUT", "/someindex/bar/1"); @@ -155,16 +174,19 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { assertAccessIsDenied("user_a", createBar); assertAccessIsDenied("user_b", createBar); assertAccessIsDenied("user_d", createBar); + assertAccessIsDenied("user_e", createBar); assertAccessIsAllowed("user_c", createBar); assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); assertAccessIsDenied("user_c", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); + assertAccessIsDenied("user_e", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); assertAccessIsAllowed("user_a", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); assertAccessIsDenied("user_b", "GET", "/_snapshot/my-repo/my-snapshot/_status"); assertAccessIsDenied("user_c", "GET", "/_snapshot/my-repo/my-snapshot/_status"); assertAccessIsAllowed("user_a", "GET", "/_snapshot/my-repo/my-snapshot/_status"); assertAccessIsAllowed("user_d", "GET", "/_snapshot/my-repo/my-snapshot/_status"); + assertAccessIsAllowed("user_e", "GET", "/_snapshot/my-repo/my-snapshot/_status"); // This snapshot needs to be finished in order to be restored waitForSnapshotToFinish("my-repo", "my-snapshot"); @@ -175,6 +197,7 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { assertAccessIsDenied("user_a", "DELETE", "/someindex"); assertAccessIsDenied("user_b", "DELETE", "/someindex"); assertAccessIsDenied("user_d", "DELETE", "/someindex"); + assertAccessIsDenied("user_e", "DELETE", "/someindex"); assertAccessIsAllowed("user_c", "DELETE", "/someindex"); Request restoreSnapshotRequest = new Request("POST", "/_snapshot/my-repo/my-snapshot/_restore"); @@ -182,21 +205,25 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { assertAccessIsDenied("user_b", restoreSnapshotRequest); assertAccessIsDenied("user_c", restoreSnapshotRequest); assertAccessIsDenied("user_d", restoreSnapshotRequest); + assertAccessIsDenied("user_e", restoreSnapshotRequest); assertAccessIsAllowed("user_a", restoreSnapshotRequest); assertAccessIsDenied("user_a", "GET", "/someindex/bar/1"); assertAccessIsDenied("user_b", "GET", "/someindex/bar/1"); assertAccessIsDenied("user_d", "GET", "/someindex/bar/1"); + assertAccessIsDenied("user_e", "GET", "/someindex/bar/1"); assertAccessIsAllowed("user_c", "GET", "/someindex/bar/1"); assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo/my-snapshot"); assertAccessIsDenied("user_c", "DELETE", "/_snapshot/my-repo/my-snapshot"); assertAccessIsDenied("user_d", "DELETE", "/_snapshot/my-repo/my-snapshot"); + assertAccessIsDenied("user_e", "DELETE", "/_snapshot/my-repo/my-snapshot"); assertAccessIsAllowed("user_a", "DELETE", "/_snapshot/my-repo/my-snapshot"); assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo"); assertAccessIsDenied("user_c", "DELETE", "/_snapshot/my-repo"); assertAccessIsDenied("user_d", "DELETE", "/_snapshot/my-repo"); + assertAccessIsDenied("user_e", "DELETE", "/_snapshot/my-repo"); assertAccessIsAllowed("user_a", "DELETE", "/_snapshot/my-repo"); } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/11_builtin.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/11_builtin.yml index c7130faf277..02961a2db12 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/11_builtin.yml +++ b/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" : 33 } + - length: { "cluster" : 34 } - length: { "index" : 17 }