security: Always allow access to a rootdoc's nested documents if access to rootdoc is allowed
relates elastic/x-pack-elasticsearch#2665 Original commit: elastic/x-pack-elasticsearch@2bbddd1dd2
This commit is contained in:
parent
b76c85e7fd
commit
652f6560b8
|
@ -17,24 +17,21 @@ import org.apache.lucene.search.ConstantScoreQuery;
|
|||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.LeafCollector;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.Weight;
|
||||
import org.apache.lucene.search.join.BitSetProducer;
|
||||
import org.apache.lucene.search.join.ToChildBlockJoinQuery;
|
||||
import org.apache.lucene.util.BitSet;
|
||||
import org.apache.lucene.util.BitSetIterator;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.util.SparseFixedBitSet;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.FilterClient;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
|
@ -46,7 +43,6 @@ import org.elasticsearch.index.query.BoolQueryBuilder;
|
|||
import org.elasticsearch.index.query.BoostingQueryBuilder;
|
||||
import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
|
||||
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
@ -139,10 +135,17 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
|||
QueryBuilder queryBuilder = queryShardContext.parseInnerQueryBuilder(parser);
|
||||
verifyRoleQuery(queryBuilder);
|
||||
failIfQueryUsesClient(queryBuilder, queryShardContext);
|
||||
ParsedQuery parsedQuery = queryShardContext.toFilter(queryBuilder);
|
||||
filter.add(parsedQuery.query(), SHOULD);
|
||||
Query roleQuery = queryShardContext.toFilter(queryBuilder).query();
|
||||
filter.add(roleQuery, SHOULD);
|
||||
if (queryShardContext.getMapperService().hasNested()) {
|
||||
// If access is allowed on root doc then also access is allowed on all nested docs of that root document:
|
||||
BitSetProducer rootDocs = queryShardContext.bitsetFilter(Queries.newNonNestedFilter());
|
||||
ToChildBlockJoinQuery includeNestedDocs = new ToChildBlockJoinQuery(roleQuery, rootDocs);
|
||||
filter.add(includeNestedDocs, SHOULD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// at least one of the queries should match
|
||||
filter.setMinimumNumberShouldMatch(1);
|
||||
reader = DocumentSubsetReader.wrap(reader, bitsetFilterCache, new ConstantScoreQuery(filter.build()));
|
||||
|
|
|
@ -23,6 +23,8 @@ import org.elasticsearch.client.Requests;
|
|||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.indices.IndicesRequestCache;
|
||||
|
@ -37,8 +39,6 @@ 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.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.test.InternalSettingsPlugin;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
|
@ -52,6 +52,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery;
|
||||
|
@ -658,7 +659,7 @@ public class DocumentLevelSecurityTests extends SecurityIntegTestCase {
|
|||
}
|
||||
|
||||
public void testParentChild_joinField() throws Exception {
|
||||
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
|
||||
XContentBuilder mapping = jsonBuilder().startObject()
|
||||
.startObject("properties")
|
||||
.startObject("join_field")
|
||||
.field("type", "join")
|
||||
|
@ -903,4 +904,44 @@ public class DocumentLevelSecurityTests extends SecurityIntegTestCase {
|
|||
assertThat(client().prepareGet("test", "type", "1").get().getSource().get("field1").toString(), equalTo("value3"));
|
||||
}
|
||||
|
||||
public void testNestedInnerHits() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate("test")
|
||||
.addMapping("type1", "field1", "type=text", "nested_field", "type=nested")
|
||||
);
|
||||
client().prepareIndex("test", "type1", "1")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field("field1", "value1")
|
||||
.startArray("nested_field")
|
||||
.startObject()
|
||||
.field("field2", "value2")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject())
|
||||
.get();
|
||||
client().prepareIndex("test", "type1", "2")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field("field1", "value2")
|
||||
.startArray("nested_field")
|
||||
.startObject()
|
||||
.field("field2", "value2")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject())
|
||||
.get();
|
||||
refresh("test");
|
||||
|
||||
SearchResponse response = client()
|
||||
.filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)))
|
||||
.prepareSearch("test")
|
||||
.setQuery(QueryBuilders.nestedQuery("nested_field", QueryBuilders.termQuery("nested_field.field2", "value2"),
|
||||
ScoreMode.None).innerHit(new InnerHitBuilder()))
|
||||
.get();
|
||||
assertHitCount(response, 1);
|
||||
assertSearchHits(response, "1");
|
||||
assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(0).getId(), equalTo("1"));
|
||||
assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(0).getNestedIdentity().getOffset(), equalTo(0));
|
||||
assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(0).getSourceAsString(),
|
||||
equalTo("{\"nested_field\":{\"field2\":\"value2\"}}"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue