From 92ed489313c6cc6101dfb65b164b9369abb673d2 Mon Sep 17 00:00:00 2001 From: Slobodan Adamovic Date: Thu, 27 Mar 2025 16:55:33 +0100 Subject: [PATCH 1/2] [Failure Store] Test FLS/DLS --- .../FailureStoreSecurityRestIT.java | 109 ++++++++++++++++-- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java index 5800ed25712aa..e6df5a58ad30a 100644 --- a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java +++ b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.support.XContentMapValues; @@ -2042,7 +2043,6 @@ public void testDlsFls() throws Exception { ); // FLS sort of applies to failure store - // TODO this will change with FLS handling assertSearchResponseContainsExpectedIndicesAndFields( performRequest(user, new Search("test1::failures").toSearchRequest()), Map.of(failureIndexName, Set.of("@timestamp")) @@ -2062,12 +2062,45 @@ public void testDlsFls() throws Exception { { "names": ["test*"], "privileges": ["read_failure_store"], + "field_security": { + "grant": ["error.type", "error.message"] + } + } + ] + }""", "test1"), role); + + // FLS applies to regular data stream + assertSearchResponseContainsExpectedIndicesAndFields( + performRequest(user, new Search(randomFrom("test1", "test1::data")).toSearchRequest()), + Map.of(dataIndexName, Set.of("@timestamp", "age")) + ); + + // FLS applies to failure store + assertSearchResponseContainsExpectedIndicesAndFields( + performRequest(user, new Search("test1::failures").toSearchRequest()), + Map.of(failureIndexName, Set.of("error.type", "error.message")) + ); + + upsertRole(Strings.format(""" + { + "cluster": ["all"], + "indices": [ + { + "names": ["%s"], + "privileges": ["read"], "field_security": { "grant": ["@timestamp", "age"] } + }, + { + "names": ["test*"], + "privileges": ["read_failure_store"], + "field_security": { + "grant": ["error.type", "error.message"] + } } ] - }""", randomFrom("test*", "test1")), role); + }""", "test*"), role); // FLS applies to regular data stream assertSearchResponseContainsExpectedIndicesAndFields( @@ -2075,11 +2108,10 @@ public void testDlsFls() throws Exception { Map.of(dataIndexName, Set.of("@timestamp", "age")) ); - // FLS sort of applies to failure store - // TODO this will change with FLS handling + // FLS applies to failure store assertSearchResponseContainsExpectedIndicesAndFields( performRequest(user, new Search("test1::failures").toSearchRequest()), - Map.of(failureIndexName, Set.of("@timestamp")) + Map.of(failureIndexName, Set.of("@timestamp", "error.type", "error.message")) ); upsertRole(""" @@ -2105,10 +2137,56 @@ public void testDlsFls() throws Exception { performRequest(user, new Search(randomFrom("test1", "test1::data")).toSearchRequest()), Map.of(dataIndexName, Set.of("@timestamp", "age", "name", "email")) ); - assertSearchResponseContainsExpectedIndicesAndFields( performRequest(user, new Search("test1::failures").toSearchRequest()), - Map.of(failureIndexName, Set.of("@timestamp", "document", "error")) + Map.of( + failureIndexName, + Set.of( + "@timestamp", + "document.id", + "document.index", + "document.source.@timestamp", + "document.source.age", + "document.source.email", + "document.source.name", + "error.message", + "error.stack_trace", + "error.type" + ) + ) + ); + + // check that direct read access to backing indices is working + upsertRole(Strings.format(""" + { + "cluster": ["all"], + "indices": [ + { + "names": ["%s"], + "privileges": ["read"], + "field_security": { + "grant": ["@timestamp", "age"] + } + }, + { + "names": ["%s"], + "privileges": ["read"], + "field_security": { + "grant": ["@timestamp", "document.source.name"] + } + } + ] + }""", dataIndexName, failureIndexName), role); + + // FLS applies to backing data index + assertSearchResponseContainsExpectedIndicesAndFields( + performRequest(user, new Search(randomFrom(dataIndexName, ".ds-*")).toSearchRequest()), + Map.of(dataIndexName, Set.of("@timestamp", "age")) + ); + // and backing failure index + assertSearchResponseContainsExpectedIndicesAndFields( + performRequest(user, new Search(randomFrom(failureIndexName, ".fs-*")).toSearchRequest()), + Map.of(failureIndexName, Set.of("@timestamp", "document.source.name")) ); // DLS @@ -2160,6 +2238,21 @@ public void testDlsFls() throws Exception { }""", role); // DLS does not apply because there is a section without DLS expectSearch(user, new Search(randomFrom("test1", "test1::data")), dataIndexDocId); + + // DLS is applicable to backing failure store when granted read directly + upsertRole(Strings.format(""" + { + "cluster": ["all"], + "indices": [ + { + "names": ["%s"], + "privileges": ["read"], + "query":{"term":{"document.source.name":{"value":"jack"}}} + } + ] + }""", failureIndexName), role); + expectSearch(user, new Search(randomFrom(".fs-*", failureIndexName))); + } private static void expectThrows(ThrowingRunnable runnable, int statusCode) { @@ -2455,7 +2548,7 @@ protected void assertSearchResponseContainsExpectedIndicesAndFields( assertThat(searchResult.keySet(), equalTo(expectedIndicesAndFields.keySet())); for (String index : expectedIndicesAndFields.keySet()) { Set expectedFields = expectedIndicesAndFields.get(index); - assertThat(searchResult.get(index).keySet(), equalTo(expectedFields)); + assertThat(Maps.flatten(searchResult.get(index), false, true).keySet(), equalTo(expectedFields)); } } finally { response.decRef(); From 9234b80ddeb51ee289dda0025420d4db0b8aa95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slobodan=20Adamovi=C4=87?= Date: Fri, 28 Mar 2025 13:38:06 +0100 Subject: [PATCH 2/2] update comment Co-authored-by: Nikolaj Volgushev --- .../xpack/security/failurestore/FailureStoreSecurityRestIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java index e6df5a58ad30a..85b5fd6d82401 100644 --- a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java +++ b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/failurestore/FailureStoreSecurityRestIT.java @@ -2042,7 +2042,7 @@ public void testDlsFls() throws Exception { Map.of(dataIndexName, Set.of("@timestamp", "age")) ); - // FLS sort of applies to failure store + // FLS applies to failure store assertSearchResponseContainsExpectedIndicesAndFields( performRequest(user, new Search("test1::failures").toSearchRequest()), Map.of(failureIndexName, Set.of("@timestamp"))