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 <anton.shuvaev91@gmail.com>
This commit is contained in:
Albert Zaharovits 2020-01-06 13:15:55 +02:00 committed by GitHub
parent 0f2d26bdca
commit 9ae3cd2a78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 39 additions and 4 deletions

View File

@ -87,6 +87,7 @@ A successful call returns an object with "cluster" and "index" fields.
"monitor_data_frame_transforms", "monitor_data_frame_transforms",
"monitor_ml", "monitor_ml",
"monitor_rollup", "monitor_rollup",
"monitor_snapshot",
"monitor_transform", "monitor_transform",
"monitor_watcher", "monitor_watcher",
"none", "none",

View File

@ -16,6 +16,9 @@ settings update, rerouting, or managing users and roles.
Privileges to create snapshots for existing repositories. Can also list and view Privileges to create snapshots for existing repositories. Can also list and view
details on existing repositories and snapshots. details on existing repositories and snapshots.
`monitor_snapshot`::
Privileges to list and view details on existing repositories and snapshots.
`manage`:: `manage`::
Builds on `monitor` and adds cluster operations that change values in the cluster. Builds on `monitor` and adds cluster operations that change values in the cluster.
This includes snapshotting, updating settings, and rerouting. It also includes This includes snapshotting, updating settings, and rerouting. It also includes

View File

