From a59a8d6b40fa5cb13ca81f75bcc8b0d9a0ef25b4 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 23 Jan 2019 22:56:38 +0200 Subject: [PATCH 1/9] Role and unit test --- .../authz/privilege/ClusterPrivilege.java | 7 +++ .../authz/store/ReservedRolesStore.java | 11 +++++ .../authz/store/ReservedRolesStoreTests.java | 49 +++++++++++++++++++ 3 files changed, 67 insertions(+) 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 fba595e7a09e4..5e8007fe9f5f5 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 @@ -6,6 +6,9 @@ package org.elasticsearch.xpack.core.security.authz.privilege; import org.apache.lucene.util.automaton.Automaton; +import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; +import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction; +import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; @@ -48,6 +51,8 @@ public final class ClusterPrivilege extends Privilege { private static final Automaton MANAGE_ROLLUP_AUTOMATON = patterns("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*"); private static final Automaton MANAGE_CCR_AUTOMATON = patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME); + private static final Automaton CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME, + GetSnapshotsAction.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); @@ -73,6 +78,7 @@ 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 CREATE_SNAPSHOT = new ClusterPrivilege("create_snapshot", CREATE_SNAPSHOT_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); @@ -98,6 +104,7 @@ public final class ClusterPrivilege extends Privilege { .put("manage_rollup", MANAGE_ROLLUP) .put("manage_ccr", MANAGE_CCR) .put("read_ccr", READ_CCR) + .put("create_snapshot", CREATE_SNAPSHOT) .put("manage_ilm", MANAGE_ILM) .put("read_ilm", READ_ILM) .immutableMap(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index dfd276f4ee9f0..c312d54a57a6d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -6,9 +6,14 @@ package org.elasticsearch.xpack.core.security.authz.store; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction; +import org.elasticsearch.action.admin.indices.get.GetIndexAction; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.xpack.core.monitoring.action.MonitoringBulkAction; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; +import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges; +import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges; @@ -166,6 +171,12 @@ private static Map initializeReservedRoles() { null, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("rollup_admin", new RoleDescriptor("rollup_admin", new String[] { "manage_rollup" }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) + .put("snapshot_user", new RoleDescriptor("snapshot_user", new String[] { "create_snapshot", GetRepositoriesAction.NAME }, + new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() + .indices("*") + .privileges(GetIndexAction.NAME) + .allowRestrictedIndices(true) + .build() }, null, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null)) .immutableMap(); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index 5a567ad13ff80..f396318797bcc 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -7,8 +7,12 @@ import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; +import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction; import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction; +import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; +import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction; +import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; @@ -158,6 +162,51 @@ public void testIsReserved() { assertThat(ReservedRolesStore.isReserved(APMSystemUser.ROLE_NAME), is(true)); assertThat(ReservedRolesStore.isReserved(RemoteMonitoringUser.COLLECTION_ROLE_NAME), is(true)); assertThat(ReservedRolesStore.isReserved(RemoteMonitoringUser.INDEXING_ROLE_NAME), is(true)); + assertThat(ReservedRolesStore.isReserved("snapshot_user"), is(true)); + } + + public void testSnapshotUserRole() { + final TransportRequest request = mock(TransportRequest.class); + + RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("snapshot_user"); + assertNotNull(roleDescriptor); + assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); + + Role snapshotUserRole = Role.builder(roleDescriptor, null).build(); + assertThat(snapshotUserRole.cluster().check(GetRepositoriesAction.NAME, request), is(true)); + assertThat(snapshotUserRole.cluster().check(CreateSnapshotAction.NAME, request), is(true)); + assertThat(snapshotUserRole.cluster().check(SnapshotsStatusAction.NAME, request), is(true)); + assertThat(snapshotUserRole.cluster().check(GetSnapshotsAction.NAME, request), is(true)); + + assertThat(snapshotUserRole.cluster().check(GetIndexTemplatesAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(DeleteIndexTemplateAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(PutPipelineAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(GetPipelineAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(DeletePipelineAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(ClusterRerouteAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(ClusterUpdateSettingsAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(MonitoringBulkAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(GetWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(PutWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(DeleteWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(ExecuteWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(AckWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(ActivateWatchAction.NAME, request), is(false)); + assertThat(snapshotUserRole.cluster().check(WatcherServiceAction.NAME, request), is(false)); + + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(randomAlphaOfLengthBetween(8, 24)), is(false)); + assertThat(snapshotUserRole.indices().allowedIndicesMatcher("indices:foo").test(randomAlphaOfLengthBetween(8, 24)), is(false)); + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetAction.NAME).test(randomAlphaOfLengthBetween(8, 24)), is(false)); + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetAction.NAME).test(randomAlphaOfLengthBetween(8, 24)), is(false)); + + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME) + .test(randomAlphaOfLengthBetween(8, 24)), is(true)); + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME) + .test(RestrictedIndicesNames.INTERNAL_SECURITY_INDEX), is(true)); + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME) + .test(RestrictedIndicesNames.SECURITY_INDEX_NAME), is(true)); + + assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.NAMES_SET); } public void testIngestAdminRole() { From e045d2f881254aeb26182124ce04f9b724d9793d Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 24 Jan 2019 21:25:06 +0200 Subject: [PATCH 2/9] TADAAA --- .../authz/privilege/ClusterPrivilege.java | 3 +- .../authz/privilege/IndexPrivilege.java | 4 +- .../authz/store/ReservedRolesStore.java | 2 +- .../authz/store/ReservedRolesStoreTests.java | 2 + .../security/authz/SnapshotUserRoleIT.java | 185 ++++++++++++++++++ 5 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java 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 5e8007fe9f5f5..b461212c6be4f 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 @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.core.security.authz.privilege; import org.apache.lucene.util.automaton.Automaton; +import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction; import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction; @@ -52,7 +53,7 @@ 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 CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME, - GetSnapshotsAction.NAME); + GetSnapshotsAction.NAME, GetRepositoriesAction.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); 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 3a92c08704e41..d24863d6d53c4 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 @@ -64,8 +64,8 @@ public final class IndexPrivilege extends Privilege { 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); + public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY); + public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON); public static final IndexPrivilege READ = new IndexPrivilege("read", READ_AUTOMATON); public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", READ_CROSS_CLUSTER_AUTOMATON); public static final IndexPrivilege CREATE = new IndexPrivilege("create", CREATE_AUTOMATON); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index c312d54a57a6d..ee7edf4c2cc2b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -174,7 +174,7 @@ private static Map initializeReservedRoles() { .put("snapshot_user", new RoleDescriptor("snapshot_user", new String[] { "create_snapshot", GetRepositoriesAction.NAME }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() .indices("*") - .privileges(GetIndexAction.NAME) + .privileges("view_index_metadata") .allowRestrictedIndices(true) .build() }, null, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null)) .immutableMap(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index f396318797bcc..79a819cec9571 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -8,6 +8,7 @@ import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction; +import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryAction; import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; @@ -178,6 +179,7 @@ public void testSnapshotUserRole() { assertThat(snapshotUserRole.cluster().check(SnapshotsStatusAction.NAME, request), is(true)); assertThat(snapshotUserRole.cluster().check(GetSnapshotsAction.NAME, request), is(true)); + assertThat(snapshotUserRole.cluster().check(PutRepositoryAction.NAME, request), is(false)); assertThat(snapshotUserRole.cluster().check(GetIndexTemplatesAction.NAME, request), is(false)); assertThat(snapshotUserRole.cluster().check(DeleteIndexTemplateAction.NAME, request), is(false)); assertThat(snapshotUserRole.cluster().check(PutPipelineAction.NAME, request), is(false)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java new file mode 100644 index 0000000000000..6bef493d2a7f4 --- /dev/null +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.security.authz; + +import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; +import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; +import org.elasticsearch.action.admin.indices.get.GetIndexResponse; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.snapshots.SnapshotState; +import org.elasticsearch.test.NativeRealmIntegTestCase; +import org.elasticsearch.xpack.core.security.authc.support.Hasher; +import org.junit.Before; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Locale; + +import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.INTERNAL_SECURITY_INDEX; +import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_INDEX_NAME; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; + +public class SnapshotUserRoleIT extends NativeRealmIntegTestCase { + + private String snapshotUserToken; + private String ordinaryIndex; + + @Before + public void setupClusterBeforeSnapshot() { + logger.info("--> creating repository"); + assertAcked(client().admin().cluster().preparePutRepository("repo") + .setType("fs") + .setSettings(Settings.builder().put("location", randomRepoPath()))); + + logger.info("--> creating ordinary index"); + final int shards = between(1, 10); + ordinaryIndex = randomAlphaOfLength(4).toLowerCase(Locale.ROOT); + assertAcked(prepareCreate(ordinaryIndex, 0, Settings.builder().put("number_of_shards", shards).put("number_of_replicas", 0))); + ensureGreen(); + + logger.info("--> creating snapshot_user user"); + final String user = "snapshot_user"; + final char[] password = new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; + snapshotUserToken = basicAuthHeaderValue(user, new SecureString(password)); + securityClient().preparePutUser(user, password, Hasher.BCRYPT, "snapshot_user").get(); + ensureGreen(INTERNAL_SECURITY_INDEX); + } + + public void testSnapshotUserRoleCanSnapshotAndSeeAllIndices() { + // view repositories + final GetRepositoriesResponse getRepositoriesResponse = client() + .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareGetRepositories(randomFrom("*", "_all")).get(); + assertThat(getRepositoriesResponse.repositories().size(), is(1)); + assertThat(getRepositoriesResponse.repositories().get(0).name(), is("repo")); + // view all indices, including restricted ones + final GetIndexResponse getIndexResponse = client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .admin().indices().prepareGetIndex().setIndices(randomFrom("_all", "*")).get(); + assertThat(Arrays.asList(getIndexResponse.indices()), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); + // create snapshot that includes restricted indices + final CreateSnapshotResponse snapshotResponse = client() + .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareCreateSnapshot("repo", "snap").setIndices(randomFrom("_all", "*")).setWaitForCompletion(true).get(); + assertThat(snapshotResponse.getSnapshotInfo().state(), is(SnapshotState.SUCCESS)); + assertThat(snapshotResponse.getSnapshotInfo().indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); + // view snapshots for repo + final GetSnapshotsResponse getSnapshotResponse = client() + .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareGetSnapshots("repo").get(); + assertThat(getSnapshotResponse.getSnapshots().size(), is(1)); + assertThat(getSnapshotResponse.getSnapshots().get(0).snapshotId().getName(), is("snap")); + assertThat(getSnapshotResponse.getSnapshots().get(0).indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); + } + + public void testSnapshotUserRoleIsReserved() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> securityClient().preparePutRole("snapshot_user").get()); + assertThat(e.getMessage(), containsString("role [snapshot_user] is reserved and cannot be modified")); + e = expectThrows(IllegalArgumentException.class, + () -> securityClient().prepareDeleteRole("snapshot_user").get()); + assertThat(e.getMessage(), containsString("role [snapshot_user] is reserved and cannot be deleted")); + } + + public void testSnapshotUserRoleUnathorizedForDestructiveActions() { + // try search all + ElasticsearchSecurityException unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .prepareSearch(randomFrom("_all", "*")).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); + // try create index + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareCreate(ordinaryIndex + "2").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:admin/create] is unauthorized for user [snapshot_user]")); + // try create another repo + unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .admin().cluster().preparePutRepository("some_other_repo").setType("fs").setSettings(Settings.builder().put("location", randomRepoPath())).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [cluster:admin/repository/put] is unauthorized for user [snapshot_user]")); + // try delete repo + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareDeleteRepository("repo").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [cluster:admin/repository/delete] is unauthorized for user [snapshot_user]")); + // try fumble with snapshots + unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .admin().cluster().prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [cluster:admin/snapshot/restore] is unauthorized for user [snapshot_user]")); + unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .admin().cluster().prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [cluster:admin/snapshot/delete] is unauthorized for user [snapshot_user]")); + // try destructive actions on all indices are unauthorized + for (final String indexToTest : Arrays.asList(INTERNAL_SECURITY_INDEX, SECURITY_INDEX_NAME, ordinaryIndex)) { + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareSearch(indexToTest).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); + + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareGet(indexToTest, "doc", "1").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/read/get] is unauthorized for user [snapshot_user]")); + + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareIndex(indexToTest, "doc").setSource("term", "val").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/write/index] is unauthorized for user [snapshot_user]")); + + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/write/update] is unauthorized for user [snapshot_user]")); + + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareDelete(indexToTest, "doc", "1").get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:data/write/delete] is unauthorized for user [snapshot_user]")); + + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareDelete(indexToTest).get()); + assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); + assertThat(unauthzException.getDetailedMessage(), + containsString("action [indices:admin/delete] is unauthorized for user [snapshot_user]")); + } + } + + // create repo + // create another index + // create user with snapshot_user role + + // test user can NOT get, search, index, change settings + // test user can NOT put repositories + // test user can NOT restore snapshot + // test user can list all indices (including .security) + // test user can snapshot all indices (including .security) + +} From ceb1116e2aaf9a15956cb54339cedb78c9a41353 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 24 Jan 2019 21:45:46 +0200 Subject: [PATCH 3/9] Trim --- .../xpack/security/authz/SnapshotUserRoleIT.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java index 6bef493d2a7f4..274a0d3124670 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java @@ -171,15 +171,5 @@ public void testSnapshotUserRoleUnathorizedForDestructiveActions() { containsString("action [indices:admin/delete] is unauthorized for user [snapshot_user]")); } } - - // create repo - // create another index - // create user with snapshot_user role - - // test user can NOT get, search, index, change settings - // test user can NOT put repositories - // test user can NOT restore snapshot - // test user can list all indices (including .security) - // test user can snapshot all indices (including .security) } From 3ab0be3faf9e681ed25d7d986c559833c24d49d6 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 24 Jan 2019 22:01:44 +0200 Subject: [PATCH 4/9] Checkstyle --- .../security/authz/SnapshotUserRoleIT.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java index 274a0d3124670..8989fd8f63708 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java @@ -30,7 +30,6 @@ import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; public class SnapshotUserRoleIT extends NativeRealmIntegTestCase { @@ -104,13 +103,16 @@ public void testSnapshotUserRoleUnathorizedForDestructiveActions() { containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); // try create index unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareCreate(ordinaryIndex + "2").get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices() + .prepareCreate(ordinaryIndex + "2").get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:admin/create] is unauthorized for user [snapshot_user]")); // try create another repo - unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .admin().cluster().preparePutRepository("some_other_repo").setType("fs").setSettings(Settings.builder().put("location", randomRepoPath())).get()); + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .preparePutRepository("some_other_repo").setType("fs") + .setSettings(Settings.builder().put("location", randomRepoPath())).get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [cluster:admin/repository/put] is unauthorized for user [snapshot_user]")); @@ -122,50 +124,57 @@ public void testSnapshotUserRoleUnathorizedForDestructiveActions() { assertThat(unauthzException.getDetailedMessage(), containsString("action [cluster:admin/repository/delete] is unauthorized for user [snapshot_user]")); // try fumble with snapshots - unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .admin().cluster().prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [cluster:admin/snapshot/restore] is unauthorized for user [snapshot_user]")); - unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .admin().cluster().prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); + unauthzException = expectThrows(ElasticsearchSecurityException.class, + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() + .prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [cluster:admin/snapshot/delete] is unauthorized for user [snapshot_user]")); // try destructive actions on all indices are unauthorized for (final String indexToTest : Arrays.asList(INTERNAL_SECURITY_INDEX, SECURITY_INDEX_NAME, ordinaryIndex)) { - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareSearch(indexToTest).get()); + unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client() + .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareSearch(indexToTest).get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareGet(indexToTest, "doc", "1").get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .prepareGet(indexToTest, "doc", "1").get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:data/read/get] is unauthorized for user [snapshot_user]")); unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareIndex(indexToTest, "doc").setSource("term", "val").get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .prepareIndex(indexToTest, "doc").setSource("term", "val").get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:data/write/index] is unauthorized for user [snapshot_user]")); unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:data/write/update] is unauthorized for user [snapshot_user]")); unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareDelete(indexToTest, "doc", "1").get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) + .prepareDelete(indexToTest, "doc", "1").get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:data/write/delete] is unauthorized for user [snapshot_user]")); unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareDelete(indexToTest).get()); + () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices() + .prepareDelete(indexToTest).get()); assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); assertThat(unauthzException.getDetailedMessage(), containsString("action [indices:admin/delete] is unauthorized for user [snapshot_user]")); From 77d8745f877bcf4bd792d4913aabede8629d1f29 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 25 Jan 2019 13:30:35 +0200 Subject: [PATCH 5/9] Precommit checks --- .../xpack/core/security/authz/store/ReservedRolesStore.java | 4 ---- ...napshotUserRoleIT.java => SnapshotUserRoleIntegTests.java} | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) rename x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/{SnapshotUserRoleIT.java => SnapshotUserRoleIntegTests.java} (99%) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index e0425e13aa01e..2c30b5fe1affe 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -7,13 +7,9 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction; -import org.elasticsearch.action.admin.indices.get.GetIndexAction; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.xpack.core.monitoring.action.MonitoringBulkAction; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges; -import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java similarity index 99% rename from x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java rename to x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java index 8989fd8f63708..8869795bdfdba 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIT.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java @@ -32,7 +32,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsInAnyOrder; -public class SnapshotUserRoleIT extends NativeRealmIntegTestCase { +public class SnapshotUserRoleIntegTests extends NativeRealmIntegTestCase { private String snapshotUserToken; private String ordinaryIndex; From e0a5f456f82b0b2a3a379d1664e011dff6995ad3 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 25 Jan 2019 22:59:53 +0200 Subject: [PATCH 6/9] Simplify SnapshotUserRoleIntegTests --- .../authz/SnapshotUserRoleIntegTests.java | 133 ++++++------------ 1 file changed, 40 insertions(+), 93 deletions(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java index 8869795bdfdba..9a6909aad26fd 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/SnapshotUserRoleIntegTests.java @@ -6,14 +6,13 @@ package org.elasticsearch.xpack.security.authz; -import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; import org.elasticsearch.action.admin.indices.get.GetIndexResponse; +import org.elasticsearch.client.Client; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.rest.RestStatus; import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.NativeRealmIntegTestCase; import org.elasticsearch.xpack.core.security.authc.support.Hasher; @@ -31,10 +30,11 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.elasticsearch.test.SecurityTestsUtils.assertThrowsAuthorizationException; public class SnapshotUserRoleIntegTests extends NativeRealmIntegTestCase { - private String snapshotUserToken; + private Client client; private String ordinaryIndex; @Before @@ -53,32 +53,28 @@ public void setupClusterBeforeSnapshot() { logger.info("--> creating snapshot_user user"); final String user = "snapshot_user"; final char[] password = new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; - snapshotUserToken = basicAuthHeaderValue(user, new SecureString(password)); + final String snapshotUserToken = basicAuthHeaderValue(user, new SecureString(password)); + client = client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)); securityClient().preparePutUser(user, password, Hasher.BCRYPT, "snapshot_user").get(); ensureGreen(INTERNAL_SECURITY_INDEX); } public void testSnapshotUserRoleCanSnapshotAndSeeAllIndices() { // view repositories - final GetRepositoriesResponse getRepositoriesResponse = client() - .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareGetRepositories(randomFrom("*", "_all")).get(); + final GetRepositoriesResponse getRepositoriesResponse = client.admin().cluster().prepareGetRepositories(randomFrom("*", "_all")) + .get(); assertThat(getRepositoriesResponse.repositories().size(), is(1)); assertThat(getRepositoriesResponse.repositories().get(0).name(), is("repo")); // view all indices, including restricted ones - final GetIndexResponse getIndexResponse = client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .admin().indices().prepareGetIndex().setIndices(randomFrom("_all", "*")).get(); + final GetIndexResponse getIndexResponse = client.admin().indices().prepareGetIndex().setIndices(randomFrom("_all", "*")).get(); assertThat(Arrays.asList(getIndexResponse.indices()), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); // create snapshot that includes restricted indices - final CreateSnapshotResponse snapshotResponse = client() - .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareCreateSnapshot("repo", "snap").setIndices(randomFrom("_all", "*")).setWaitForCompletion(true).get(); + final CreateSnapshotResponse snapshotResponse = client.admin().cluster().prepareCreateSnapshot("repo", "snap") + .setIndices(randomFrom("_all", "*")).setWaitForCompletion(true).get(); assertThat(snapshotResponse.getSnapshotInfo().state(), is(SnapshotState.SUCCESS)); assertThat(snapshotResponse.getSnapshotInfo().indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); // view snapshots for repo - final GetSnapshotsResponse getSnapshotResponse = client() - .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareGetSnapshots("repo").get(); + final GetSnapshotsResponse getSnapshotResponse = client.admin().cluster().prepareGetSnapshots("repo").get(); assertThat(getSnapshotResponse.getSnapshots().size(), is(1)); assertThat(getSnapshotResponse.getSnapshots().get(0).snapshotId().getName(), is("snap")); assertThat(getSnapshotResponse.getSnapshots().get(0).indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex)); @@ -95,89 +91,40 @@ public void testSnapshotUserRoleIsReserved() { public void testSnapshotUserRoleUnathorizedForDestructiveActions() { // try search all - ElasticsearchSecurityException unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .prepareSearch(randomFrom("_all", "*")).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); + assertThrowsAuthorizationException(() -> client.prepareSearch(randomFrom("_all", "*")).get(), "indices:data/read/search", + "snapshot_user"); // try create index - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices() - .prepareCreate(ordinaryIndex + "2").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:admin/create] is unauthorized for user [snapshot_user]")); + assertThrowsAuthorizationException(() -> client.admin().indices().prepareCreate(ordinaryIndex + "2").get(), "indices:admin/create", + "snapshot_user"); // try create another repo - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .preparePutRepository("some_other_repo").setType("fs") - .setSettings(Settings.builder().put("location", randomRepoPath())).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [cluster:admin/repository/put] is unauthorized for user [snapshot_user]")); + assertThrowsAuthorizationException( + () -> client.admin().cluster().preparePutRepository("some_other_repo").setType("fs") + .setSettings(Settings.builder().put("location", randomRepoPath())).get(), + "cluster:admin/repository/put", "snapshot_user"); // try delete repo - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareDeleteRepository("repo").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [cluster:admin/repository/delete] is unauthorized for user [snapshot_user]")); + assertThrowsAuthorizationException(() -> client.admin().cluster().prepareDeleteRepository("repo").get(), + "cluster:admin/repository/delete", "snapshot_user"); // try fumble with snapshots - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [cluster:admin/snapshot/restore] is unauthorized for user [snapshot_user]")); - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster() - .prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [cluster:admin/snapshot/delete] is unauthorized for user [snapshot_user]")); - // try destructive actions on all indices are unauthorized + assertThrowsAuthorizationException( + () -> client.admin().cluster().prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get(), + "cluster:admin/snapshot/restore", "snapshot_user"); + assertThrowsAuthorizationException( + () -> client.admin().cluster().prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get(), + "cluster:admin/snapshot/delete", "snapshot_user"); + // try destructive/revealing actions on all indices for (final String indexToTest : Arrays.asList(INTERNAL_SECURITY_INDEX, SECURITY_INDEX_NAME, ordinaryIndex)) { - unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client() - .filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareSearch(indexToTest).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]")); - - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .prepareGet(indexToTest, "doc", "1").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/read/get] is unauthorized for user [snapshot_user]")); - - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .prepareIndex(indexToTest, "doc").setSource("term", "val").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/write/index] is unauthorized for user [snapshot_user]")); - - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/write/update] is unauthorized for user [snapshot_user]")); - - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)) - .prepareDelete(indexToTest, "doc", "1").get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:data/write/delete] is unauthorized for user [snapshot_user]")); - - unauthzException = expectThrows(ElasticsearchSecurityException.class, - () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices() - .prepareDelete(indexToTest).get()); - assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN)); - assertThat(unauthzException.getDetailedMessage(), - containsString("action [indices:admin/delete] is unauthorized for user [snapshot_user]")); + assertThrowsAuthorizationException(() -> client.prepareSearch(indexToTest).get(), "indices:data/read/search", "snapshot_user"); + assertThrowsAuthorizationException(() -> client.prepareGet(indexToTest, "doc", "1").get(), "indices:data/read/get", + "snapshot_user"); + assertThrowsAuthorizationException(() -> client.prepareIndex(indexToTest, "doc").setSource("term", "val").get(), + "indices:data/write/index", "snapshot_user"); + assertThrowsAuthorizationException(() -> client.prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get(), + "indices:data/write/update", "snapshot_user"); + assertThrowsAuthorizationException(() -> client.prepareDelete(indexToTest, "doc", "1").get(), "indices:data/write/delete", + "snapshot_user"); + + assertThrowsAuthorizationException(() -> client.admin().indices().prepareDelete(indexToTest).get(), "indices:admin/delete", + "snapshot_user"); } } From 4ff1cce9ef16a31b7ce31753d183427cf5a211b8 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 25 Jan 2019 23:25:17 +0200 Subject: [PATCH 7/9] added snapshot status to create_snapshot + test --- .../authz/privilege/ClusterPrivilege.java | 4 +-- .../integration/ClusterPrivilegeTests.java | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) 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 b461212c6be4f..f3822dac4e4f5 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 @@ -52,8 +52,8 @@ public final class ClusterPrivilege extends Privilege { private static final Automaton MANAGE_ROLLUP_AUTOMATON = patterns("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*"); private static final Automaton MANAGE_CCR_AUTOMATON = patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME); - private static final Automaton CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME, - GetSnapshotsAction.NAME, GetRepositoriesAction.NAME); + private static final Automaton CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*", + GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.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); 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 bf81fd77dc59d..191f4778b1a54 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 @@ -32,12 +32,15 @@ public class ClusterPrivilegeTests extends AbstractPrivilegeTestCase { "role_c:\n" + " indices:\n" + " - names: 'someindex'\n" + - " privileges: [ all ]\n"; + " privileges: [ all ]\n" + + "role_d:\n" + + " cluster: [ create_snapshot ]\n"; private static final String USERS_ROLES = "role_a:user_a\n" + "role_b:user_b\n" + - "role_c:user_c\n"; + "role_c:user_c\n" + + "role_d:user_d\n"; private static Path repositoryLocation; @@ -75,8 +78,8 @@ protected String configUsers() { return super.configUsers() + "user_a:" + usersPasswdHashed + "\n" + "user_b:" + usersPasswdHashed + "\n" + - "user_c:" + usersPasswdHashed + "\n"; - + "user_c:" + usersPasswdHashed + "\n" + + "user_d:" + usersPasswdHashed + "\n"; } @Override @@ -122,13 +125,25 @@ public void testThatClusterPrivilegesWorkAsExpectedViaHttp() throws Exception { assertAccessIsDenied("user_c", "GET", "/_nodes/infos"); assertAccessIsDenied("user_c", "POST", "/_cluster/reroute"); assertAccessIsDenied("user_c", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); - } + + // user_d can view repos and create and view snapshots on existings repos, everything else is DENIED + assertAccessIsDenied("user_d", "GET", "/_cluster/state"); + assertAccessIsDenied("user_d", "GET", "/_cluster/health"); + assertAccessIsDenied("user_d", "GET", "/_cluster/settings"); + assertAccessIsDenied("user_d", "GET", "/_cluster/stats"); + assertAccessIsDenied("user_d", "GET", "/_cluster/pending_tasks"); + assertAccessIsDenied("user_d", "GET", "/_nodes/stats"); + assertAccessIsDenied("user_d", "GET", "/_nodes/hot_threads"); + assertAccessIsDenied("user_d", "GET", "/_nodes/infos"); + assertAccessIsDenied("user_d", "POST", "/_cluster/reroute"); + assertAccessIsDenied("user_d", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); } public void testThatSnapshotAndRestore() throws Exception { String repoJson = Strings.toString(jsonBuilder().startObject().field("type", "fs").startObject("settings").field("location", repositoryLocation.toString()).endObject().endObject()); assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsDenied("user_c", "PUT", "/_snapshot/my-repo", repoJson); + assertAccessIsDenied("user_d", "PUT", "/_snapshot/my-repo", repoJson); assertAccessIsAllowed("user_a", "PUT", "/_snapshot/my-repo", repoJson); Request createBar = new Request("PUT", "/someindex/bar/1"); @@ -136,6 +151,7 @@ public void testThatSnapshotAndRestore() throws Exception { createBar.addParameter("refresh", "true"); assertAccessIsDenied("user_a", createBar); assertAccessIsDenied("user_b", createBar); + assertAccessIsDenied("user_d", createBar); assertAccessIsAllowed("user_c", createBar); assertAccessIsDenied("user_b", "PUT", "/_snapshot/my-repo/my-snapshot", "{ \"indices\": \"someindex\" }"); @@ -145,30 +161,38 @@ public void testThatSnapshotAndRestore() throws Exception { 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"); // This snapshot needs to be finished in order to be restored waitForSnapshotToFinish("my-repo", "my-snapshot"); + // user_d can create snapshots, but not concurrently + assertAccessIsAllowed("user_d", "PUT", "/_snapshot/my-repo/my-snapshot-d", "{ \"indices\": \"someindex\" }"); assertAccessIsDenied("user_a", "DELETE", "/someindex"); assertAccessIsDenied("user_b", "DELETE", "/someindex"); + assertAccessIsDenied("user_d", "DELETE", "/someindex"); assertAccessIsAllowed("user_c", "DELETE", "/someindex"); Request restoreSnapshotRequest = new Request("POST", "/_snapshot/my-repo/my-snapshot/_restore"); restoreSnapshotRequest.addParameter("wait_for_completion", "true"); assertAccessIsDenied("user_b", restoreSnapshotRequest); assertAccessIsDenied("user_c", restoreSnapshotRequest); + assertAccessIsDenied("user_d", 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"); 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"); 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"); assertAccessIsAllowed("user_a", "DELETE", "/_snapshot/my-repo"); } From 04f104b4efe91caaac430fa7c49f5f05a3b7c344 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Sun, 27 Jan 2019 14:50:13 +0200 Subject: [PATCH 8/9] Checkstyle --- .../org/elasticsearch/integration/ClusterPrivilegeTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 191f4778b1a54..3b30982784b76 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 @@ -136,7 +136,8 @@ public void testThatClusterPrivilegesWorkAsExpectedViaHttp() throws Exception { assertAccessIsDenied("user_d", "GET", "/_nodes/hot_threads"); assertAccessIsDenied("user_d", "GET", "/_nodes/infos"); 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\" } }"); + } public void testThatSnapshotAndRestore() throws Exception { String repoJson = Strings.toString(jsonBuilder().startObject().field("type", "fs").startObject("settings").field("location", From 2eb1e9e17bf1e3569be4fc4d03acbfda8132c688 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Sun, 27 Jan 2019 20:46:28 +0200 Subject: [PATCH 9/9] Fix SecurityDocumentationIT --- .../client/documentation/SecurityDocumentationIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java index b7261b2dd9581..fa10de4fe4ce9 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java @@ -638,8 +638,8 @@ public void testGetRoles() throws Exception { List roles = response.getRoles(); assertNotNull(response); - // 23 system roles plus the three we created - assertThat(roles.size(), equalTo(26)); + // 24 system roles plus the three we created + assertThat(roles.size(), equalTo(27)); } {