From 62215f1fae539d60d54de8a04d0033397e26509f Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 25 Oct 2017 15:12:21 +0200 Subject: [PATCH] security: Fail request if suggesters are used and DLS is active. Original commit: elastic/x-pack-elasticsearch@056c735e7783ffb97f4161800614b0a140cd6916 --- docs/en/security/limitations.asciidoc | 2 + .../interceptor/SearchRequestInterceptor.java | 9 ++ .../DocumentLevelSecurityTests.java | 104 ++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/docs/en/security/limitations.asciidoc b/docs/en/security/limitations.asciidoc index fdede740c8f..693308b25c4 100644 --- a/docs/en/security/limitations.asciidoc +++ b/docs/en/security/limitations.asciidoc @@ -62,6 +62,8 @@ When a user's role enables document level security for an index: ** The `terms` query with terms lookup isn't supported. ** The `geo_shape` query with indexed shapes isn't supported. ** The `percolate` query isn't supported. +* If suggesters are specified and document level security is enabled then + the specified suggesters are ignored. [float] [[alias-limitations]] diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/action/interceptor/SearchRequestInterceptor.java b/plugin/src/main/java/org/elasticsearch/xpack/security/action/interceptor/SearchRequestInterceptor.java index 7201d8190a8..1dcfc725c7b 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/action/interceptor/SearchRequestInterceptor.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/action/interceptor/SearchRequestInterceptor.java @@ -5,10 +5,12 @@ */ package org.elasticsearch.xpack.security.action.interceptor; +import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportRequest; @@ -25,6 +27,13 @@ public class SearchRequestInterceptor extends FieldAndDocumentLevelSecurityReque @Override public void disableFeatures(SearchRequest request, boolean fieldLevelSecurityEnabled, boolean documentLevelSecurityEnabled) { request.requestCache(false); + + if (documentLevelSecurityEnabled) { + if (request.source() != null && request.source().suggest() != null) { + throw new ElasticsearchSecurityException("Suggest isn't supported if document level security is enabled", + RestStatus.BAD_REQUEST); + } + } } @Override diff --git a/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index 52ca23183b0..9db12348d68 100644 --- a/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -6,6 +6,7 @@ package org.elasticsearch.integration; import org.apache.lucene.search.join.ScoreMode; +import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BulkItemResponse; @@ -39,6 +40,13 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortMode; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.completion.CompletionSuggestion; +import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder; +import org.elasticsearch.search.suggest.phrase.PhraseSuggestion; +import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder; +import org.elasticsearch.search.suggest.term.TermSuggestion; +import org.elasticsearch.search.suggest.term.TermSuggestionBuilder; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.xpack.XPackPlugin; @@ -66,7 +74,9 @@ import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordTok import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +@LuceneTestCase.SuppressCodecs("*") // suppress test codecs otherwise test using completion suggester fails public class DocumentLevelSecurityTests extends SecurityIntegTestCase { protected static final SecureString USERS_PASSWD = new SecureString("change_me".toCharArray()); @@ -944,4 +954,98 @@ public class DocumentLevelSecurityTests extends SecurityIntegTestCase { equalTo("{\"field2\":\"value2\"}")); } + public void testSuggesters() throws Exception { + assertAcked(client().admin().indices().prepareCreate("test") + .addMapping("type1", "field1", "type=text", "suggest_field1", "type=text", "suggest_field2", "type=completion") + ); + + client().prepareIndex("test", "type1", "1") + .setSource(jsonBuilder().startObject() + .field("field1", "value1") + .field("suggest_field1", "value") + .startObject("suggest_field2") + .field("input", "value") + .endObject() + .endObject()).get(); + // A document that is always included by role query of both roles: + client().prepareIndex("test", "type1", "2") + .setSource(jsonBuilder().startObject() + .field("field1", "value1") + .field("field2", "value2") + .endObject()).get(); + refresh("test"); + + // Term suggester: + SearchResponse response = client() + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valeu") + .addSuggestion("_name1", new TermSuggestionBuilder("suggest_field1")) + ).get(); + assertNoFailures(response); + + TermSuggestion termSuggestion = response.getSuggest().getSuggestion("_name1"); + assertThat(termSuggestion, notNullValue()); + assertThat(termSuggestion.getEntries().size(), equalTo(1)); + assertThat(termSuggestion.getEntries().get(0).getOptions().size(), equalTo(1)); + assertThat(termSuggestion.getEntries().get(0).getOptions().get(0).getText().string(), equalTo("value")); + + Exception e = expectThrows(ElasticsearchSecurityException.class, () -> client() + .filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valeu") + .addSuggestion("_name1", new TermSuggestionBuilder("suggest_field1")) + ).get()); + assertThat(e.getMessage(), equalTo("Suggest isn't supported if document level security is enabled")); + + // Phrase suggester: + response = client() + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valeu") + .addSuggestion("_name1", new PhraseSuggestionBuilder("suggest_field1")) + ).get(); + assertNoFailures(response); + + PhraseSuggestion phraseSuggestion = response.getSuggest().getSuggestion("_name1"); + assertThat(phraseSuggestion, notNullValue()); + assertThat(phraseSuggestion.getEntries().size(), equalTo(1)); + assertThat(phraseSuggestion.getEntries().get(0).getOptions().size(), equalTo(1)); + assertThat(phraseSuggestion.getEntries().get(0).getOptions().get(0).getText().string(), equalTo("value")); + + e = expectThrows(ElasticsearchSecurityException.class, () -> client() + .filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valeu") + .addSuggestion("_name1", new PhraseSuggestionBuilder("suggest_field1")) + ).get()); + assertThat(e.getMessage(), equalTo("Suggest isn't supported if document level security is enabled")); + + // Completion suggester: + response = client() + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valu") + .addSuggestion("_name1", new CompletionSuggestionBuilder("suggest_field2")) + ).get(); + assertNoFailures(response); + + CompletionSuggestion completionSuggestion = response.getSuggest().getSuggestion("_name1"); + assertThat(completionSuggestion, notNullValue()); + assertThat(completionSuggestion.getEntries().size(), equalTo(1)); + assertThat(completionSuggestion.getEntries().get(0).getOptions().size(), equalTo(1)); + assertThat(completionSuggestion.getEntries().get(0).getOptions().get(0).getText().string(), equalTo("value")); + + e = expectThrows(ElasticsearchSecurityException.class, () -> client() + .filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) + .prepareSearch("test") + .suggest(new SuggestBuilder() + .setGlobalText("valeu") + .addSuggestion("_name1", new CompletionSuggestionBuilder("suggest_field2")) + ).get()); + assertThat(e.getMessage(), equalTo("Suggest isn't supported if document level security is enabled")); + } + }