Skip to content

Commit aa88dc5

Browse files
committed
Teach field subset reader about multi-fields.
1 parent 580831a commit aa88dc5

File tree

7 files changed

+242
-51
lines changed

7 files changed

+242
-51
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/FieldSubsetReader.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.util.List;
5555
import java.util.Map;
5656
import java.util.Optional;
57+
import java.util.function.Function;
5758

5859
/**
5960
* A {@link FilterLeafReader} that exposes only a subset
@@ -68,36 +69,42 @@ public final class FieldSubsetReader extends SequentialStoredFieldsLeafReader {
6869
* Note that for convenience, the returned reader
6970
* can be used normally (e.g. passed to {@link DirectoryReader#openIfChanged(DirectoryReader)})
7071
* and so on.
71-
* @param in reader to filter
72-
* @param filter fields to filter.
72+
*
73+
* @param in reader to filter
74+
* @param filter fields to filter.
75+
* @param getParentField
7376
*/
74-
public static DirectoryReader wrap(DirectoryReader in, CharacterRunAutomaton filter) throws IOException {
75-
return new FieldSubsetDirectoryReader(in, filter);
77+
public static DirectoryReader wrap(DirectoryReader in, CharacterRunAutomaton filter, Function<String, String> getParentField)
78+
throws IOException {
79+
return new FieldSubsetDirectoryReader(in, filter, getParentField);
7680
}
7781

7882
// wraps subreaders with fieldsubsetreaders.
7983
static class FieldSubsetDirectoryReader extends FilterDirectoryReader {
8084

8185
private final CharacterRunAutomaton filter;
86+
private final Function<String, String> getParentField;
8287

83-
FieldSubsetDirectoryReader(DirectoryReader in, final CharacterRunAutomaton filter) throws IOException {
88+
FieldSubsetDirectoryReader(DirectoryReader in, final CharacterRunAutomaton filter, Function<String, String> getParentField)
89+
throws IOException {
8490
super(in, new FilterDirectoryReader.SubReaderWrapper() {
8591
@Override
8692
public LeafReader wrap(LeafReader reader) {
8793
try {
88-
return new FieldSubsetReader(reader, filter);
94+
return new FieldSubsetReader(reader, filter, getParentField);
8995
} catch (IOException e) {
9096
throw new UncheckedIOException(e);
9197
}
9298
}
9399
});
94100
this.filter = filter;
101+
this.getParentField = getParentField;
95102
verifyNoOtherFieldSubsetDirectoryReaderIsWrapped(in);
96103
}
97104

98105
@Override
99106
protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) throws IOException {
100-
return new FieldSubsetDirectoryReader(in, filter);
107+
return new FieldSubsetDirectoryReader(in, filter, getParentField);
101108
}
102109

103110
/** Return the automaton that is used to filter fields. */
@@ -125,6 +132,7 @@ public CacheHelper getReaderCacheHelper() {
125132

126133
/** List of filtered fields */
127134
private final FieldInfos fieldInfos;
135+
private final Function<String, String> getParentField;
128136
/** An automaton that only accepts authorized fields. */
129137
private final CharacterRunAutomaton filter;
130138
/** {@link Terms} cache with filtered stats for the {@link FieldNamesFieldMapper} field. */
@@ -133,15 +141,20 @@ public CacheHelper getReaderCacheHelper() {
133141
/**
134142
* Wrap a single segment, exposing a subset of its fields.
135143
*/
136-
FieldSubsetReader(LeafReader in, CharacterRunAutomaton filter) throws IOException {
144+
FieldSubsetReader(LeafReader in, CharacterRunAutomaton filter, Function<String, String> getParentField) throws IOException {
137145
super(in);
138146
ArrayList<FieldInfo> filteredInfos = new ArrayList<>();
139147
for (FieldInfo fi : in.getFieldInfos()) {
140148
if (filter.run(fi.name)) {
149+
String parentField = getParentField.apply(fi.name);
150+
if (parentField != null && filter.run(parentField) == false) {
151+
continue;
152+
}
141153
filteredInfos.add(fi);
142154
}
143155
}
144156
fieldInfos = new FieldInfos(filteredInfos.toArray(new FieldInfo[filteredInfos.size()]));
157+
this.getParentField = getParentField;
145158
this.filter = filter;
146159
}
147160

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapper.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ public DirectoryReader apply(final DirectoryReader reader) {
9696
}
9797
}
9898

99-
return permissions.getFieldPermissions().filter(wrappedReader);
99+
var mappingLookup = searchExecutionContextProvider.apply(shardId).getMappingLookup();
100+
Function<String, String> getParentField = mappingLookup::parentField;
101+
return permissions.getFieldPermissions().filter(wrappedReader, getParentField);
100102
} catch (IOException e) {
101103
logger.error("Unable to apply field level security");
102104
throw ExceptionsHelper.convertToElastic(e);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/FieldPermissions.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.List;
3333
import java.util.Objects;
3434
import java.util.Set;
35+
import java.util.function.Function;
3536
import java.util.stream.Collectors;
3637

3738
/**
@@ -243,11 +244,11 @@ public boolean hasFieldLevelSecurity() {
243244
}
244245

245246
/** Return a wrapped reader that only exposes allowed fields. */
246-
public DirectoryReader filter(DirectoryReader reader) throws IOException {
247+
public DirectoryReader filter(DirectoryReader reader, Function<String, String> getParentField) throws IOException {
247248
if (hasFieldLevelSecurity() == false) {
248249
return reader;
249250
}
250-
return FieldSubsetReader.wrap(reader, permittedFieldsAutomaton);
251+
return FieldSubsetReader.wrap(reader, permittedFieldsAutomaton, getParentField);
251252
}
252253

253254
Automaton getIncludeAutomaton() {

0 commit comments

Comments
 (0)