Skip to content

Commit a5bd96a

Browse files
committed
Merge branch 'main' into top_bug
2 parents e3e8dc3 + c1deef4 commit a5bd96a

File tree

13 files changed

+352
-48
lines changed

13 files changed

+352
-48
lines changed

docs/changelog/121715.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 121715
2+
summary: Fix synthetic source issue with deeply nested ignored source fields
3+
area: Mapping
4+
type: bug
5+
issues: []

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/ExternalEntitlement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* using this annotation is considered parseable as part of a policy file
2323
* for entitlements.
2424
*/
25-
@Target(ElementType.CONSTRUCTOR)
25+
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
2626
@Retention(RetentionPolicy.RUNTIME)
2727
public @interface ExternalEntitlement {
2828

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import java.util.List;
1818
import java.util.Objects;
1919

20+
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
21+
2022
public final class FileAccessTree {
2123
public static final FileAccessTree EMPTY = new FileAccessTree(List.of());
24+
private static final String FILE_SEPARATOR = getDefaultFileSystem().getSeparator();
2225

2326
private final String[] readPaths;
2427
private final String[] writePaths;
@@ -27,11 +30,11 @@ private FileAccessTree(List<FileEntitlement> fileEntitlements) {
2730
List<String> readPaths = new ArrayList<>();
2831
List<String> writePaths = new ArrayList<>();
2932
for (FileEntitlement fileEntitlement : fileEntitlements) {
30-
var mode = fileEntitlement.mode();
31-
if (mode == FileEntitlement.Mode.READ_WRITE) {
32-
writePaths.add(fileEntitlement.path());
33+
String path = normalizePath(Path.of(fileEntitlement.path()));
34+
if (fileEntitlement.mode() == FileEntitlement.Mode.READ_WRITE) {
35+
writePaths.add(path);
3336
}
34-
readPaths.add(fileEntitlement.path());
37+
readPaths.add(path);
3538
}
3639

3740
readPaths.sort(String::compareTo);
@@ -46,14 +49,20 @@ public static FileAccessTree of(List<FileEntitlement> fileEntitlements) {
4649
}
4750

4851
boolean canRead(Path path) {
49-
return checkPath(normalize(path), readPaths);
52+
return checkPath(normalizePath(path), readPaths);
5053
}
5154

5255
boolean canWrite(Path path) {
53-
return checkPath(normalize(path), writePaths);
56+
return checkPath(normalizePath(path), writePaths);
5457
}
5558

56-
private static String normalize(Path path) {
59+
/**
60+
* @return the "canonical" form of the given {@code path}, to be used for entitlement checks.
61+
*/
62+
static String normalizePath(Path path) {
63+
// Note that toAbsolutePath produces paths separated by the default file separator,
64+
// so on Windows, if the given path uses forward slashes, this consistently
65+
// converts it to backslashes.
5766
return path.toAbsolutePath().normalize().toString();
5867
}
5968

@@ -64,7 +73,7 @@ private static boolean checkPath(String path, String[] paths) {
6473
int ndx = Arrays.binarySearch(paths, path);
6574
if (ndx < -1) {
6675
String maybeParent = paths[-ndx - 2];
67-
return path.startsWith(maybeParent);
76+
return path.startsWith(maybeParent) && path.startsWith(FILE_SEPARATOR, maybeParent.length());
6877
}
6978
return ndx >= 0;
7079
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.io.UncheckedIOException;
2828
import java.lang.reflect.Constructor;
2929
import java.lang.reflect.InvocationTargetException;
30+
import java.lang.reflect.Method;
31+
import java.lang.reflect.Modifier;
3032
import java.util.ArrayList;
3133
import java.util.Arrays;
3234
import java.util.List;
@@ -147,6 +149,7 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
147149
}
148150

149151
Constructor<?> entitlementConstructor = null;
152+
Method entitlementMethod = null;
150153
ExternalEntitlement entitlementMetadata = null;
151154
for (var ctor : entitlementClass.getConstructors()) {
152155
var metadata = ctor.getAnnotation(ExternalEntitlement.class);
@@ -161,8 +164,27 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
161164
entitlementConstructor = ctor;
162165
entitlementMetadata = metadata;
163166
}
164-
165167
}
168+
for (var method : entitlementClass.getMethods()) {
169+
var metadata = method.getAnnotation(ExternalEntitlement.class);
170+
if (metadata != null) {
171+
if (Modifier.isStatic(method.getModifiers()) == false) {
172+
throw new IllegalStateException(
173+
"entitlement class [" + entitlementClass.getName() + "] has non-static method annotated with ExternalEntitlement"
174+
);
175+
}
176+
if (entitlementMetadata != null) {
177+
throw new IllegalStateException(
178+
"entitlement class ["
179+
+ entitlementClass.getName()
180+
+ "] has more than one constructor and/or method annotated with ExternalEntitlement"
181+
);
182+
}
183+
entitlementMethod = method;
184+
entitlementMetadata = metadata;
185+
}
186+
}
187+
166188
if (entitlementMetadata == null) {
167189
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
168190
}
@@ -171,7 +193,9 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
171193
throw newPolicyParserException("entitlement type [" + entitlementType + "] is allowed only on modules");
172194
}
173195

174-
Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes();
196+
Class<?>[] parameterTypes = entitlementConstructor != null
197+
? entitlementConstructor.getParameterTypes()
198+
: entitlementMethod.getParameterTypes();
175199
String[] parametersNames = entitlementMetadata.parameterNames();
176200

177201
if (parameterTypes.length != 0 || parametersNames.length != 0) {
@@ -204,7 +228,11 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
204228
}
205229

206230
try {
207-
return (Entitlement) entitlementConstructor.newInstance(parameterValues);
231+
if (entitlementConstructor != null) {
232+
return (Entitlement) entitlementConstructor.newInstance(parameterValues);
233+
} else {
234+
return (Entitlement) entitlementMethod.invoke(null, parameterValues);
235+
}
208236
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
209237
if (e.getCause() instanceof PolicyValidationException piae) {
210238
throw newPolicyParserException(startLocation, scopeName, entitlementType, piae);

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FileEntitlement.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
import org.elasticsearch.entitlement.runtime.policy.ExternalEntitlement;
1313
import org.elasticsearch.entitlement.runtime.policy.PolicyValidationException;
1414

15-
import java.nio.file.Paths;
16-
1715
/**
18-
* Describes a file entitlement with a path and mode.
16+
* Describes entitlement to access files at a particular location.
17+
*
18+
* @param path the location of the files. For directories, implicitly includes access to
19+
* all contained files and (recursively) subdirectories.
20+
* @param mode the type of operation
1921
*/
2022
public record FileEntitlement(String path, Mode mode) implements Entitlement {
2123

@@ -24,14 +26,6 @@ public enum Mode {
2426
READ_WRITE
2527
}
2628

27-
public FileEntitlement {
28-
path = normalizePath(path);
29-
}
30-
31-
private static String normalizePath(String path) {
32-
return Paths.get(path).toAbsolutePath().normalize().toString();
33-
}
34-
3529
private static Mode parseMode(String mode) {
3630
if (mode.equals("read")) {
3731
return Mode.READ;
@@ -43,7 +37,7 @@ private static Mode parseMode(String mode) {
4337
}
4438

4539
@ExternalEntitlement(parameterNames = { "path", "mode" }, esModulesOnly = false)
46-
public FileEntitlement(String path, String mode) {
47-
this(path, parseMode(mode));
40+
public static FileEntitlement create(String path, String mode) {
41+
return new FileEntitlement(path, parseMode(mode));
4842
}
4943
}

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.nio.file.Path;
1717
import java.util.List;
1818

19+
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
1920
import static org.hamcrest.Matchers.is;
2021

2122
public class FileAccessTreeTests extends ESTestCase {
@@ -41,7 +42,9 @@ public void testRead() {
4142
var tree = FileAccessTree.of(List.of(entitlement("foo", "read")));
4243
assertThat(tree.canRead(path("foo")), is(true));
4344
assertThat(tree.canRead(path("foo/subdir")), is(true));
45+
assertThat(tree.canRead(path("food")), is(false));
4446
assertThat(tree.canWrite(path("foo")), is(false));
47+
assertThat(tree.canWrite(path("food")), is(false));
4548

4649
assertThat(tree.canRead(path("before")), is(false));
4750
assertThat(tree.canRead(path("later")), is(false));
@@ -51,7 +54,9 @@ public void testWrite() {
5154
var tree = FileAccessTree.of(List.of(entitlement("foo", "read_write")));
5255
assertThat(tree.canWrite(path("foo")), is(true));
5356
assertThat(tree.canWrite(path("foo/subdir")), is(true));
57+
assertThat(tree.canWrite(path("food")), is(false));
5458
assertThat(tree.canRead(path("foo")), is(true));
59+
assertThat(tree.canRead(path("food")), is(false));
5560

5661
assertThat(tree.canWrite(path("before")), is(false));
5762
assertThat(tree.canWrite(path("later")), is(false));
@@ -83,8 +88,24 @@ public void testNormalizePath() {
8388
assertThat(tree.canRead(path("")), is(false));
8489
}
8590

91+
public void testForwardSlashes() {
92+
String sep = getDefaultFileSystem().getSeparator();
93+
var tree = FileAccessTree.of(List.of(entitlement("a/b", "read"), entitlement("m" + sep + "n", "read")));
94+
95+
// Native separators work
96+
assertThat(tree.canRead(path("a" + sep + "b")), is(true));
97+
assertThat(tree.canRead(path("m" + sep + "n")), is(true));
98+
99+
// Forward slashes also work
100+
assertThat(tree.canRead(path("a/b")), is(true));
101+
assertThat(tree.canRead(path("m/n")), is(true));
102+
103+
// In case the native separator is a backslash, don't treat that as an escape
104+
assertThat(tree.canRead(path("m\n")), is(false));
105+
}
106+
86107
FileEntitlement entitlement(String path, String mode) {
87108
Path p = path(path);
88-
return new FileEntitlement(p.toString(), mode);
109+
return FileEntitlement.create(p.toString(), mode);
89110
}
90111
}

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyParserTests.java

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ public ManyConstructorsEntitlement(String s) {}
4040
public ManyConstructorsEntitlement(int i) {}
4141
}
4242

43+
public static class ManyMethodsEntitlement implements Entitlement {
44+
@ExternalEntitlement
45+
public static ManyMethodsEntitlement create(String s) {
46+
return new ManyMethodsEntitlement();
47+
}
48+
49+
@ExternalEntitlement
50+
public static ManyMethodsEntitlement create(int i) {
51+
return new ManyMethodsEntitlement();
52+
}
53+
}
54+
55+
public static class ConstructorAndMethodEntitlement implements Entitlement {
56+
@ExternalEntitlement
57+
public static ConstructorAndMethodEntitlement create(String s) {
58+
return new ConstructorAndMethodEntitlement(s);
59+
}
60+
61+
@ExternalEntitlement
62+
public ConstructorAndMethodEntitlement(String s) {}
63+
}
64+
65+
public static class NonStaticMethodEntitlement implements Entitlement {
66+
@ExternalEntitlement
67+
public NonStaticMethodEntitlement create() {
68+
return new NonStaticMethodEntitlement();
69+
}
70+
}
71+
4372
public void testGetEntitlementTypeName() {
4473
assertEquals("create_class_loader", PolicyParser.getEntitlementTypeName(CreateClassLoaderEntitlement.class));
4574

@@ -55,7 +84,7 @@ public void testPolicyBuilder() throws IOException {
5584
.parsePolicy();
5685
Policy expected = new Policy(
5786
"test-policy.yaml",
58-
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write"))))
87+
List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
5988
);
6089
assertEquals(expected, parsedPolicy);
6190
}
@@ -65,7 +94,7 @@ public void testPolicyBuilderOnExternalPlugin() throws IOException {
6594
.parsePolicy();
6695
Policy expected = new Policy(
6796
"test-policy.yaml",
68-
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write"))))
97+
List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
6998
);
7099
assertEquals(expected, parsedPolicy);
71100
}
@@ -174,4 +203,60 @@ public void testMultipleConstructorsAnnotated() throws IOException {
174203
)
175204
);
176205
}
206+
207+
public void testMultipleMethodsAnnotated() throws IOException {
208+
var parser = new PolicyParser(new ByteArrayInputStream("""
209+
entitlement-module-name:
210+
- many_methods
211+
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("many_methods", ManyMethodsEntitlement.class));
212+
213+
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
214+
assertThat(
215+
e.getMessage(),
216+
equalTo(
217+
"entitlement class "
218+
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ManyMethodsEntitlement]"
219+
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
220+
)
221+
);
222+
}
223+
224+
public void testConstructorAndMethodAnnotated() throws IOException {
225+
var parser = new PolicyParser(
226+
new ByteArrayInputStream("""
227+
entitlement-module-name:
228+
- constructor_and_method
229+
""".getBytes(StandardCharsets.UTF_8)),
230+
"test-policy.yaml",
231+
true,
232+
Map.of("constructor_and_method", ConstructorAndMethodEntitlement.class)
233+
);
234+
235+
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
236+
assertThat(
237+
e.getMessage(),
238+
equalTo(
239+
"entitlement class "
240+
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ConstructorAndMethodEntitlement]"
241+
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
242+
)
243+
);
244+
}
245+
246+
public void testNonStaticMethodAnnotated() throws IOException {
247+
var parser = new PolicyParser(new ByteArrayInputStream("""
248+
entitlement-module-name:
249+
- non_static
250+
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("non_static", NonStaticMethodEntitlement.class));
251+
252+
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
253+
assertThat(
254+
e.getMessage(),
255+
equalTo(
256+
"entitlement class "
257+
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$NonStaticMethodEntitlement]"
258+
+ " has non-static method annotated with ExternalEntitlement"
259+
)
260+
);
261+
}
177262
}

server/src/main/java/org/elasticsearch/TransportVersion.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ public static List<TransportVersion> getAllVersions() {
118118
return VersionsHolder.ALL_VERSIONS;
119119
}
120120

121+
/**
122+
* @return whether this is a known {@link TransportVersion}, i.e. one declared in {@link TransportVersions}. Other versions may exist
123+
* in the wild (they're sent over the wire by numeric ID) but we don't know how to communicate using such versions.
124+
*/
125+
public boolean isKnown() {
126+
return VersionsHolder.ALL_VERSIONS_MAP.containsKey(id);
127+
}
128+
121129
public static TransportVersion fromString(String str) {
122130
return TransportVersion.fromId(Integer.parseInt(str));
123131
}

server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,7 @@ public boolean setIgnoredValues(Map<String, List<IgnoredSourceFieldMapper.NameVa
11271127
for (SourceLoader.SyntheticFieldLoader loader : fields) {
11281128
ignoredValuesPresent |= loader.setIgnoredValues(objectsWithIgnoredFields);
11291129
}
1130-
return this.ignoredValues != null;
1130+
return ignoredValuesPresent;
11311131
}
11321132

11331133
@Override

0 commit comments

Comments
 (0)