Skip to content

Commit dd6f489

Browse files
Spotless apply
Signed-off-by: Finn Carroll <carrofin@amazon.com>
1 parent dc53c24 commit dd6f489

1 file changed

Lines changed: 89 additions & 57 deletions

File tree

integ-test/src/test/java/org/opensearch/sql/security/AnalyticsEngineSecurityIT.java

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
/**
1818
* Integration tests for analytics engine index-level authorization via the production SQL plugin
19-
* PPL endpoint. Verifies that queries on composite (analytics-engine-backed) indices are subject
20-
* to the {@code indices:data/read/analytics/query} permission check.
19+
* PPL endpoint. Verifies that queries on composite (analytics-engine-backed) indices are subject to
20+
* the {@code indices:data/read/analytics/query} permission check.
2121
*/
2222
public class AnalyticsEngineSecurityIT extends SecurityTestBase {
2323

@@ -55,8 +55,9 @@ private void waitForSecurityPlugin() throws Exception {
5555
try {
5656
Request req = new Request("GET", "/_plugins/_security/api/roles");
5757
RequestOptions.Builder opts = RequestOptions.DEFAULT.toBuilder();
58-
opts.addHeader("Authorization", "Basic " +
59-
java.util.Base64.getEncoder().encodeToString("admin:admin".getBytes()));
58+
opts.addHeader(
59+
"Authorization",
60+
"Basic " + java.util.Base64.getEncoder().encodeToString("admin:admin".getBytes()));
6061
req.setOptions(opts);
6162
Response resp = client().performRequest(req);
6263
if (resp.getStatusLine().getStatusCode() == 200) return;
@@ -74,10 +75,13 @@ private void createTestIndices() throws IOException {
7475
createCompositeIndex(TEST_INDEX);
7576
Request bulk = new Request("POST", "/_bulk");
7677
bulk.addParameter("refresh", "true");
77-
bulk.setJsonEntity(String.format(Locale.ROOT,
78-
"{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"alice\", \"age\": 30}\n"
79-
+ "{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"bob\", \"age\": 25}\n",
80-
TEST_INDEX, TEST_INDEX));
78+
bulk.setJsonEntity(
79+
String.format(
80+
Locale.ROOT,
81+
"{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"alice\", \"age\": 30}\n"
82+
+ "{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"bob\", \"age\": 25}\n",
83+
TEST_INDEX,
84+
TEST_INDEX));
8185
RequestOptions.Builder opts = RequestOptions.DEFAULT.toBuilder();
8286
opts.addHeader("Content-Type", "application/x-ndjson");
8387
bulk.setOptions(opts);
@@ -86,17 +90,20 @@ private void createTestIndices() throws IOException {
8690
createCompositeIndex(FORBIDDEN_INDEX);
8791
Request bulkF = new Request("POST", "/_bulk");
8892
bulkF.addParameter("refresh", "true");
89-
bulkF.setJsonEntity(String.format(Locale.ROOT,
90-
"{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"secret\", \"age\": 99}\n",
91-
FORBIDDEN_INDEX));
93+
bulkF.setJsonEntity(
94+
String.format(
95+
Locale.ROOT,
96+
"{\"index\": {\"_index\": \"%s\"}}\n{\"name\": \"secret\", \"age\": 99}\n",
97+
FORBIDDEN_INDEX));
9298
bulkF.setOptions(opts);
9399
client().performRequest(bulkF);
94100
}
95101

96102
private void createCompositeIndex(String index) throws IOException {
97103
try {
98104
Request req = new Request("PUT", "/" + index);
99-
req.setJsonEntity("""
105+
req.setJsonEntity(
106+
"""
100107
{
101108
"settings": {
102109
"number_of_shards": 1,
@@ -121,9 +128,7 @@ private void createSecurityRolesAndUsers() throws IOException {
121128
TEST_INDEX,
122129
new String[] {"cluster:admin/opensearch/ppl", "cluster:admin/opensearch/sql"},
123130
new String[] {
124-
"indices:data/read*",
125-
"indices:admin/mappings/get",
126-
"indices:monitor/settings/get"
131+
"indices:data/read*", "indices:admin/mappings/get", "indices:monitor/settings/get"
127132
});
128133
createUser(ALLOWED_USER, ALLOWED_ROLE);
129134

@@ -133,9 +138,7 @@ private void createSecurityRolesAndUsers() throws IOException {
133138
"some_other_index",
134139
new String[] {"cluster:admin/opensearch/ppl", "cluster:admin/opensearch/sql"},
135140
new String[] {
136-
"indices:data/read*",
137-
"indices:admin/mappings/get",
138-
"indices:monitor/settings/get"
141+
"indices:data/read*", "indices:admin/mappings/get", "indices:monitor/settings/get"
139142
});
140143
createUser(DENIED_USER, DENIED_ROLE);
141144

@@ -160,9 +163,7 @@ private void createSecurityRolesAndUsers() throws IOException {
160163
"analytics_security*",
161164
new String[] {"cluster:admin/opensearch/ppl", "cluster:admin/opensearch/sql"},
162165
new String[] {
163-
"indices:data/read*",
164-
"indices:admin/mappings/get",
165-
"indices:monitor/settings/get"
166+
"indices:data/read*", "indices:admin/mappings/get", "indices:monitor/settings/get"
166167
});
167168
createUser(WILDCARD_USER, WILDCARD_ROLE);
168169
}
@@ -172,27 +173,34 @@ public void testPPLQueryAllowedForAuthorizedUser() throws IOException {
172173
// Verify the request passes SecurityFilter (not 403). The query may fail post-auth
173174
// if the backend can't execute, but the FGAC check itself succeeded.
174175
try {
175-
JSONObject result = executePPLAsUser(
176-
"source = " + TEST_INDEX + " | fields name, age", ALLOWED_USER);
176+
JSONObject result =
177+
executePPLAsUser("source = " + TEST_INDEX + " | fields name, age", ALLOWED_USER);
177178
assertTrue("Expected datarows in response", result.has("datarows"));
178179
} catch (ResponseException e) {
179180
assertNotEquals(
180181
"Expected auth to pass (not 403) for authorized user",
181-
403, e.getResponse().getStatusLine().getStatusCode());
182+
403,
183+
e.getResponse().getStatusLine().getStatusCode());
182184
}
183185
}
184186

185187
@Test
186188
public void testPPLQueryDeniedForUnauthorizedUser() throws IOException {
187-
ResponseException e = assertThrows(ResponseException.class, () ->
188-
executePPLAsUser("source = " + TEST_INDEX + " | fields name, age", DENIED_USER));
189+
ResponseException e =
190+
assertThrows(
191+
ResponseException.class,
192+
() -> executePPLAsUser("source = " + TEST_INDEX + " | fields name, age", DENIED_USER));
189193
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
190194
}
191195

192196
@Test
193197
public void testPPLQueryDeniedForForbiddenIndex() throws IOException {
194-
ResponseException e = assertThrows(ResponseException.class, () ->
195-
executePPLAsUser("source = " + FORBIDDEN_INDEX + " | fields name, age", ALLOWED_USER));
198+
ResponseException e =
199+
assertThrows(
200+
ResponseException.class,
201+
() ->
202+
executePPLAsUser(
203+
"source = " + FORBIDDEN_INDEX + " | fields name, age", ALLOWED_USER));
196204
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
197205
}
198206

@@ -201,8 +209,12 @@ public void testPPLQueryDeniedWithSearchPermissionOnly() throws IOException {
201209
// User has indices:data/read/search* but NOT indices:data/read/analytics/query.
202210
// The analytics engine dispatches through AnalyticsQueryAction which requires the
203211
// specific analytics/query permission — search permission alone is insufficient.
204-
ResponseException e = assertThrows(ResponseException.class, () ->
205-
executePPLAsUser("source = " + TEST_INDEX + " | fields name, age", SEARCH_ONLY_USER));
212+
ResponseException e =
213+
assertThrows(
214+
ResponseException.class,
215+
() ->
216+
executePPLAsUser(
217+
"source = " + TEST_INDEX + " | fields name, age", SEARCH_ONLY_USER));
206218
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
207219
}
208220

@@ -211,36 +223,43 @@ public void testPPLQueryAllowedWithWildcardPermission() throws IOException {
211223
// User's role has index_patterns: ["analytics_security*"] which should match
212224
// "analytics_security_test" via wildcard expansion in the security plugin.
213225
try {
214-
JSONObject result = executePPLAsUser(
215-
"source = " + TEST_INDEX + " | fields name, age", WILDCARD_USER);
226+
JSONObject result =
227+
executePPLAsUser("source = " + TEST_INDEX + " | fields name, age", WILDCARD_USER);
216228
assertTrue("Expected datarows in response", result.has("datarows"));
217229
} catch (ResponseException e) {
218230
assertNotEquals(
219231
"Expected auth to pass (not 403) for wildcard-permitted user",
220-
403, e.getResponse().getStatusLine().getStatusCode());
232+
403,
233+
e.getResponse().getStatusLine().getStatusCode());
221234
}
222235
}
223236

224237
@Test
225238
public void testPPLQueryDeniedWithWildcardPermissionOnNonMatchingIndex() throws IOException {
226239
// User's role has index_patterns: ["analytics_security*"] which should NOT match
227240
// "analytics_forbidden_test".
228-
ResponseException e = assertThrows(ResponseException.class, () ->
229-
executePPLAsUser("source = " + FORBIDDEN_INDEX + " | fields name, age", WILDCARD_USER));
241+
ResponseException e =
242+
assertThrows(
243+
ResponseException.class,
244+
() ->
245+
executePPLAsUser(
246+
"source = " + FORBIDDEN_INDEX + " | fields name, age", WILDCARD_USER));
230247
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
231248
}
232249

233250
@Test
234251
public void testSQLQueryAllowedForAuthorizedUser() throws IOException {
235252
try {
236-
JSONObject result = executeSQLAsUser(
237-
"SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", ALLOWED_USER);
238-
assertTrue("Expected datarows or schema in response",
253+
JSONObject result =
254+
executeSQLAsUser("SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", ALLOWED_USER);
255+
assertTrue(
256+
"Expected datarows or schema in response",
239257
result.has("datarows") || result.has("schema"));
240258
} catch (ResponseException e) {
241259
assertNotEquals(
242260
"Expected auth to pass (not 403) for authorized user",
243-
403, e.getResponse().getStatusLine().getStatusCode());
261+
403,
262+
e.getResponse().getStatusLine().getStatusCode());
244263
}
245264
}
246265

@@ -252,34 +271,49 @@ public void testSQLQueryAllowedForAuthorizedUser() throws IOException {
252271

253272
@Test
254273
public void testSQLQueryDeniedForUnauthorizedUser() throws IOException {
255-
ResponseException e = assertThrows(ResponseException.class, () ->
256-
executeSQLAsUser("SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", DENIED_USER));
257-
assertTrue("Expected 403 or 500 with security exception, got " + e.getResponse().getStatusLine().getStatusCode(),
274+
ResponseException e =
275+
assertThrows(
276+
ResponseException.class,
277+
() ->
278+
executeSQLAsUser("SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", DENIED_USER));
279+
assertTrue(
280+
"Expected 403 or 500 with security exception, got "
281+
+ e.getResponse().getStatusLine().getStatusCode(),
258282
e.getResponse().getStatusLine().getStatusCode() == 403
259-
|| e.getResponse().getStatusLine().getStatusCode() == 500);
283+
|| e.getResponse().getStatusLine().getStatusCode() == 500);
260284
}
261285

262286
@Test
263287
public void testSQLQueryDeniedForForbiddenIndex() throws IOException {
264-
ResponseException e = assertThrows(ResponseException.class, () ->
265-
executeSQLAsUser("SELECT name, age FROM " + FORBIDDEN_INDEX + " LIMIT 3", ALLOWED_USER));
266-
assertTrue("Expected 403 or 500 with security exception, got " + e.getResponse().getStatusLine().getStatusCode(),
288+
ResponseException e =
289+
assertThrows(
290+
ResponseException.class,
291+
() ->
292+
executeSQLAsUser(
293+
"SELECT name, age FROM " + FORBIDDEN_INDEX + " LIMIT 3", ALLOWED_USER));
294+
assertTrue(
295+
"Expected 403 or 500 with security exception, got "
296+
+ e.getResponse().getStatusLine().getStatusCode(),
267297
e.getResponse().getStatusLine().getStatusCode() == 403
268-
|| e.getResponse().getStatusLine().getStatusCode() == 500);
298+
|| e.getResponse().getStatusLine().getStatusCode() == 500);
269299
}
270300

271301
@Test
272302
public void testSQLQueryDeniedWithSearchPermissionOnly() throws IOException {
273-
ResponseException e = assertThrows(ResponseException.class, () ->
274-
executeSQLAsUser("SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", SEARCH_ONLY_USER));
275-
assertTrue("Expected 403 or 500 with security exception, got " + e.getResponse().getStatusLine().getStatusCode(),
303+
ResponseException e =
304+
assertThrows(
305+
ResponseException.class,
306+
() ->
307+
executeSQLAsUser(
308+
"SELECT name, age FROM " + TEST_INDEX + " LIMIT 3", SEARCH_ONLY_USER));
309+
assertTrue(
310+
"Expected 403 or 500 with security exception, got "
311+
+ e.getResponse().getStatusLine().getStatusCode(),
276312
e.getResponse().getStatusLine().getStatusCode() == 403
277-
|| e.getResponse().getStatusLine().getStatusCode() == 500);
313+
|| e.getResponse().getStatusLine().getStatusCode() == 500);
278314
}
279315

280-
/**
281-
* Executes a PPL query via the production SQL plugin endpoint (/_plugins/_ppl).
282-
*/
316+
/** Executes a PPL query via the production SQL plugin endpoint (/_plugins/_ppl). */
283317
private JSONObject executePPLAsUser(String query, String username) throws IOException {
284318
Request request = new Request("POST", "/_plugins/_ppl");
285319
request.setJsonEntity(String.format(Locale.ROOT, "{\"query\": \"%s\"}", query));
@@ -295,9 +329,7 @@ private JSONObject executePPLAsUser(String query, String username) throws IOExce
295329
return new JSONObject(body);
296330
}
297331

298-
/**
299-
* Executes a SQL query via the production SQL plugin endpoint (/_plugins/_sql).
300-
*/
332+
/** Executes a SQL query via the production SQL plugin endpoint (/_plugins/_sql). */
301333
private JSONObject executeSQLAsUser(String query, String username) throws IOException {
302334
Request request = new Request("POST", "/_plugins/_sql");
303335
request.setJsonEntity(String.format(Locale.ROOT, "{\"query\": \"%s\"}", query));

0 commit comments

Comments
 (0)