Skip to content

Commit 9a2a59b

Browse files
authored
Allow _rollup_search with read privilege (#52043)
Currently _rollup_search requires manage privilege to access. It should really be a read only operation. This PR changes the requirement to be read indices privilege. Resolves: #50245
1 parent fbd8d79 commit 9a2a59b

File tree

3 files changed

+88
-124
lines changed

3 files changed

+88
-124
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/RollupSearchAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
public class RollupSearchAction extends ActionType<SearchResponse> {
1515

1616
public static final RollupSearchAction INSTANCE = new RollupSearchAction();
17-
public static final String NAME = "indices:admin/xpack/rollup/search";
17+
public static final String NAME = "indices:data/read/xpack/rollup/search";
1818

1919
private RollupSearchAction() {
2020
super(NAME, SearchResponse::new);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
*
3+
* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
4+
* * or more contributor license agreements. Licensed under the Elastic License;
5+
* * you may not use this file except in compliance with the Elastic License.
6+
*
7+
*/
8+
9+
package org.elasticsearch.xpack.core.rollup.action;
10+
11+
import org.elasticsearch.test.ESTestCase;
12+
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
13+
import org.elasticsearch.xpack.core.security.support.Automatons;
14+
15+
public class RollupSearchActionTests extends ESTestCase {
16+
17+
public void testIndexReadPrivilegeCanPerformRollupSearchAction() {
18+
assertTrue(Automatons.predicate(IndexPrivilege.READ.getAutomaton()).test(RollupSearchAction.NAME));
19+
}
20+
}

x-pack/plugin/ml/qa/native-multi-node-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DatafeedJobsRestIT.java

+67-123
Original file line numberDiff line numberDiff line change
@@ -497,72 +497,15 @@ public void testInsufficientSearchPrivilegesOnPut() throws Exception {
497497
containsString("user ml_admin lacks permissions on the indices"));
498498
}
499499

500-
public void testInsufficientSearchPrivilegesOnPutWithRollup() throws Exception {
500+
public void testCreationOnPutWithRollup() throws Exception {
501501
setupDataAccessRole("airline-data-aggs-rollup");
502502
String jobId = "privs-put-job-rollup";
503-
Request createJobRequest = new Request("PUT", MachineLearning.BASE_PATH + "anomaly_detectors/" + jobId);
504-
createJobRequest.setJsonEntity("{\n"
505-
+ " \"description\": \"Aggs job\",\n"
506-
+ " \"analysis_config\": {\n"
507-
+ " \"bucket_span\": \"1h\",\n"
508-
+ " \"summary_count_field_name\": \"doc_count\",\n"
509-
+ " \"detectors\": [\n"
510-
+ " {\n"
511-
+ " \"function\": \"mean\",\n"
512-
+ " \"field_name\": \"responsetime\",\n"
513-
+ " \"by_field_name\": \"airline\"\n"
514-
+ " }\n"
515-
+ " ]\n"
516-
+ " },\n"
517-
+ " \"data_description\": {\"time_field\": \"time stamp\"}\n"
518-
+ "}");
519-
client().performRequest(createJobRequest);
520-
521-
String rollupJobId = "rollup-" + jobId;
522-
Request createRollupRequest = new Request("PUT", "/_rollup/job/" + rollupJobId);
523-
createRollupRequest.setJsonEntity("{\n"
524-
+ "\"index_pattern\": \"airline-data-aggs\",\n"
525-
+ " \"rollup_index\": \"airline-data-aggs-rollup\",\n"
526-
+ " \"cron\": \"*/30 * * * * ?\",\n"
527-
+ " \"page_size\" :1000,\n"
528-
+ " \"groups\" : {\n"
529-
+ " \"date_histogram\": {\n"
530-
+ " \"field\": \"time stamp\",\n"
531-
+ " \"fixed_interval\": \"2m\",\n"
532-
+ " \"delay\": \"7d\"\n"
533-
+ " },\n"
534-
+ " \"terms\": {\n"
535-
+ " \"fields\": [\"airline\"]\n"
536-
+ " }"
537-
+ " },\n"
538-
+ " \"metrics\": [\n"
539-
+ " {\n"
540-
+ " \"field\": \"responsetime\",\n"
541-
+ " \"metrics\": [\"avg\",\"min\",\"max\",\"sum\"]\n"
542-
+ " },\n"
543-
+ " {\n"
544-
+ " \"field\": \"time stamp\",\n"
545-
+ " \"metrics\": [\"min\",\"max\"]\n"
546-
+ " }\n"
547-
+ " ]\n"
548-
+ "}");
549-
client().performRequest(createRollupRequest);
550-
551503
String datafeedId = "datafeed-" + jobId;
552-
String aggregations = "{\"buckets\":{\"date_histogram\":{\"field\":\"time stamp\",\"fixed_interval\":\"3600000ms\"},"
553-
+ "\"aggregations\":{"
554-
+ "\"time stamp\":{\"max\":{\"field\":\"time stamp\"}},"
555-
+ "\"responsetime\":{\"avg\":{\"field\":\"responsetime\"}}}}}";
556-
504+
final Response response = createJobAndDataFeed(jobId, datafeedId);
557505

558-
ResponseException e = expectThrows(ResponseException.class, () ->
559-
new DatafeedBuilder(datafeedId, jobId, "airline-data-aggs-rollup")
560-
.setAggregations(aggregations)
561-
.setAuthHeader(BASIC_AUTH_VALUE_ML_ADMIN_WITH_SOME_DATA_ACCESS) //want to search, but no admin access
562-
.build());
563-
assertThat(e.getMessage(), containsString("Cannot create datafeed"));
564-
assertThat(e.getMessage(),
565-
containsString("user ml_admin_plus_data lacks permissions on the indices"));
506+
assertEquals(200, response.getStatusLine().getStatusCode());
507+
assertThat(EntityUtils.toString(response.getEntity()), containsString("\"datafeed_id\":\"" + datafeedId
508+
+ "\",\"job_id\":\"" + jobId + "\""));
566509
}
567510

568511
public void testInsufficientSearchPrivilegesOnPreview() throws Exception {
@@ -953,67 +896,8 @@ public void testLookbackOnlyGivenAggregationsWithHistogramAndRollupIndex() throw
953896
public void testLookbackWithoutPermissionsAndRollup() throws Exception {
954897
setupFullAccessRole("airline-data-aggs-rollup");
955898
String jobId = "rollup-permission-test-network-job";
956-
Request createJobRequest = new Request("PUT", MachineLearning.BASE_PATH + "anomaly_detectors/" + jobId);
957-
createJobRequest.setJsonEntity("{\n"
958-
+ " \"description\": \"Aggs job\",\n"
959-
+ " \"analysis_config\": {\n"
960-
+ " \"bucket_span\": \"1h\",\n"
961-
+ " \"summary_count_field_name\": \"doc_count\",\n"
962-
+ " \"detectors\": [\n"
963-
+ " {\n"
964-
+ " \"function\": \"mean\",\n"
965-
+ " \"field_name\": \"responsetime\",\n"
966-
+ " \"by_field_name\": \"airline\"\n"
967-
+ " }\n"
968-
+ " ]\n"
969-
+ " },\n"
970-
+ " \"data_description\": {\"time_field\": \"time stamp\"}\n"
971-
+ "}");
972-
client().performRequest(createJobRequest);
973-
974-
String rollupJobId = "rollup-" + jobId;
975-
Request createRollupRequest = new Request("PUT", "/_rollup/job/" + rollupJobId);
976-
createRollupRequest.setJsonEntity("{\n"
977-
+ "\"index_pattern\": \"airline-data-aggs\",\n"
978-
+ " \"rollup_index\": \"airline-data-aggs-rollup\",\n"
979-
+ " \"cron\": \"*/30 * * * * ?\",\n"
980-
+ " \"page_size\" :1000,\n"
981-
+ " \"groups\" : {\n"
982-
+ " \"date_histogram\": {\n"
983-
+ " \"field\": \"time stamp\",\n"
984-
+ " \"fixed_interval\": \"2m\",\n"
985-
+ " \"delay\": \"7d\"\n"
986-
+ " },\n"
987-
+ " \"terms\": {\n"
988-
+ " \"fields\": [\"airline\"]\n"
989-
+ " }"
990-
+ " },\n"
991-
+ " \"metrics\": [\n"
992-
+ " {\n"
993-
+ " \"field\": \"responsetime\",\n"
994-
+ " \"metrics\": [\"avg\",\"min\",\"max\",\"sum\"]\n"
995-
+ " },\n"
996-
+ " {\n"
997-
+ " \"field\": \"time stamp\",\n"
998-
+ " \"metrics\": [\"min\",\"max\"]\n"
999-
+ " }\n"
1000-
+ " ]\n"
1001-
+ "}");
1002-
client().performRequest(createRollupRequest);
1003-
1004899
String datafeedId = "datafeed-" + jobId;
1005-
String aggregations = "{\"buckets\":{\"date_histogram\":{\"field\":\"time stamp\",\"fixed_interval\":\"3600000ms\"},"
1006-
+ "\"aggregations\":{"
1007-
+ "\"time stamp\":{\"max\":{\"field\":\"time stamp\"}},"
1008-
+ "\"responsetime\":{\"avg\":{\"field\":\"responsetime\"}}}}}";
1009-
1010-
1011-
// At the time we create the datafeed the user can access the network-data index that we have access to
1012-
new DatafeedBuilder(datafeedId, jobId, "airline-data-aggs-rollup")
1013-
.setAggregations(aggregations)
1014-
.setChunkingTimespan("300s")
1015-
.setAuthHeader(BASIC_AUTH_VALUE_ML_ADMIN_WITH_SOME_DATA_ACCESS)
1016-
.build();
900+
createJobAndDataFeed(jobId, datafeedId);
1017901

1018902
// Change the role so that the user can no longer access network-data
1019903
setupFullAccessRole("some-other-data");
@@ -1028,7 +912,7 @@ public void testLookbackWithoutPermissionsAndRollup() throws Exception {
1028912
new Request("GET", NotificationsIndex.NOTIFICATIONS_INDEX + "/_search?size=1000&q=job_id:" + jobId));
1029913
String notificationsResponseAsString = EntityUtils.toString(notificationsResponse.getEntity());
1030914
assertThat(notificationsResponseAsString, containsString("\"message\":\"Datafeed is encountering errors extracting data: " +
1031-
"action [indices:admin/xpack/rollup/search] is unauthorized for user [ml_admin_plus_data]\""));
915+
"action [indices:data/read/xpack/rollup/search] is unauthorized for user [ml_admin_plus_data]\""));
1032916
}
1033917

1034918
public void testLookbackWithSingleBucketAgg() throws Exception {
@@ -1362,4 +1246,64 @@ private void bulkIndex(String bulk) throws IOException {
13621246
String bulkResponse = EntityUtils.toString(client().performRequest(bulkRequest).getEntity());
13631247
assertThat(bulkResponse, not(containsString("\"errors\": false")));
13641248
}
1249+
1250+
private Response createJobAndDataFeed(String jobId, String datafeedId) throws IOException {
1251+
Request createJobRequest = new Request("PUT", MachineLearning.BASE_PATH + "anomaly_detectors/" + jobId);
1252+
createJobRequest.setJsonEntity("{\n"
1253+
+ " \"description\": \"Aggs job\",\n"
1254+
+ " \"analysis_config\": {\n"
1255+
+ " \"bucket_span\": \"1h\",\n"
1256+
+ " \"summary_count_field_name\": \"doc_count\",\n"
1257+
+ " \"detectors\": [\n"
1258+
+ " {\n"
1259+
+ " \"function\": \"mean\",\n"
1260+
+ " \"field_name\": \"responsetime\",\n"
1261+
+ " \"by_field_name\": \"airline\"\n"
1262+
+ " }\n"
1263+
+ " ]\n"
1264+
+ " },\n"
1265+
+ " \"data_description\": {\"time_field\": \"time stamp\"}\n"
1266+
+ "}");
1267+
client().performRequest(createJobRequest);
1268+
1269+
String rollupJobId = "rollup-" + jobId;
1270+
Request createRollupRequest = new Request("PUT", "/_rollup/job/" + rollupJobId);
1271+
createRollupRequest.setJsonEntity("{\n"
1272+
+ "\"index_pattern\": \"airline-data-aggs\",\n"
1273+
+ " \"rollup_index\": \"airline-data-aggs-rollup\",\n"
1274+
+ " \"cron\": \"*/30 * * * * ?\",\n"
1275+
+ " \"page_size\" :1000,\n"
1276+
+ " \"groups\" : {\n"
1277+
+ " \"date_histogram\": {\n"
1278+
+ " \"field\": \"time stamp\",\n"
1279+
+ " \"fixed_interval\": \"2m\",\n"
1280+
+ " \"delay\": \"7d\"\n"
1281+
+ " },\n"
1282+
+ " \"terms\": {\n"
1283+
+ " \"fields\": [\"airline\"]\n"
1284+
+ " }"
1285+
+ " },\n"
1286+
+ " \"metrics\": [\n"
1287+
+ " {\n"
1288+
+ " \"field\": \"responsetime\",\n"
1289+
+ " \"metrics\": [\"avg\",\"min\",\"max\",\"sum\"]\n"
1290+
+ " },\n"
1291+
+ " {\n"
1292+
+ " \"field\": \"time stamp\",\n"
1293+
+ " \"metrics\": [\"min\",\"max\"]\n"
1294+
+ " }\n"
1295+
+ " ]\n"
1296+
+ "}");
1297+
client().performRequest(createRollupRequest);
1298+
1299+
String aggregations = "{\"buckets\":{\"date_histogram\":{\"field\":\"time stamp\",\"fixed_interval\":\"3600000ms\"},"
1300+
+ "\"aggregations\":{"
1301+
+ "\"time stamp\":{\"max\":{\"field\":\"time stamp\"}},"
1302+
+ "\"responsetime\":{\"avg\":{\"field\":\"responsetime\"}}}}}";
1303+
1304+
return new DatafeedBuilder(datafeedId, jobId, "airline-data-aggs-rollup")
1305+
.setAggregations(aggregations)
1306+
.setAuthHeader(BASIC_AUTH_VALUE_ML_ADMIN_WITH_SOME_DATA_ACCESS)
1307+
.build();
1308+
}
13651309
}

0 commit comments

Comments
 (0)