Skip to content

Commit f796ceb

Browse files
authored
Allow read template with cluster monitor privilege (#82244)
All three template types (legacy templates, composable index templates and component templates) are stored in cluster state metadata (in fields "templates", "index_template" and "component_template"). This cluster state is readable (via GET /_cluster/state) for users who have the monitor privilege at the cluster level. However, calling the explicit read endpoints for these templates required the manage_index_templates privilege. This change grants access to the template specific retrieval APIs for all users (or API Keys) with the cluster monitor privilge so that they can make use of these fit-for-purpose APIs instead of parsing data directly from cluster metadata Relates: elastic/beats#29554, #78832 Backport of: #82046
1 parent c5ecf80 commit f796ceb

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction;
1616
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction;
1717
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
18+
import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction;
19+
import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction;
20+
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction;
1821
import org.elasticsearch.action.ingest.GetPipelineAction;
1922
import org.elasticsearch.action.ingest.SimulatePipelineAction;
2023
import org.elasticsearch.common.Strings;
@@ -69,7 +72,12 @@ public class ClusterPrivilegeResolver {
6972
private static final Set<String> MANAGE_SERVICE_ACCOUNT_PATTERN = Collections.singleton(
7073
"cluster:admin/xpack/security/service_account/*"
7174
);
72-
private static final Set<String> MONITOR_PATTERN = Collections.singleton("cluster:monitor/*");
75+
private static final Set<String> MONITOR_PATTERN = Sets.newHashSet(
76+
"cluster:monitor/*",
77+
GetIndexTemplatesAction.NAME,
78+
GetComponentTemplateAction.NAME,
79+
GetComposableIndexTemplateAction.NAME
80+
);
7381
private static final Set<String> MONITOR_TEXT_STRUCTURE_PATTERN = Collections.singleton("cluster:monitor/text_structure/*");
7482
private static final Set<String> MONITOR_TRANSFORM_PATTERN = Collections.unmodifiableSet(
7583
Sets.newHashSet("cluster:monitor/data_frame/*", "cluster:monitor/transform/*")

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,31 @@
88
package org.elasticsearch.xpack.core.security.authz.privilege;
99

1010
import org.elasticsearch.action.ActionType;
11+
import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction;
12+
import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction;
13+
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction;
1114
import org.elasticsearch.test.ESTestCase;
1215
import org.elasticsearch.xpack.core.enrich.action.EnrichStatsAction;
1316
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
1417

18+
import java.util.Arrays;
19+
1520
public class ClusterPrivilegeTests extends ESTestCase {
1621

1722
public void testMonitorPrivilegeWillGrantActions() {
1823
assertGranted(ClusterPrivilegeResolver.MONITOR, EnrichStatsAction.INSTANCE);
1924
}
2025

26+
public void testMonitorPrivilegeGrantsGetTemplateActions() {
27+
for (ActionType<?> action : Arrays.asList(
28+
GetComponentTemplateAction.INSTANCE,
29+
GetComposableIndexTemplateAction.INSTANCE,
30+
GetIndexTemplatesAction.INSTANCE
31+
)) {
32+
assertGranted(ClusterPrivilegeResolver.MONITOR, action);
33+
}
34+
}
35+
2136
public static void assertGranted(ClusterPrivilege clusterPrivilege, ActionType<?> actionType) {
2237
assertTrue(clusterPrivilege.buildPermission(ClusterPermission.builder()).build().check(actionType.name(), null, null));
2338
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ public void testRemoteMonitoringCollectorRole() {
12061206
assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true));
12071207
assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true));
12081208
assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true));
1209-
assertThat(remoteMonitoringCollectorRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(false));
1209+
assertThat(remoteMonitoringCollectorRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(true));
12101210
assertThat(remoteMonitoringCollectorRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
12111211
assertThat(remoteMonitoringCollectorRole.cluster().check(DeleteIndexTemplateAction.NAME, request, authentication), is(false));
12121212
assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));

x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ClusterPrivilegeIntegrationTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,17 @@ public void testThatClusterPrivilegesWorkAsExpectedViaHttp() throws Exception {
123123
assertAccessIsAllowed("user_b", "GET", "/_nodes/stats");
124124
assertAccessIsAllowed("user_b", "GET", "/_nodes/hot_threads");
125125
assertAccessIsAllowed("user_b", "GET", "/_nodes/infos");
126+
// monitoring allows template retrieval (because it's implied by having read access to cluster state
127+
assertAccessIsAllowed("user_b", "GET", "/_cat/templates/" + (randomBoolean() ? "" : randomAlphaOfLengthBetween(2, 8)));
128+
assertAccessIsAllowed("user_b", "GET", "/_template/");
129+
assertAccessIsAllowed("user_b", "GET", "/_index_template/");
130+
assertAccessIsAllowed("user_b", "GET", "/_component_template/");
126131
// but no admin stuff
127132
assertAccessIsDenied("user_b", "POST", "/_cluster/reroute");
128133
assertAccessIsDenied("user_b", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }");
134+
assertAccessIsDenied("user_b", "DELETE", "/_template/" + randomAlphaOfLengthBetween(2, 8));
135+
assertAccessIsDenied("user_b", "DELETE", "/_index_template/" + randomAlphaOfLengthBetween(2, 8));
136+
assertAccessIsDenied("user_b", "DELETE", "/_component_template/" + randomAlphaOfLengthBetween(2, 8));
129137

130138
// sorry user_c, you are not allowed anything
131139
assertAccessIsDenied("user_c", "GET", "/_cluster/state");

0 commit comments

Comments
 (0)