1313import org .elasticsearch .client .Response ;
1414import org .elasticsearch .client .ResponseException ;
1515import org .elasticsearch .common .settings .SecureString ;
16+ import org .elasticsearch .common .xcontent .support .XContentMapValues ;
1617import org .elasticsearch .core .Strings ;
1718import org .elasticsearch .search .SearchHit ;
1819import org .elasticsearch .search .SearchResponseUtils ;
1920
2021import java .io .IOException ;
2122import java .util .ArrayList ;
2223import java .util .Arrays ;
24+ import java .util .Collections ;
2325import java .util .List ;
2426import java .util .Map ;
2527
2931
3032public class FailureStoreSecurityRestIT extends SecurityOnTrialLicenseRestTestCase {
3133
32- private static final String USER = "user" ;
34+ private static final String DATA_ACCESS_USER = "data_access_user" ;
35+ private static final String FAILURE_STORE_ACCESS_USER = "failure_store_access_user" ;
3336 private static final SecureString PASSWORD = new SecureString ("elastic-password" );
3437
38+ @ SuppressWarnings ("unchecked" )
3539 public void testFailureStoreAccess () throws IOException {
40+ String dataAccessRole = "data_access" ;
3641 String failureStoreAccessRole = "failure_store_access" ;
37- createUser (USER , PASSWORD , List .of (failureStoreAccessRole ));
42+
43+ createUser (DATA_ACCESS_USER , PASSWORD , List .of (dataAccessRole ));
44+ createUser (FAILURE_STORE_ACCESS_USER , PASSWORD , List .of (failureStoreAccessRole ));
3845
46+ upsertRole (Strings .format ("""
47+ {
48+ "description": "Role with data access",
49+ "cluster": ["all"],
50+ "indices": [{"names": ["test*"], "privileges": ["read"]}]
51+ }""" ), dataAccessRole );
3952 upsertRole (Strings .format ("""
4053 {
4154 "description": "Role with failure store access",
4255 "cluster": ["all"],
43- "indices": [{"names": ["test*::failures"], "privileges": ["read"]}]
56+ "indices": [{"names": ["test*::failures", ".failure_store_access_marker" ], "privileges": ["read"]}]
4457 }""" ), failureStoreAccessRole );
4558
4659 createTemplates ();
47- List <String > ids = populateDataStreamWithBulkRequest ();
48- assertThat (ids .size (), equalTo (2 ));
49- assertThat (ids , hasItem ("1" ));
60+ List <String > docIds = populateDataStreamWithBulkRequest ();
61+ assertThat (docIds .size (), equalTo (2 ));
62+ assertThat (docIds , hasItem ("1" ));
5063 String successDocId = "1" ;
51- String failedDocId = ids .stream ().filter (id -> false == id .equals (successDocId )).findFirst ().get ();
64+ String failedDocId = docIds .stream ().filter (id -> false == id .equals (successDocId )).findFirst ().get ();
65+
66+ Request dataStream = new Request ("GET" , "/_data_stream/test1" );
67+ Response response = adminClient ().performRequest (dataStream );
68+ Map <String , Object > dataStreams = entityAsMap (response );
69+ assertEquals (Collections .singletonList ("test1" ), XContentMapValues .extractValue ("data_streams.name" , dataStreams ));
70+ List <String > dataIndexNames = (List <String >) XContentMapValues .extractValue ("data_streams.indices.index_name" , dataStreams );
71+ assertThat (dataIndexNames .size (), equalTo (1 ));
72+ List <String > failureIndexNames = (List <String >) XContentMapValues .extractValue (
73+ "data_streams.failure_store.indices.index_name" ,
74+ dataStreams
75+ );
76+ assertThat (failureIndexNames .size (), equalTo (1 ));
77+
78+ String dataIndexName = dataIndexNames .get (0 );
79+ String failureIndexName = failureIndexNames .get (0 );
5280
5381 // user with access to failures index
54- assertContainsDocIds (performRequestAsUser1 (new Request ("GET" , "/test1::failures/_search" )), failedDocId );
55- assertContainsDocIds (performRequestAsUser1 (new Request ("GET" , "/test*::failures/_search" )), failedDocId );
56- assertContainsDocIds (performRequestAsUser1 (new Request ("GET" , "/*1::failures/_search" )), failedDocId );
57- assertContainsDocIds (performRequestAsUser1 (new Request ("GET" , "/*::failures/_search" )), failedDocId );
58- assertContainsDocIds (performRequestAsUser1 (new Request ("GET" , "/.fs*/_search" )), failedDocId );
82+ assertContainsDocIds (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test1::failures/_search" )), failedDocId );
83+ assertContainsDocIds (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test*::failures/_search" )), failedDocId );
84+ assertContainsDocIds (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/*1::failures/_search" )), failedDocId );
85+ assertContainsDocIds (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/*::failures/_search" )), failedDocId );
86+ assertContainsDocIds (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/.fs*/_search" )), failedDocId );
87+ assertContainsDocIds (
88+ performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/" + failureIndexName + "/_search" )),
89+ failedDocId
90+ );
91+ // TODO fix this
92+ // assertContainsDocIds(
93+ // performRequest(FAILURE_STORE_ACCESS_USER, new Request("GET", "/" + failureIndexName + "/_search?ignore_unavailable=true")),
94+ // failedDocId
95+ // );
5996
60- expectThrows404 (() -> performRequestAsUser1 (new Request ("GET" , "/test12::failures/_search" )));
61- expectThrows404 (() -> performRequestAsUser1 (new Request ("GET" , "/test2::failures/_search" )));
97+ expectThrows404 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test12::failures/_search" )));
98+ expectThrows404 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test2::failures/_search" )));
99+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test12::*/_search" )));
100+
101+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test1::data/_search" )));
102+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test1/_search" )));
103+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test2::data/_search" )));
104+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test2/_search" )));
105+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/" + dataIndexName + "/_search" )));
106+
107+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test1::data/_search?ignore_unavailable=true" )));
108+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test1/_search?ignore_unavailable=true" )));
109+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test2::data/_search?ignore_unavailable=true" )));
110+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test2/_search?ignore_unavailable=true" )));
111+ // TODO fix this
112+ // assertEmpty(
113+ // performRequest(FAILURE_STORE_ACCESS_USER, new Request("GET", "/" + dataIndexName + "/_search?ignore_unavailable=true"))
114+ // );
115+
116+ // assertEmpty(performRequest(FAILURE_STORE_ACCESS_USER, new Request("GET", "/*1::data/_search")));
117+ // assertEmpty(performRequest(FAILURE_STORE_ACCESS_USER, new Request("GET", "/*1/_search")));
118+ // TODO is this correct?
119+ assertEmpty (performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/.ds*/_search" )));
120+
121+ // user with access to data index
122+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test1/_search" )), successDocId );
123+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test*/_search" )), successDocId );
124+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/*1/_search" )), successDocId );
125+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/*/_search" )), successDocId );
126+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/.ds*/_search" )), successDocId );
127+ assertContainsDocIds (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/" + dataIndexName + "/_search" )), successDocId );
128+ assertContainsDocIds (
129+ performRequest (DATA_ACCESS_USER , new Request ("GET" , "/" + dataIndexName + "/_search?ignore_unavailable=true" )),
130+ successDocId
131+ );
132+
133+ expectThrows404 (() -> performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test12/_search" )));
134+ expectThrows404 (() -> performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test2/_search" )));
135+ expectThrows403 (() -> performRequest (FAILURE_STORE_ACCESS_USER , new Request ("GET" , "/test12::*/_search" )));
136+
137+ expectThrows403 (() -> performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test1::failures/_search" )));
138+ expectThrows403 (() -> performRequest (DATA_ACCESS_USER , new Request ("GET" , "/test2::failures/_search" )));
139+ expectThrows403 (() -> performRequest (DATA_ACCESS_USER , new Request ("GET" , "/" + failureIndexName + "/_search" )));
140+ // TODO is this correct?
141+ assertEmpty (performRequest (DATA_ACCESS_USER , new Request ("GET" , "/.fs*/_search" )));
142+ // assertEmpty(performRequest(DATA_ACCESS_USER, new Request("GET", "/*1::failures/_search")));
62143
63144 // user with access to everything
64145 assertContainsDocIds (adminClient ().performRequest (new Request ("GET" , "/test1::failures/_search" )), failedDocId );
@@ -76,6 +157,11 @@ private static void expectThrows404(ThrowingRunnable get) {
76157 assertThat (ex .getResponse ().getStatusLine ().getStatusCode (), equalTo (404 ));
77158 }
78159
160+ private static void expectThrows403 (ThrowingRunnable get ) {
161+ var ex = expectThrows (ResponseException .class , get );
162+ assertThat (ex .getResponse ().getStatusLine ().getStatusCode (), equalTo (403 ));
163+ }
164+
79165 @ SuppressWarnings ("unchecked" )
80166 private static void assertContainsDocIds (Response response , String ... docIds ) throws IOException {
81167 assertOK (response );
@@ -90,12 +176,15 @@ private static void assertContainsDocIds(Response response, String... docIds) th
90176 }
91177 }
92178
93- private static void assert404 (Response response ) {
94- assertThat (response .getStatusLine ().getStatusCode (), equalTo (404 ));
95- }
96-
97- private static void assert403 (Response response ) {
98- assertThat (response .getStatusLine ().getStatusCode (), equalTo (403 ));
179+ private static void assertEmpty (Response response ) throws IOException {
180+ assertOK (response );
181+ final SearchResponse searchResponse = SearchResponseUtils .parseSearchResponse (responseAsParser (response ));
182+ try {
183+ SearchHit [] hits = searchResponse .getHits ().getHits ();
184+ assertThat (hits .length , equalTo (0 ));
185+ } finally {
186+ searchResponse .decRef ();
187+ }
99188 }
100189
101190 private void createTemplates () throws IOException {
@@ -120,10 +209,10 @@ private void createTemplates() throws IOException {
120209 }
121210 },
122211 "data_stream_options": {
123- "failure_store": {
124- "enabled": true
125- }
126- }
212+ "failure_store": {
213+ "enabled": true
214+ }
215+ }
127216 }
128217 }
129218 """ );
@@ -165,10 +254,8 @@ private List<String> populateDataStreamWithBulkRequest() throws IOException {
165254 return ids ;
166255 }
167256
168- private Response performRequestAsUser1 (Request request ) throws IOException {
169- request .setOptions (RequestOptions .DEFAULT .toBuilder ().addHeader ("Authorization" , basicAuthHeaderValue (USER , PASSWORD )).build ());
170- var response = client ().performRequest (request );
171- return response ;
257+ private Response performRequest (String user , Request request ) throws IOException {
258+ request .setOptions (RequestOptions .DEFAULT .toBuilder ().addHeader ("Authorization" , basicAuthHeaderValue (user , PASSWORD )).build ());
259+ return client ().performRequest (request );
172260 }
173-
174261}
0 commit comments