@@ -68,6 +68,8 @@ protected Settings restAdminSettings() {
6868 private static final String FAILURE_STORE_ACCESS_USER = "failure_store_access_user" ;
6969 private static final String BOTH_ACCESS_USER = "both_access_user" ;
7070 private static final String WRITE_ACCESS_USER = "write_access_user" ;
71+ private static final String MANAGE_ACCESS_USER = "manage_access_user" ;
72+ private static final String MANAGE_FAILURE_STORE_ACCESS_USER = "manage_failure_store_access_user" ;
7173 private static final SecureString PASSWORD = new SecureString ("elastic-password" );
7274
7375 public void testGetUserPrivileges () throws IOException {
@@ -167,12 +169,16 @@ public void testFailureStoreAccess() throws IOException {
167169 String failureStoreAccessRole = "failure_store_access" ;
168170 String bothAccessRole = "both_access" ;
169171 String writeAccessRole = "write_access" ;
172+ String manageAccessRole = "manage_access" ;
173+ String manageFailureStoreRole = "manage_failure_store_access" ;
170174
171175 createUser (DATA_ACCESS_USER , PASSWORD , List .of (dataAccessRole ));
172176 createUser (STAR_READ_ONLY_USER , PASSWORD , List .of (starReadOnlyRole ));
173177 createUser (FAILURE_STORE_ACCESS_USER , PASSWORD , List .of (failureStoreAccessRole ));
174178 createUser (BOTH_ACCESS_USER , PASSWORD , randomBoolean () ? List .of (bothAccessRole ) : List .of (dataAccessRole , failureStoreAccessRole ));
175179 createUser (WRITE_ACCESS_USER , PASSWORD , List .of (writeAccessRole ));
180+ createUser (MANAGE_ACCESS_USER , PASSWORD , List .of (manageAccessRole ));
181+ createUser (MANAGE_FAILURE_STORE_ACCESS_USER , PASSWORD , List .of (manageFailureStoreRole ));
176182
177183 upsertRole (Strings .format ("""
178184 {
@@ -204,6 +210,18 @@ public void testFailureStoreAccess() throws IOException {
204210 "cluster": ["all"],
205211 "indices": [{"names": ["test*"], "privileges": ["write", "auto_configure"]}]
206212 }""" ), writeAccessRole );
213+ upsertRole (Strings .format ("""
214+ {
215+ "description": "Role with regular manage access without failure store access",
216+ "cluster": ["all"],
217+ "indices": [{"names": ["test*"], "privileges": ["manage"]}]
218+ }""" ), manageAccessRole );
219+ upsertRole (Strings .format ("""
220+ {
221+ "description": "Role with failure store manage access",
222+ "cluster": ["all"],
223+ "indices": [{"names": ["test*"], "privileges": ["manage_failure_store"]}]
224+ }""" ), manageFailureStoreRole );
207225
208226 createTemplates ();
209227 List <String > docIds = populateDataStreamWithBulkRequest ();
@@ -240,7 +258,6 @@ public void testFailureStoreAccess() throws IOException {
240258 performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/" + failureIndexName + "/_search" )),
241259 failedDocId
242260 );
243- // TODO fix me
244261 assertContainsDocIds (
245262 performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/" + failureIndexName + "/_search?ignore_unavailable=true" )),
246263 failedDocId
@@ -263,9 +280,9 @@ public void testFailureStoreAccess() throws IOException {
263280 assertEmpty (
264281 performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/" + dataIndexName + "/_search?ignore_unavailable=true" ))
265282 );
283+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/.ds*/_search" )));
266284 assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/*1::data/_search" )));
267285 assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/*1/_search" )));
268- assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/.ds*/_search" )));
269286
270287 // user with access to data index
271288 assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test1/_search" )), successDocId );
@@ -324,16 +341,35 @@ public void testFailureStoreAccess() throws IOException {
324341
325342 assertEmpty (performRequest (BOTH_ACCESS_USER , new Request ("GET" , "/test12/_search?ignore_unavailable=true" )));
326343 assertEmpty (performRequest (BOTH_ACCESS_USER , new Request ("GET" , "/test2/_search?ignore_unavailable=true" )));
344+
345+ // TODO extra make sure manage_failure_store CANNOT delete the whole data stream
346+
347+ // user with manage access to data stream does NOT get direct access to failure index
348+ expectThrows403 (() -> deleteIndex (MANAGE_ACCESS_USER , failureIndexName ));
349+ expectThrows (() -> deleteIndex (MANAGE_ACCESS_USER , dataIndexName ), 400 );
350+ // manage_failure_store user COULD delete failure index (not valid because it's a write index, but allow security-wise)
351+ expectThrows403 (() -> deleteIndex (MANAGE_FAILURE_STORE_ACCESS_USER , dataIndexName ));
352+ expectThrows (() -> deleteIndex (MANAGE_FAILURE_STORE_ACCESS_USER , failureIndexName ), 400 );
353+ expectThrows403 (() -> deleteDataStream (MANAGE_FAILURE_STORE_ACCESS_USER , dataIndexName ));
354+
355+ // manage user can delete data stream
356+ deleteDataStream (MANAGE_ACCESS_USER , "test1" );
357+
358+ expectThrows404 (() -> performRequest (BOTH_ACCESS_USER , new Request ("GET" , "/test1/_search" )));
359+ expectThrows404 (() -> performRequest (BOTH_ACCESS_USER , new Request ("GET" , "/test1::failures/_search" )));
327360 }
328361
329362 private static void expectThrows404 (ThrowingRunnable get ) {
330- var ex = expectThrows (ResponseException .class , get );
331- assertThat (ex .getResponse ().getStatusLine ().getStatusCode (), equalTo (404 ));
363+ expectThrows (get , 404 );
332364 }
333365
334366 private static void expectThrows403 (ThrowingRunnable get ) {
367+ expectThrows (get , 403 );
368+ }
369+
370+ private static void expectThrows (ThrowingRunnable get , int statusCode ) {
335371 var ex = expectThrows (ResponseException .class , get );
336- assertThat (ex .getResponse ().getStatusLine ().getStatusCode (), equalTo (403 ));
372+ assertThat (ex .getResponse ().getStatusLine ().getStatusCode (), equalTo (statusCode ));
337373 }
338374
339375 @ SuppressWarnings ("unchecked" )
@@ -428,6 +464,14 @@ private List<String> populateDataStreamWithBulkRequest() throws IOException {
428464 return ids ;
429465 }
430466
467+ private void deleteDataStream (String user , String dataStreamName ) throws IOException {
468+ assertOK (performRequest (user , new Request ("DELETE" , "/_data_stream/" + dataStreamName )));
469+ }
470+
471+ private void deleteIndex (String user , String indexName ) throws IOException {
472+ assertOK (performRequest (user , new Request ("DELETE" , "/" + indexName )));
473+ }
474+
431475 private Response performRequest (String user , Request request ) throws IOException {
432476 request .setOptions (RequestOptions .DEFAULT .toBuilder ().addHeader ("Authorization" , basicAuthHeaderValue (user , PASSWORD )).build ());
433477 return client ().performRequest (request );
0 commit comments