@ -75,6 +75,8 @@ public class ClusterPrivilegeResolver {
private static final Set<String> CREATE_SNAPSHOT_PATTERN = Collections.unmodifiableSet( private static final Set<String> CREATE_SNAPSHOT_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*", Sets.newHashSet(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*",
GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME)); GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME));
private static final Set<String> MONITOR_SNAPSHOT_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(
SnapshotsStatusAction.NAME + "*", GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME));
private static final Set<String> READ_CCR_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(ClusterStateAction.NAME, private static final Set<String> READ_CCR_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(ClusterStateAction.NAME,
HasPrivilegesAction.NAME)); HasPrivilegesAction.NAME));
private static final Set<String> MANAGE_ILM_PATTERN = Collections.singleton("cluster:admin/ilm/*"); private static final Set<String> 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 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 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 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 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 READ_ILM = new ActionClusterPrivilege("read_ilm", READ_ILM_PATTERN);
public static final NamedClusterPrivilege MANAGE_SLM = new ActionClusterPrivilege("manage_slm", MANAGE_SLM_PATTERN); public static final NamedClusterPrivilege MANAGE_SLM = new ActionClusterPrivilege("manage_slm", MANAGE_SLM_PATTERN);
@ -159,6 +162,7 @@ public class ClusterPrivilegeResolver {
MANAGE_CCR, MANAGE_CCR,
READ_CCR, READ_CCR,
CREATE_SNAPSHOT, CREATE_SNAPSHOT,
MONITOR_SNAPSHOT,
MANAGE_ILM, MANAGE_ILM,
READ_ILM, READ_ILM,
MANAGE_SLM, MANAGE_SLM,

View File

@ -36,13 +36,17 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
" - names: 'someindex'\n" + " - names: 'someindex'\n" +
" privileges: [ all ]\n" + " privileges: [ all ]\n" +
"role_d:\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 = private static final String USERS_ROLES =
"role_a:user_a\n" + "role_a:user_a\n" +
"role_b:user_b\n" + "role_b:user_b\n" +
"role_c:user_c\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; private static Path repositoryLocation;
@ -81,7 +85,8 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
"user_a:" + usersPasswdHashed + "\n" + "user_a:" + usersPasswdHashed + "\n" +
"user_b:" + usersPasswdHashed + "\n" + "user_b:" + usersPasswdHashed + "\n" +
"user_c:" + usersPasswdHashed + "\n" + "user_c:" + usersPasswdHashed + "\n" +
"user_d:" + usersPasswdHashed + "\n"; "user_d:" + usersPasswdHashed + "\n" +
"user_e:" + usersPasswdHashed + "\n";
} }
@Override @Override
@ -139,6 +144,19 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
assertAccessIsDenied("user_d", "GET", "/_nodes/infos"); assertAccessIsDenied("user_d", "GET", "/_nodes/infos");
assertAccessIsDenied("user_d", "POST", "/_cluster/reroute"); assertAccessIsDenied("user_d", "POST", "/_cluster/reroute");
assertAccessIsDenied("user_d", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); 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 { public void testThatSnapshotAndRestore() throws Exception {
@ -147,6 +165,7 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo", repoJson);
assertAccessIsDenied("user_c", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsDenied("user_c", "PUT", "/_snapshot/my-repo", repoJson);
assertAccessIsDenied("user_d", "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); assertAccessIsAllowed("user_a", "PUT", "/_snapshot/my-repo", repoJson);
Request createBar = new Request("PUT", "/someindex/bar/1"); Request createBar = new Request("PUT", "/someindex/bar/1");
@ -155,16 +174,19 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
assertAccessIsDenied("user_a", createBar); assertAccessIsDenied("user_a", createBar);
assertAccessIsDenied("user_b", createBar); assertAccessIsDenied("user_b", createBar);
assertAccessIsDenied("user_d", createBar); assertAccessIsDenied("user_d", createBar);
assertAccessIsDenied("user_e", createBar);
assertAccessIsAllowed("user_c", createBar); assertAccessIsAllowed("user_c", createBar);
assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }");
assertAccessIsDenied("user_c", "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\" }"); assertAccessIsAllowed("user_a", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }");
assertAccessIsDenied("user_b", "GET", "/_snapshot/my-repo/my-snapshot/_status"); assertAccessIsDenied("user_b", "GET", "/_snapshot/my-repo/my-snapshot/_status");
assertAccessIsDenied("user_c", "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_a", "GET", "/_snapshot/my-repo/my-snapshot/_status");
assertAccessIsAllowed("user_d", "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 // This snapshot needs to be finished in order to be restored
waitForSnapshotToFinish("my-repo", "my-snapshot"); waitForSnapshotToFinish("my-repo", "my-snapshot");
@ -175,6 +197,7 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase {
assertAccessIsDenied("user_a", "DELETE", "/someindex"); assertAccessIsDenied("user_a", "DELETE", "/someindex");
assertAccessIsDenied("user_b", "DELETE", "/someindex"); assertAccessIsDenied("user_b", "DELETE", "/someindex");
assertAccessIsDenied("user_d", "DELETE", "/someindex"); assertAccessIsDenied("user_d", "DELETE", "/someindex");
assertAccessIsDenied("user_e", "DELETE", "/someindex");
assertAccessIsAllowed("user_c", "DELETE", "/someindex"); assertAccessIsAllowed("user_c", "DELETE", "/someindex");
Request restoreSnapshotRequest = new Request("POST", "/_snapshot/my-repo/my-snapshot/_restore"); 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_b", restoreSnapshotRequest);
assertAccessIsDenied("user_c", restoreSnapshotRequest); assertAccessIsDenied("user_c", restoreSnapshotRequest);
assertAccessIsDenied("user_d", restoreSnapshotRequest); assertAccessIsDenied("user_d", restoreSnapshotRequest);
assertAccessIsDenied("user_e", restoreSnapshotRequest);
assertAccessIsAllowed("user_a", restoreSnapshotRequest); assertAccessIsAllowed("user_a", restoreSnapshotRequest);
assertAccessIsDenied("user_a", "GET", "/someindex/bar/1"); assertAccessIsDenied("user_a", "GET", "/someindex/bar/1");
assertAccessIsDenied("user_b", "GET", "/someindex/bar/1"); assertAccessIsDenied("user_b", "GET", "/someindex/bar/1");
assertAccessIsDenied("user_d", "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"); assertAccessIsAllowed("user_c", "GET", "/someindex/bar/1");
assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo/my-snapshot"); assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo/my-snapshot");
assertAccessIsDenied("user_c", "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_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"); assertAccessIsAllowed("user_a", "DELETE", "/_snapshot/my-repo/my-snapshot");
assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo"); assertAccessIsDenied("user_b", "DELETE", "/_snapshot/my-repo");
assertAccessIsDenied("user_c", "DELETE", "/_snapshot/my-repo"); assertAccessIsDenied("user_c", "DELETE", "/_snapshot/my-repo");
assertAccessIsDenied("user_d", "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"); assertAccessIsAllowed("user_a", "DELETE", "/_snapshot/my-repo");
} }

View File

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