diff --git a/docs/changelog/116860.yaml b/docs/changelog/116860.yaml new file mode 100644 index 0000000000000..e0ee82e8a3fbd --- /dev/null +++ b/docs/changelog/116860.yaml @@ -0,0 +1,5 @@ +pr: 116860 +summary: Add `read_failures` privilege to authorize reading the failure store +area: Authorization +type: enhancement +issues: [] diff --git a/docs/reference/rest-api/security/get-builtin-privileges.asciidoc b/docs/reference/rest-api/security/get-builtin-privileges.asciidoc index 7f3d75b926780..0bab2a7cd1313 100644 --- a/docs/reference/rest-api/security/get-builtin-privileges.asciidoc +++ b/docs/reference/rest-api/security/get-builtin-privileges.asciidoc @@ -149,6 +149,7 @@ A successful call returns an object with "cluster", "index", and "remote_cluster "none", "read", "read_cross_cluster", + "read_failures", "view_index_metadata", "write" ], diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java index f4df99dcefea4..88917f290b696 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java @@ -179,6 +179,7 @@ public final class IndexPrivilege extends Privilege { public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY); public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON); public static final IndexPrivilege READ = new IndexPrivilege("read", READ_AUTOMATON); + public static final IndexPrivilege READ_FAILURES = new IndexPrivilege("read_failures", READ_AUTOMATON); public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", READ_CROSS_CLUSTER_AUTOMATON); public static final IndexPrivilege CREATE = new IndexPrivilege("create", CREATE_AUTOMATON); public static final IndexPrivilege INDEX = new IndexPrivilege("index", INDEX_AUTOMATON); @@ -221,6 +222,7 @@ public final class IndexPrivilege extends Privilege { entry("create_index", CREATE_INDEX), entry("monitor", MONITOR), entry("read", READ), + entry("read_failures", READ_FAILURES), entry("index", INDEX), entry("delete", DELETE), entry("write", WRITE), diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilegeTests.java index 073b3b92a43a5..193e435d75d03 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilegeTests.java @@ -58,7 +58,7 @@ public void testOrderingOfPrivilegeNames() throws Exception { } public void testFindPrivilegesThatGrant() { - assertThat(findPrivilegesThatGrant(TransportSearchAction.TYPE.name()), equalTo(List.of("read", "all"))); + assertThat(findPrivilegesThatGrant(TransportSearchAction.TYPE.name()), equalTo(List.of("read", "read_failures", "all"))); assertThat(findPrivilegesThatGrant(TransportIndexAction.NAME), equalTo(List.of("create_doc", "create", "index", "write", "all"))); assertThat(findPrivilegesThatGrant(TransportUpdateAction.NAME), equalTo(List.of("index", "write", "all"))); assertThat(findPrivilegesThatGrant(TransportDeleteAction.NAME), equalTo(List.of("delete", "write", "all"))); @@ -71,7 +71,7 @@ public void testFindPrivilegesThatGrant() { public void testPrivilegesForRollupFieldCapsAction() { final Collection privileges = findPrivilegesThatGrant(GetRollupIndexCapsAction.NAME); - assertThat(Set.copyOf(privileges), equalTo(Set.of("read", "view_index_metadata", "manage", "all"))); + assertThat(Set.copyOf(privileges), equalTo(Set.of("read", "read_failures", "view_index_metadata", "manage", "all"))); } public void testPrivilegesForGetCheckPointAction() { diff --git a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityEsqlIT.java b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityEsqlIT.java index 09449f81121fd..42e55ce1db291 100644 --- a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityEsqlIT.java +++ b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityEsqlIT.java @@ -655,7 +655,7 @@ public void testCrossClusterQueryWithOnlyRemotePrivs() throws Exception { error.getMessage(), containsString( "action [indices:data/read/esql] is unauthorized for user [remote_search_user] with effective roles [remote_search], " - + "this action is granted by the index privileges [read,read_cross_cluster,all]" + + "this action is granted by the index privileges [read,read_failures,read_cross_cluster,all]" ) ); @@ -671,7 +671,7 @@ public void testCrossClusterQueryWithOnlyRemotePrivs() throws Exception { error.getMessage(), containsString( "action [indices:data/read/esql] is unauthorized for user [remote_search_user] with effective roles " - + "[remote_search], this action is granted by the index privileges [read,read_cross_cluster,all]" + + "[remote_search], this action is granted by the index privileges [read,read_failures,read_cross_cluster,all]" ) ); @@ -686,7 +686,7 @@ public void testCrossClusterQueryWithOnlyRemotePrivs() throws Exception { error.getMessage(), containsString( "action [indices:data/read/esql] is unauthorized for user [remote_search_user] with effective roles " - + "[remote_search], this action is granted by the index privileges [read,read_cross_cluster,all]" + + "[remote_search], this action is granted by the index privileges [read,read_failures,read_cross_cluster,all]" ) ); } diff --git a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityFcActionAuthorizationIT.java b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityFcActionAuthorizationIT.java index 793313e238651..dde9ff892a27f 100644 --- a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityFcActionAuthorizationIT.java +++ b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/RemoteClusterSecurityFcActionAuthorizationIT.java @@ -435,7 +435,7 @@ public void testUpdateCrossClusterApiKey() throws Exception { + "for user [foo] with assigned roles [role] authenticated by API key id [" + apiKeyId + "] of user [test_user] on indices [index], this action is granted by the index privileges " - + "[view_index_metadata,manage,read,all]" + + "[read,read_failures,view_index_metadata,manage,all]" ) ); @@ -483,7 +483,7 @@ public void testUpdateCrossClusterApiKey() throws Exception { + "for user [foo] with assigned roles [role] authenticated by API key id [" + apiKeyId + "] of user [test_user] on indices [index], this action is granted by the index privileges " - + "[view_index_metadata,manage,read,all]" + + "[read,read_failures,view_index_metadata,manage,all]" ) ); } diff --git a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyWorkflowsRestrictionRestIT.java b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyWorkflowsRestrictionRestIT.java index e0b9d9ecd0ede..0475b8132919f 100644 --- a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyWorkflowsRestrictionRestIT.java +++ b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyWorkflowsRestrictionRestIT.java @@ -187,7 +187,7 @@ public void testWorkflowsRestrictionAllowsAccess() throws IOException { + apiKeyId + "] of user [" + WORKFLOW_API_KEY_USER - + "] on indices [my-app-b], this action is granted by the index privileges [read,all]" + + "] on indices [my-app-b], this action is granted by the index privileges [read,read_failures,all]" ) ); assertThat(e.getMessage(), not(containsString("access restricted by workflow"))); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index c2e9a92e45353..44cfd6ee2dd8a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -987,7 +987,10 @@ public void testUnknownRoleCausesDenial() { ) ) ); - assertThat(securityException, throwableWithMessage(containsString("this action is granted by the index privileges [read,all]"))); + assertThat( + securityException, + throwableWithMessage(containsString("this action is granted by the index privileges [read,read_failures,all]")) + ); verify(auditTrail).accessDenied(eq(requestId), eq(authentication), eq(action), eq(request), authzInfoRoles(Role.EMPTY.names())); verifyNoMoreInteractions(auditTrail); @@ -1033,7 +1036,10 @@ public void testServiceAccountDenial() { throwableWithMessage(containsString("[" + action + "] is unauthorized for service account [" + serviceUser.principal() + "]")) ); verify(auditTrail).accessDenied(eq(requestId), eq(authentication), eq(action), eq(request), authzInfoRoles(role.names())); - assertThat(securityException, throwableWithMessage(containsString("this action is granted by the index privileges [read,all]"))); + assertThat( + securityException, + throwableWithMessage(containsString("this action is granted by the index privileges [read,read_failures,all]")) + ); verifyNoMoreInteractions(auditTrail); } @@ -1083,7 +1089,10 @@ public void testThatRoleWithNoIndicesIsDenied() { containsString("[" + action + "] is unauthorized" + " for user [test user]" + " with effective roles [no_indices]") ) ); - assertThat(securityException, throwableWithMessage(containsString("this action is granted by the index privileges [read,all]"))); + assertThat( + securityException, + throwableWithMessage(containsString("this action is granted by the index privileges [read,read_failures,all]")) + ); verify(auditTrail).accessDenied( eq(requestId), @@ -1536,7 +1545,10 @@ public void testDenialErrorMessagesForSearchAction() { assertThat(securityException, throwableWithMessage(containsString("other-4"))); assertThat(securityException, throwableWithMessage(not(containsString("all-1")))); assertThat(securityException, throwableWithMessage(not(containsString("read-2")))); - assertThat(securityException, throwableWithMessage(containsString(", this action is granted by the index privileges [read,all]"))); + assertThat( + securityException, + throwableWithMessage(containsString(", this action is granted by the index privileges [read,read_failures,all]")) + ); } public void testDenialErrorMessagesForBulkIngest() throws Exception { diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml index d03e6925cab1f..aa11ed63602c7 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml @@ -16,4 +16,4 @@ setup: # I would much prefer we could just check that specific entries are in the array, but we don't have # an assertion for that - length: { "cluster" : 62 } - - length: { "index" : 22 } + - length: { "index" : 23 }