4242import static org .elasticsearch .test .MapMatcher .matchesMap ;
4343import static org .hamcrest .Matchers .containsString ;
4444import static org .hamcrest .Matchers .equalTo ;
45+ import static org .hamcrest .Matchers .not ;
4546
4647public class EsqlSecurityIT extends ESRestTestCase {
4748 @ ClassRule
@@ -69,6 +70,8 @@ public class EsqlSecurityIT extends ESRestTestCase {
6970 .user ("logs_foo_after_2021" , "x-pack-test-password" , "logs_foo_after_2021" , false )
7071 .user ("logs_foo_after_2021_pattern" , "x-pack-test-password" , "logs_foo_after_2021_pattern" , false )
7172 .user ("logs_foo_after_2021_alias" , "x-pack-test-password" , "logs_foo_after_2021_alias" , false )
73+ .user ("user_without_monitor_privileges" , "x-pack-test-password" , "user_without_monitor_privileges" , false )
74+ .user ("user_with_monitor_privileges" , "x-pack-test-password" , "user_with_monitor_privileges" , false )
7275 .build ();
7376
7477 @ Override
@@ -309,7 +312,7 @@ public void testIndexPatternErrorMessageComparison_ESQL_SearchDSL() throws Excep
309312 json .endObject ();
310313 Request searchRequest = new Request ("GET" , "/index-user1,index-user2/_search" );
311314 searchRequest .setJsonEntity (Strings .toString (json ));
312- searchRequest . setOptions ( RequestOptions . DEFAULT . toBuilder (). addHeader ( "es-security-runas-user" , "metadata1_read2" ) );
315+ setUser ( searchRequest , "metadata1_read2" );
313316
314317 // ES|QL query on the same index pattern
315318 var esqlResp = expectThrows (ResponseException .class , () -> runESQLCommand ("metadata1_read2" , "FROM index-user1,index-user2" ));
@@ -429,13 +432,13 @@ public void testFieldLevelSecurityAllow() throws Exception {
429432
430433 public void testFieldLevelSecurityAllowPartial () throws Exception {
431434 Request request = new Request ("GET" , "/index*/_field_caps" );
432- request . setOptions ( RequestOptions . DEFAULT . toBuilder (). addHeader ( "es-security-runas-user" , "fls_user" ) );
435+ setUser ( request , "fls_user" );
433436 request .addParameter ("error_trace" , "true" );
434437 request .addParameter ("pretty" , "true" );
435438 request .addParameter ("fields" , "*" );
436439
437440 request = new Request ("GET" , "/index*/_search" );
438- request . setOptions ( RequestOptions . DEFAULT . toBuilder (). addHeader ( "es-security-runas-user" , "fls_user" ) );
441+ setUser ( request , "fls_user" );
439442 request .addParameter ("error_trace" , "true" );
440443 request .addParameter ("pretty" , "true" );
441444
@@ -761,6 +764,36 @@ public void testFromLookupIndexForbidden() throws Exception {
761764 assertThat (resp .getResponse ().getStatusLine ().getStatusCode (), equalTo (HttpStatus .SC_BAD_REQUEST ));
762765 }
763766
767+ public void testListQueryAllowed () throws Exception {
768+ Request request = new Request ("GET" , "_query/queries" );
769+ setUser (request , "user_with_monitor_privileges" );
770+ var resp = client ().performRequest (request );
771+ assertOK (resp );
772+ }
773+
774+ public void testListQueryForbidden () throws Exception {
775+ Request request = new Request ("GET" , "_query/queries" );
776+ setUser (request , "user_without_monitor_privileges" );
777+ var resp = expectThrows (ResponseException .class , () -> client ().performRequest (request ));
778+ assertThat (resp .getResponse ().getStatusLine ().getStatusCode (), equalTo (403 ));
779+ assertThat (resp .getMessage (), containsString ("this action is granted by the cluster privileges [monitor_esql,monitor,manage,all]" ));
780+ }
781+
782+ public void testGetQueryAllowed () throws Exception {
783+ // This is a bit tricky, since there is no such running query. We just make sure it didn't fail on forbidden privileges.
784+ Request request = new Request ("GET" , "_query/queries/foo:1234" );
785+ var resp = expectThrows (ResponseException .class , () -> client ().performRequest (request ));
786+ assertThat (resp .getResponse ().getStatusLine ().getStatusCode (), not (equalTo (404 )));
787+ }
788+
789+ public void testGetQueryForbidden () throws Exception {
790+ Request request = new Request ("GET" , "_query/queries/foo:1234" );
791+ setUser (request , "user_without_monitor_privileges" );
792+ var resp = expectThrows (ResponseException .class , () -> client ().performRequest (request ));
793+ assertThat (resp .getResponse ().getStatusLine ().getStatusCode (), equalTo (403 ));
794+ assertThat (resp .getMessage (), containsString ("this action is granted by the cluster privileges [monitor_esql,monitor,manage,all]" ));
795+ }
796+
764797 private void createEnrichPolicy () throws Exception {
765798 createIndex ("songs" , Settings .EMPTY , """
766799 "properties":{"song_id": {"type": "keyword"}, "title": {"type": "keyword"}, "artist": {"type": "keyword"} }
@@ -837,11 +870,16 @@ protected Response runESQLCommand(String user, String command) throws IOExceptio
837870 json .endObject ();
838871 Request request = new Request ("POST" , "_query" );
839872 request .setJsonEntity (Strings .toString (json ));
840- request . setOptions ( RequestOptions . DEFAULT . toBuilder (). addHeader ( "es-security-runas-user" , user ) );
873+ setUser ( request , user );
841874 request .addParameter ("error_trace" , "true" );
842875 return client ().performRequest (request );
843876 }
844877
878+ private static void setUser (Request request , String user ) {
879+ request .setOptions (RequestOptions .DEFAULT .toBuilder ().addHeader ("es-security-runas-user" , user ));
880+
881+ }
882+
845883 static void addRandomPragmas (XContentBuilder builder ) throws IOException {
846884 if (Build .current ().isSnapshot ()) {
847885 Settings pragmas = randomPragmas ();
@@ -853,7 +891,7 @@ static void addRandomPragmas(XContentBuilder builder) throws IOException {
853891 }
854892 }
855893
856- static Settings randomPragmas () {
894+ private static Settings randomPragmas () {
857895 Settings .Builder settings = Settings .builder ();
858896 if (randomBoolean ()) {
859897 settings .put ("page_size" , between (1 , 5 ));
0 commit comments