security: Added templating support to DLS' role query.
Closes elastic/elasticsearch#410 Original commit: elastic/x-pack-elasticsearch@2b91ea9eed
This commit is contained in:
parent
d734d483c5
commit
d33e639d4c
|
@ -0,0 +1,27 @@
|
||||||
|
apply plugin: 'elasticsearch.rest-test'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
|
||||||
|
testCompile project(path: ':modules:lang-mustache', configuration: 'runtime')
|
||||||
|
}
|
||||||
|
|
||||||
|
integTest {
|
||||||
|
cluster {
|
||||||
|
plugin ':x-plugins:elasticsearch:x-pack'
|
||||||
|
setting 'xpack.watcher.enabled', 'false'
|
||||||
|
setting 'xpack.monitoring.enabled', 'false'
|
||||||
|
setting 'path.scripts', "${project.buildDir}/resources/test/templates"
|
||||||
|
setupCommand 'setupDummyUser',
|
||||||
|
'bin/x-pack/users', 'useradd', 'test_admin', '-p', 'changeme', '-r', 'superuser'
|
||||||
|
waitCondition = { node, ant ->
|
||||||
|
File tmpFile = new File(node.cwd, 'wait.success')
|
||||||
|
ant.get(src: "http://${node.httpUri()}",
|
||||||
|
dest: tmpFile.toString(),
|
||||||
|
username: 'test_admin',
|
||||||
|
password: 'changeme',
|
||||||
|
ignoreerrors: true,
|
||||||
|
retries: 10)
|
||||||
|
return tmpFile.exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.smoketest;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
|
import org.elasticsearch.test.rest.RestTestCandidate;
|
||||||
|
import org.elasticsearch.test.rest.parser.RestTestParseException;
|
||||||
|
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
|
||||||
|
public class RestIT extends ESRestTestCase {
|
||||||
|
|
||||||
|
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("test_admin", new SecuredString("changeme".toCharArray()));
|
||||||
|
|
||||||
|
public RestIT(@Name("yaml") RestTestCandidate testCandidate) {
|
||||||
|
super(testCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParametersFactory
|
||||||
|
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
|
||||||
|
return ESRestTestCase.createParameters(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings restClientSettings() {
|
||||||
|
return Settings.builder()
|
||||||
|
.put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
---
|
||||||
|
setup:
|
||||||
|
- skip:
|
||||||
|
features: headers
|
||||||
|
|
||||||
|
- do:
|
||||||
|
cluster.health:
|
||||||
|
wait_for_status: yellow
|
||||||
|
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "inline_template_user"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "changeme",
|
||||||
|
"roles" : [ "inline_template_role" ]
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "stored_template_user"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "changeme",
|
||||||
|
"roles" : [ "stored_template_role" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "file_template_user"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "changeme",
|
||||||
|
"roles" : [ "file_template_role" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "inline_template_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"names": "foobar",
|
||||||
|
"privileges": ["all"],
|
||||||
|
"query" : {
|
||||||
|
"template" : {
|
||||||
|
"inline" : {
|
||||||
|
"term" : { "username" : "{{_user.username}}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "stored_template_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"names": "foobar",
|
||||||
|
"privileges": ["all"],
|
||||||
|
"query" : {
|
||||||
|
"template" : {
|
||||||
|
"id" : "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "file_template_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"names": "foobar",
|
||||||
|
"privileges": ["all"],
|
||||||
|
"query" : {
|
||||||
|
"template" : {
|
||||||
|
"file" : "query"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
put_template:
|
||||||
|
id: "1"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"term" : {
|
||||||
|
"username" : "{{_user.username}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
index:
|
||||||
|
index: foobar
|
||||||
|
type: type
|
||||||
|
id: 1
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"username": "inline_template_user"
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
index:
|
||||||
|
index: foobar
|
||||||
|
type: type
|
||||||
|
id: 2
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"username": "stored_template_user"
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
index:
|
||||||
|
index: foobar
|
||||||
|
type: type
|
||||||
|
id: 3
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"username": "file_template_user"
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.refresh: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
teardown:
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "inline_template_user"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "stored_template_user"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "file_template_user"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_role:
|
||||||
|
name: "inline_template_role"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_role:
|
||||||
|
name: "stored_template_role"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_role:
|
||||||
|
name: "file_template_role"
|
||||||
|
ignore: 404
|
||||||
|
|
||||||
|
---
|
||||||
|
"Test inline template":
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic aW5saW5lX3RlbXBsYXRlX3VzZXI6Y2hhbmdlbWU="
|
||||||
|
search:
|
||||||
|
index: foobar
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.username: inline_template_user}
|
||||||
|
|
||||||
|
---
|
||||||
|
"Test stored template":
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic c3RvcmVkX3RlbXBsYXRlX3VzZXI6Y2hhbmdlbWU="
|
||||||
|
search:
|
||||||
|
index: foobar
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.username: stored_template_user}
|
||||||
|
|
||||||
|
---
|
||||||
|
"Test file template":
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic ZmlsZV90ZW1wbGF0ZV91c2VyOmNoYW5nZW1l"
|
||||||
|
search:
|
||||||
|
index: foobar
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.username: file_template_user}
|
|
@ -0,0 +1,198 @@
|
||||||
|
---
|
||||||
|
setup:
|
||||||
|
- skip:
|
||||||
|
features: headers
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: shared_logs
|
||||||
|
|
||||||
|
- do:
|
||||||
|
cluster.health:
|
||||||
|
wait_for_status: yellow
|
||||||
|
- do:
|
||||||
|
ingest.put_pipeline:
|
||||||
|
id: "my_pipeline"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"processors": [
|
||||||
|
{
|
||||||
|
"set_security_user" : {
|
||||||
|
"field" : "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "joe"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "changeme",
|
||||||
|
"roles" : [ "small_companies_role" ],
|
||||||
|
"metadata" : {
|
||||||
|
"customer_id" : "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "john"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "changeme",
|
||||||
|
"roles" : [ "small_companies_role" ],
|
||||||
|
"metadata" : {
|
||||||
|
"customer_id" : "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
teardown:
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "joe"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "john"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_role:
|
||||||
|
name: "small_companies_role"
|
||||||
|
ignore: 404
|
||||||
|
|
||||||
|
---
|
||||||
|
"Test shared index seperating user by using DLS role query with user's username":
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "small_companies_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"names": "shared_logs",
|
||||||
|
"privileges": ["read", "create"],
|
||||||
|
"query" : {
|
||||||
|
"template" : {
|
||||||
|
"inline" : {
|
||||||
|
"term" : { "user.username" : "{{_user.username}}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9lOmNoYW5nZW1l"
|
||||||
|
index:
|
||||||
|
index: shared_logs
|
||||||
|
type: type
|
||||||
|
pipeline: "my_pipeline"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"log": "Joe's first log entry"
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
|
||||||
|
index:
|
||||||
|
index: shared_logs
|
||||||
|
type: type
|
||||||
|
pipeline: "my_pipeline"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"log": "John's first log entry"
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.refresh: {}
|
||||||
|
|
||||||
|
# Joe searches:
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9lOmNoYW5nZW1l"
|
||||||
|
search:
|
||||||
|
index: shared_logs
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.user.username: joe}
|
||||||
|
|
||||||
|
# John searches:
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
|
||||||
|
search:
|
||||||
|
index: shared_logs
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.user.username: john}
|
||||||
|
|
||||||
|
---
|
||||||
|
"Test shared index seperating user by using DLS role query with user's metadata":
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "small_companies_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"names": "shared_logs",
|
||||||
|
"privileges": ["read", "create"],
|
||||||
|
"query" : {
|
||||||
|
"template" : {
|
||||||
|
"inline" : {
|
||||||
|
"term" : { "user.metadata.customer_id" : "{{_user.metadata.customer_id}}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9lOmNoYW5nZW1l"
|
||||||
|
index:
|
||||||
|
index: shared_logs
|
||||||
|
type: type
|
||||||
|
pipeline: "my_pipeline"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"log": "Joe's first log entry"
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
|
||||||
|
index:
|
||||||
|
index: shared_logs
|
||||||
|
type: type
|
||||||
|
pipeline: "my_pipeline"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"log": "John's first log entry"
|
||||||
|
}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.refresh: {}
|
||||||
|
|
||||||
|
# Joe searches:
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9lOmNoYW5nZW1l"
|
||||||
|
search:
|
||||||
|
index: shared_logs
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.user.username: joe}
|
||||||
|
|
||||||
|
# John searches:
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
|
||||||
|
search:
|
||||||
|
index: shared_logs
|
||||||
|
body: { "query" : { "match_all" : {} } }
|
||||||
|
- match: { hits.total: 1}
|
||||||
|
- match: { hits.hits.0._source.user.username: john}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"term" : {
|
||||||
|
"username" : "{{_user.username}}"
|
||||||
|
}
|
||||||
|
}
|
|
@ -430,7 +430,7 @@ public class Security implements ActionPlugin, IngestPlugin {
|
||||||
module.setSearcherWrapper((indexService) -> new SecurityIndexSearcherWrapper(indexService.getIndexSettings(),
|
module.setSearcherWrapper((indexService) -> new SecurityIndexSearcherWrapper(indexService.getIndexSettings(),
|
||||||
indexService.newQueryShardContext(), indexService.mapperService(),
|
indexService.newQueryShardContext(), indexService.mapperService(),
|
||||||
indexService.cache().bitsetFilterCache(), indexService.getIndexServices().getThreadPool().getThreadContext(),
|
indexService.cache().bitsetFilterCache(), indexService.getIndexServices().getThreadPool().getThreadContext(),
|
||||||
securityLicenseState));
|
securityLicenseState, indexService.getIndexServices().getScriptService()));
|
||||||
}
|
}
|
||||||
if (transportClientMode == false) {
|
if (transportClientMode == false) {
|
||||||
/* We need to forcefully overwrite the query cache implementation to use security's opt out query cache implementation.
|
/* We need to forcefully overwrite the query cache implementation to use security's opt out query cache implementation.
|
||||||
|
|
|
@ -22,7 +22,9 @@ import org.apache.lucene.util.BitSet;
|
||||||
import org.apache.lucene.util.BitSetIterator;
|
import org.apache.lucene.util.BitSetIterator;
|
||||||
import org.apache.lucene.util.Bits;
|
import org.apache.lucene.util.Bits;
|
||||||
import org.apache.lucene.util.SparseFixedBitSet;
|
import org.apache.lucene.util.SparseFixedBitSet;
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
|
import org.elasticsearch.common.ParseFieldMatcher;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
||||||
|
@ -43,16 +45,24 @@ import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.shard.IndexSearcherWrapper;
|
import org.elasticsearch.index.shard.IndexSearcherWrapper;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.index.shard.ShardUtils;
|
import org.elasticsearch.index.shard.ShardUtils;
|
||||||
|
import org.elasticsearch.script.ExecutableScript;
|
||||||
|
import org.elasticsearch.script.Script;
|
||||||
|
import org.elasticsearch.script.ScriptContext;
|
||||||
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
||||||
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
||||||
import org.elasticsearch.xpack.security.support.Exceptions;
|
import org.elasticsearch.xpack.security.support.Exceptions;
|
||||||
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -78,10 +88,13 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
||||||
private final SecurityLicenseState securityLicenseState;
|
private final SecurityLicenseState securityLicenseState;
|
||||||
private final ThreadContext threadContext;
|
private final ThreadContext threadContext;
|
||||||
private final ESLogger logger;
|
private final ESLogger logger;
|
||||||
|
private final ScriptService scriptService;
|
||||||
|
|
||||||
public SecurityIndexSearcherWrapper(IndexSettings indexSettings, QueryShardContext queryShardContext,
|
public SecurityIndexSearcherWrapper(IndexSettings indexSettings, QueryShardContext queryShardContext,
|
||||||
MapperService mapperService, BitsetFilterCache bitsetFilterCache,
|
MapperService mapperService, BitsetFilterCache bitsetFilterCache,
|
||||||
ThreadContext threadContext, SecurityLicenseState securityLicenseState) {
|
ThreadContext threadContext, SecurityLicenseState securityLicenseState,
|
||||||
|
ScriptService scriptService) {
|
||||||
|
this.scriptService = scriptService;
|
||||||
this.logger = Loggers.getLogger(getClass(), indexSettings.getSettings());
|
this.logger = Loggers.getLogger(getClass(), indexSettings.getSettings());
|
||||||
this.mapperService = mapperService;
|
this.mapperService = mapperService;
|
||||||
this.queryShardContext = queryShardContext;
|
this.queryShardContext = queryShardContext;
|
||||||
|
@ -124,6 +137,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
||||||
BooleanQuery.Builder filter = new BooleanQuery.Builder();
|
BooleanQuery.Builder filter = new BooleanQuery.Builder();
|
||||||
for (BytesReference bytesReference : permissions.getQueries()) {
|
for (BytesReference bytesReference : permissions.getQueries()) {
|
||||||
QueryShardContext queryShardContext = copyQueryShardContext(this.queryShardContext);
|
QueryShardContext queryShardContext = copyQueryShardContext(this.queryShardContext);
|
||||||
|
bytesReference = evaluateTemplate(bytesReference);
|
||||||
try (XContentParser parser = XContentFactory.xContent(bytesReference).createParser(bytesReference)) {
|
try (XContentParser parser = XContentFactory.xContent(bytesReference).createParser(bytesReference)) {
|
||||||
Optional<QueryBuilder> queryBuilder = queryShardContext.newParseContext(parser).parseInnerQueryBuilder();
|
Optional<QueryBuilder> queryBuilder = queryShardContext.newParseContext(parser).parseInnerQueryBuilder();
|
||||||
if (queryBuilder.isPresent()) {
|
if (queryBuilder.isPresent()) {
|
||||||
|
@ -262,6 +276,45 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BytesReference evaluateTemplate(BytesReference querySource) throws IOException {
|
||||||
|
try (XContentParser parser = XContentFactory.xContent(querySource).createParser(querySource)) {
|
||||||
|
XContentParser.Token token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
|
||||||
|
}
|
||||||
|
token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.FIELD_NAME) {
|
||||||
|
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
|
||||||
|
}
|
||||||
|
if ("template".equals(parser.currentName())) {
|
||||||
|
token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
|
||||||
|
}
|
||||||
|
Script script = Script.parse(parser, ParseFieldMatcher.EMPTY);
|
||||||
|
// Add the user details to the params
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
if (script.getParams() != null) {
|
||||||
|
params.putAll(script.getParams());
|
||||||
|
}
|
||||||
|
User user = getUser();
|
||||||
|
Map<String, Object> userModel = new HashMap<>();
|
||||||
|
userModel.put("username", user.principal());
|
||||||
|
userModel.put("full_name", user.fullName());
|
||||||
|
userModel.put("email", user.email());
|
||||||
|
userModel.put("roles", Arrays.asList(user.roles()));
|
||||||
|
userModel.put("metadata", Collections.unmodifiableMap(user.metadata()));
|
||||||
|
params.put("_user", userModel);
|
||||||
|
// Always enforce mustache script lang:
|
||||||
|
script = new Script(script.getScript(), script.getType(), "mustache", params, script.getContentType());
|
||||||
|
ExecutableScript executable = scriptService.executable(script, ScriptContext.Standard.SEARCH, Collections.emptyMap());
|
||||||
|
return (BytesReference) executable.run();
|
||||||
|
} else {
|
||||||
|
return querySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected IndicesAccessControl getIndicesAccessControl() {
|
protected IndicesAccessControl getIndicesAccessControl() {
|
||||||
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationService.INDICES_PERMISSIONS_KEY);
|
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationService.INDICES_PERMISSIONS_KEY);
|
||||||
if (indicesAccessControl == null) {
|
if (indicesAccessControl == null) {
|
||||||
|
@ -269,4 +322,9 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
||||||
}
|
}
|
||||||
return indicesAccessControl;
|
return indicesAccessControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected User getUser(){
|
||||||
|
Authentication authentication = Authentication.getAuthentication(threadContext);
|
||||||
|
return authentication.getUser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.TermQueryBuilder;
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.IndexSettingsModule;
|
import org.elasticsearch.test.IndexSettingsModule;
|
||||||
|
@ -55,13 +56,14 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
||||||
public void testDLS() throws Exception {
|
public void testDLS() throws Exception {
|
||||||
ShardId shardId = new ShardId("_index", "_na_", 0);
|
ShardId shardId = new ShardId("_index", "_na_", 0);
|
||||||
MapperService mapperService = mock(MapperService.class);
|
MapperService mapperService = mock(MapperService.class);
|
||||||
|
ScriptService scriptService = mock(ScriptService.class);
|
||||||
when(mapperService.docMappers(anyBoolean())).thenReturn(Collections.emptyList());
|
when(mapperService.docMappers(anyBoolean())).thenReturn(Collections.emptyList());
|
||||||
when(mapperService.simpleMatchToIndexNames(anyString()))
|
when(mapperService.simpleMatchToIndexNames(anyString()))
|
||||||
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
|
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
|
||||||
|
|
||||||
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
|
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
|
||||||
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true, null,
|
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true, null,
|
||||||
singleton(new BytesArray("{}")));
|
singleton(new BytesArray("{\"match_all\" : {}}")));
|
||||||
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(), Settings.EMPTY);
|
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(), Settings.EMPTY);
|
||||||
QueryShardContext queryShardContext = mock(QueryShardContext.class);
|
QueryShardContext queryShardContext = mock(QueryShardContext.class);
|
||||||
QueryParseContext queryParseContext = mock(QueryParseContext.class);
|
QueryParseContext queryParseContext = mock(QueryParseContext.class);
|
||||||
|
@ -79,7 +81,7 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
||||||
SecurityLicenseState licenseState = mock(SecurityLicenseState.class);
|
SecurityLicenseState licenseState = mock(SecurityLicenseState.class);
|
||||||
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
|
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
|
||||||
SecurityIndexSearcherWrapper wrapper = new SecurityIndexSearcherWrapper(indexSettings, queryShardContext, mapperService,
|
SecurityIndexSearcherWrapper wrapper = new SecurityIndexSearcherWrapper(indexSettings, queryShardContext, mapperService,
|
||||||
bitsetFilterCache, threadContext, licenseState) {
|
bitsetFilterCache, threadContext, licenseState, scriptService) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected QueryShardContext copyQueryShardContext(QueryShardContext context) {
|
protected QueryShardContext copyQueryShardContext(QueryShardContext context) {
|
||||||
|
@ -140,7 +142,7 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
||||||
ParsedQuery parsedQuery = new ParsedQuery(new TermQuery(new Term("field", values[i])));
|
ParsedQuery parsedQuery = new ParsedQuery(new TermQuery(new Term("field", values[i])));
|
||||||
when(queryShardContext.newParseContext(any(XContentParser.class))).thenReturn(queryParseContext);
|
when(queryShardContext.newParseContext(any(XContentParser.class))).thenReturn(queryParseContext);
|
||||||
when(queryParseContext.parseInnerQueryBuilder())
|
when(queryParseContext.parseInnerQueryBuilder())
|
||||||
.thenReturn(Optional.of((QueryBuilder) new TermQueryBuilder("field", values[i])));
|
.thenReturn(Optional.of(new TermQueryBuilder("field", values[i])));
|
||||||
when(queryShardContext.toQuery(any(QueryBuilder.class))).thenReturn(parsedQuery);
|
when(queryShardContext.toQuery(any(QueryBuilder.class))).thenReturn(parsedQuery);
|
||||||
DirectoryReader wrappedDirectoryReader = wrapper.wrap(directoryReader);
|
DirectoryReader wrappedDirectoryReader = wrapper.wrap(directoryReader);
|
||||||
IndexSearcher indexSearcher = wrapper.wrap(new IndexSearcher(wrappedDirectoryReader));
|
IndexSearcher indexSearcher = wrapper.wrap(new IndexSearcher(wrappedDirectoryReader));
|
||||||
|
|
|
@ -35,33 +35,48 @@ import org.apache.lucene.util.BitSet;
|
||||||
import org.apache.lucene.util.FixedBitSet;
|
import org.apache.lucene.util.FixedBitSet;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
import org.apache.lucene.util.SparseFixedBitSet;
|
import org.apache.lucene.util.SparseFixedBitSet;
|
||||||
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
|
import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.IndexSettings;
|
import org.elasticsearch.index.IndexSettings;
|
||||||
import org.elasticsearch.index.analysis.AnalysisService;
|
import org.elasticsearch.index.analysis.AnalysisService;
|
||||||
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
|
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
||||||
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
import org.elasticsearch.index.shard.IndexShard;
|
import org.elasticsearch.index.shard.IndexShard;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.indices.IndicesModule;
|
import org.elasticsearch.indices.IndicesModule;
|
||||||
|
import org.elasticsearch.script.ExecutableScript;
|
||||||
|
import org.elasticsearch.script.Script;
|
||||||
|
import org.elasticsearch.script.ScriptContext;
|
||||||
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.aggregations.LeafBucketCollector;
|
import org.elasticsearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
||||||
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.IndexSettingsModule;
|
import org.elasticsearch.test.IndexSettingsModule;
|
||||||
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
@ -75,13 +90,18 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.sameInstance;
|
import static org.hamcrest.Matchers.sameInstance;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
|
|
||||||
private ThreadContext threadContext;
|
private ThreadContext threadContext;
|
||||||
private MapperService mapperService;
|
private MapperService mapperService;
|
||||||
|
private ScriptService scriptService;
|
||||||
private SecurityIndexSearcherWrapper securityIndexSearcherWrapper;
|
private SecurityIndexSearcherWrapper securityIndexSearcherWrapper;
|
||||||
private ElasticsearchDirectoryReader esIn;
|
private ElasticsearchDirectoryReader esIn;
|
||||||
private SecurityLicenseState licenseState;
|
private SecurityLicenseState licenseState;
|
||||||
|
@ -90,6 +110,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
Index index = new Index("_index", "testUUID");
|
Index index = new Index("_index", "testUUID");
|
||||||
|
scriptService = mock(ScriptService.class);
|
||||||
indexSettings = IndexSettingsModule.newIndexSettings(index, Settings.EMPTY);
|
indexSettings = IndexSettingsModule.newIndexSettings(index, Settings.EMPTY);
|
||||||
AnalysisService analysisService = new AnalysisService(indexSettings, Collections.emptyMap(), Collections.emptyMap(),
|
AnalysisService analysisService = new AnalysisService(indexSettings, Collections.emptyMap(), Collections.emptyMap(),
|
||||||
Collections.emptyMap(), Collections.emptyMap());
|
Collections.emptyMap(), Collections.emptyMap());
|
||||||
|
@ -125,7 +146,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
mapperService.merge("type", new CompressedXContent(mappingSource.string()), MapperService.MergeReason.MAPPING_UPDATE, false);
|
mapperService.merge("type", new CompressedXContent(mappingSource.string()), MapperService.MergeReason.MAPPING_UPDATE, false);
|
||||||
|
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState) {
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
|
||||||
@Override
|
@Override
|
||||||
protected IndicesAccessControl getIndicesAccessControl() {
|
protected IndicesAccessControl getIndicesAccessControl() {
|
||||||
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
|
@ -156,14 +177,14 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
public void testWrapReaderWhenFeatureDisabled() throws Exception {
|
public void testWrapReaderWhenFeatureDisabled() throws Exception {
|
||||||
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(false);
|
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(false);
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
|
||||||
DirectoryReader reader = securityIndexSearcherWrapper.wrap(esIn);
|
DirectoryReader reader = securityIndexSearcherWrapper.wrap(esIn);
|
||||||
assertThat(reader, sameInstance(esIn));
|
assertThat(reader, sameInstance(esIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWrapSearcherWhenFeatureDisabled() throws Exception {
|
public void testWrapSearcherWhenFeatureDisabled() throws Exception {
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
|
||||||
IndexSearcher indexSearcher = new IndexSearcher(esIn);
|
IndexSearcher indexSearcher = new IndexSearcher(esIn);
|
||||||
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
|
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
|
||||||
assertThat(result, sameInstance(indexSearcher));
|
assertThat(result, sameInstance(indexSearcher));
|
||||||
|
@ -275,7 +296,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
DirectoryReader directoryReader = DocumentSubsetReader.wrap(esIn, bitsetFilterCache, new MatchAllDocsQuery());
|
DirectoryReader directoryReader = DocumentSubsetReader.wrap(esIn, bitsetFilterCache, new MatchAllDocsQuery());
|
||||||
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
|
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
|
||||||
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
|
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
|
||||||
assertThat(result, not(sameInstance(indexSearcher)));
|
assertThat(result, not(sameInstance(indexSearcher)));
|
||||||
assertThat(result.getSimilarity(true), sameInstance(indexSearcher.getSimilarity(true)));
|
assertThat(result.getSimilarity(true), sameInstance(indexSearcher.getSimilarity(true)));
|
||||||
|
@ -284,7 +305,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIntersectScorerAndRoleBits() throws Exception {
|
public void testIntersectScorerAndRoleBits() throws Exception {
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
|
||||||
final Directory directory = newDirectory();
|
final Directory directory = newDirectory();
|
||||||
IndexWriter iw = new IndexWriter(
|
IndexWriter iw = new IndexWriter(
|
||||||
directory,
|
directory,
|
||||||
|
@ -373,7 +394,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
|
|
||||||
private void assertResolvedFields(String expression, String... expectedFields) {
|
private void assertResolvedFields(String expression, String... expectedFields) {
|
||||||
securityIndexSearcherWrapper =
|
securityIndexSearcherWrapper =
|
||||||
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState) {
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
|
||||||
@Override
|
@Override
|
||||||
protected IndicesAccessControl getIndicesAccessControl() {
|
protected IndicesAccessControl getIndicesAccessControl() {
|
||||||
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
|
@ -407,6 +428,60 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
doTestIndexSearcherWrapper(false, true);
|
doTestIndexSearcherWrapper(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testTemplating() throws Exception {
|
||||||
|
User user = new User("_username", new String[]{"role1", "role2"}, "_full_name", "_email",
|
||||||
|
Collections.singletonMap("key", "value"));
|
||||||
|
securityIndexSearcherWrapper =
|
||||||
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExecutableScript executableScript = mock(ExecutableScript.class);
|
||||||
|
when(scriptService.executable(any(Script.class), eq(ScriptContext.Standard.SEARCH), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(executableScript);
|
||||||
|
|
||||||
|
XContentBuilder builder = jsonBuilder();
|
||||||
|
String query = new TermQueryBuilder("field", "{{_user.username}}").toXContent(builder, ToXContent.EMPTY_PARAMS).string();
|
||||||
|
Script script = new Script(query, ScriptService.ScriptType.INLINE, null, Collections.singletonMap("custom", "value"));
|
||||||
|
builder = jsonBuilder().startObject().field("template");
|
||||||
|
script.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
|
BytesReference querySource = builder.endObject().bytes();
|
||||||
|
|
||||||
|
securityIndexSearcherWrapper.evaluateTemplate(querySource);
|
||||||
|
ArgumentCaptor<Script> argument = ArgumentCaptor.forClass(Script.class);
|
||||||
|
verify(scriptService).executable(argument.capture(), eq(ScriptContext.Standard.SEARCH), eq(Collections.emptyMap()));
|
||||||
|
Script usedScript = argument.getValue();
|
||||||
|
assertThat(usedScript.getScript(), equalTo(script.getScript()));
|
||||||
|
assertThat(usedScript.getType(), equalTo(script.getType()));
|
||||||
|
assertThat(usedScript.getLang(), equalTo("mustache"));
|
||||||
|
assertThat(usedScript.getContentType(), equalTo(script.getContentType()));
|
||||||
|
assertThat(usedScript.getParams().size(), equalTo(2));
|
||||||
|
assertThat(usedScript.getParams().get("custom"), equalTo("value"));
|
||||||
|
|
||||||
|
Map<String, Object> userModel = new HashMap<>();
|
||||||
|
userModel.put("username", user.principal());
|
||||||
|
userModel.put("full_name", user.fullName());
|
||||||
|
userModel.put("email", user.email());
|
||||||
|
userModel.put("roles", Arrays.asList(user.roles()));
|
||||||
|
userModel.put("metadata", user.metadata());
|
||||||
|
assertThat(usedScript.getParams().get("_user"), equalTo(userModel));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipTemplating() throws Exception {
|
||||||
|
securityIndexSearcherWrapper =
|
||||||
|
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
|
||||||
|
XContentBuilder builder = jsonBuilder();
|
||||||
|
BytesReference querySource = new TermQueryBuilder("field", "value").toXContent(builder, ToXContent.EMPTY_PARAMS).bytes();
|
||||||
|
BytesReference result = securityIndexSearcherWrapper.evaluateTemplate(querySource);
|
||||||
|
assertThat(result, sameInstance(querySource));
|
||||||
|
verifyZeroInteractions(scriptService);
|
||||||
|
}
|
||||||
|
|
||||||
static class CreateScorerOnceWeight extends Weight {
|
static class CreateScorerOnceWeight extends Weight {
|
||||||
|
|
||||||
private final Weight weight;
|
private final Weight weight;
|
||||||
|
|
Loading…
Reference in New Issue