Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FLS for frozen tier #82521

Merged
merged 5 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.SourceFieldMapper;
import org.elasticsearch.transport.Transports;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentType;

Expand All @@ -48,6 +49,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* A {@link FilterLeafReader} that exposes only a subset
Expand Down Expand Up @@ -122,7 +124,7 @@ public CacheHelper getReaderCacheHelper() {
/** An automaton that only accepts authorized fields. */
private final CharacterRunAutomaton filter;
/** {@link Terms} cache with filtered stats for the {@link FieldNamesFieldMapper} field. */
private final Terms fieldNamesFilterTerms;
private volatile Optional<Terms> fieldNamesFilterTerms;

/**
* Wrap a single segment, exposing a subset of its fields.
Expand All @@ -137,8 +139,6 @@ public CacheHelper getReaderCacheHelper() {
}
fieldInfos = new FieldInfos(filteredInfos.toArray(new FieldInfo[filteredInfos.size()]));
this.filter = filter;
final Terms fieldNameTerms = super.terms(FieldNamesFieldMapper.NAME);
this.fieldNamesFilterTerms = fieldNameTerms == null ? null : new FieldNamesTerms(fieldNameTerms);
}

/** returns true if this field is allowed. */
Expand Down Expand Up @@ -432,7 +432,18 @@ private Terms wrapTerms(Terms terms, String field) throws IOException {
// for the _field_names field, fields for the document
// are encoded as postings, where term is the field.
// so we hide terms for fields we filter out.
return fieldNamesFilterTerms;
if (fieldNamesFilterTerms == null) {
synchronized (this) {
if (fieldNamesFilterTerms == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you leave a comment that explains why this is computed lazily?

assert Transports.assertNotTransportThread("resolving filter terms");
final Terms fieldNameTerms = super.terms(FieldNamesFieldMapper.NAME);
this.fieldNamesFilterTerms = Optional.ofNullable(
fieldNameTerms == null ? null : new FieldNamesTerms(fieldNameTerms)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it feels a bit weird to use Optional.ofNullable when you need to check for null already anyway, what about something like that instead?

this.fieldNamesFilterTerms = fieldNameTerms == null ? Optional.empty() : Optional.of(new FieldNamesTerms(fieldNameTerms));

}
}
}
return fieldNamesFilterTerms.orElse(null);
} else {
return terms;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ setup:
{
"names": ["*"],
"privileges": ["read"],
"query": {"match": {"marker": "test_1"}}
"query": {"match": {"marker": "test_1"}},
"field_security" : {
"grant" : [ "*" ],
"except" : [ "forbidden_field" ]
}
}
]
}
Expand Down Expand Up @@ -72,14 +76,37 @@ setup:

- do:
indices.create:
index: test_index
index: test_index1
body:
mappings:
properties:
location:
properties:
city:
type: "keyword"
created_at:
type: date # add date field to trigger can-match phase in searches
format: "yyyy-MM-dd"

settings:
index:
number_of_shards: 1
number_of_replicas: 0

- do:
indices.create:
index: test_index2
body:
mappings:
properties:
location:
properties:
city:
type: "keyword"
created_at:
type: date # add date field to trigger can-match phase in searches
format: "yyyy-MM-dd"

settings:
index:
number_of_shards: 1
Expand All @@ -89,10 +116,14 @@ setup:
bulk:
refresh: true
body:
- '{"index": {"_index": "test_index"}}'
- '{"marker": "test_1", "location.city": "bos"}'
- '{"index": {"_index": "test_index"}}'
- '{"marker": "test_2", "location.city": "ams"}'
- '{"index": {"_index": "test_index1"}}'
- '{"marker": "test_1", "location.city": "bos", "forbidden_field" : 1, "created_at": "2016-01-01"}'
- '{"index": {"_index": "test_index1"}}'
- '{"marker": "test_2", "location.city": "ams", "forbidden_field" : 2, "created_at": "2016-01-01"}'
- '{"index": {"_index": "test_index2"}}'
- '{"marker": "test_2", "location.city": "bos", "forbidden_field" : 1, "created_at": "2019-01-02"}'
- '{"index": {"_index": "test_index2"}}'
- '{"marker": "test_2", "location.city": "ams", "forbidden_field" : 2, "created_at": "2019-01-02"}'

- do:
snapshot.create:
Expand All @@ -102,7 +133,11 @@ setup:

- do:
indices.delete:
index: test_index
index: test_index1

- do:
indices.delete:
index: test_index2

---
teardown:
Expand Down Expand Up @@ -136,8 +171,22 @@ teardown:
wait_for_completion: true
storage: full_copy
body:
index: test_index
renamed_index: test_index
index: test_index1
renamed_index: test_index1

- match: { snapshot.snapshot: snapshot }
- match: { snapshot.shards.failed: 0 }
- match: { snapshot.shards.successful: 1 }

- do:
searchable_snapshots.mount:
repository: repository-fs
snapshot: snapshot
wait_for_completion: true
storage: full_copy
body:
index: test_index2
renamed_index: test_index2

- match: { snapshot.snapshot: snapshot }
- match: { snapshot.shards.failed: 0 }
Expand All @@ -147,16 +196,22 @@ teardown:
headers: { Authorization: "Basic ZnVsbDp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # full - user
search:
rest_total_hits_as_int: true
index: test_index
index: test_index*
size: 0
from: 0
body:
query:
range:
created_at:
"gte": "2016-01-01"
"lt": "2018-02-01"
aggs:
cities:
terms:
field: location.city

- match: { _shards.total: 1 }
- match: { _shards.total: 2 }
- match: { _shards.skipped: 1 }
- match: { hits.total: 2 }
- length: { aggregations.cities.buckets: 2 }
- match: { aggregations.cities.buckets.0.key: "ams" }
Expand All @@ -168,24 +223,30 @@ teardown:
headers: { Authorization: "Basic bGltaXRlZDp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # limited - user
search:
rest_total_hits_as_int: true
index: test_index
index: test_index*
size: 0
from: 0
body:
query:
range:
created_at:
"gte": "2016-01-01"
"lt": "2018-02-01"
aggs:
cities:
terms:
field: location.city

- match: { _shards.total: 1 }
- match: { _shards.total: 2 }
- match: { _shards.skipped: 1 }
- match: { hits.total: 1 }
- length: { aggregations.cities.buckets: 1 }
- match: { aggregations.cities.buckets.0.key: "bos" }
- match: { aggregations.cities.buckets.0.doc_count: 1 }

- do:
indices.delete:
index: test_index
index: test_index*

---
"Test doc level security with different users on shared_cache index":
Expand All @@ -197,8 +258,22 @@ teardown:
wait_for_completion: true
storage: shared_cache
body:
index: test_index
renamed_index: test_index
index: test_index1
renamed_index: test_index1

- match: { snapshot.snapshot: snapshot }
- match: { snapshot.shards.failed: 0 }
- match: { snapshot.shards.successful: 1 }

- do:
searchable_snapshots.mount:
repository: repository-fs
snapshot: snapshot
wait_for_completion: true
storage: shared_cache
body:
index: test_index2
renamed_index: test_index2

- match: { snapshot.snapshot: snapshot }
- match: { snapshot.shards.failed: 0 }
Expand All @@ -208,16 +283,22 @@ teardown:
headers: { Authorization: "Basic ZnVsbDp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # full - user
search:
rest_total_hits_as_int: true
index: test_index
index: test_index*
size: 0
from: 0
body:
query:
range:
created_at:
"gte": "2016-01-01"
"lt": "2018-02-01"
aggs:
cities:
terms:
field: location.city

- match: { _shards.total: 1 }
- match: { _shards.total: 2 }
- match: { _shards.skipped: 1 }
- match: { hits.total: 2 }
- length: { aggregations.cities.buckets: 2 }
- match: { aggregations.cities.buckets.0.key: "ams" }
Expand All @@ -229,21 +310,27 @@ teardown:
headers: { Authorization: "Basic bGltaXRlZDp4LXBhY2stdGVzdC1wYXNzd29yZA==" } # limited - user
search:
rest_total_hits_as_int: true
index: test_index
index: test_index*
size: 0
from: 0
body:
query:
range:
created_at:
"gte": "2016-01-01"
"lt": "2018-02-01"
aggs:
cities:
terms:
field: location.city

- match: { _shards.total: 1 }
- match: { _shards.total: 2 }
- match: { _shards.skipped: 1 }
- match: { hits.total: 1 }
- length: { aggregations.cities.buckets: 1 }
- match: { aggregations.cities.buckets.0.key: "bos" }
- match: { aggregations.cities.buckets.0.doc_count: 1 }

- do:
indices.delete:
index: test_index
index: test_index*