diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java index 98076af51ae60..660459f06d58b 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java @@ -49,8 +49,24 @@ private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup) readPaths.sort(String::compareTo); writePaths.sort(String::compareTo); - this.readPaths = readPaths.toArray(new String[0]); - this.writePaths = writePaths.toArray(new String[0]); + this.readPaths = pruneSortedPaths(readPaths).toArray(new String[0]); + this.writePaths = pruneSortedPaths(writePaths).toArray(new String[0]); + } + + private static List pruneSortedPaths(List paths) { + List prunedReadPaths = new ArrayList<>(); + if (paths.isEmpty() == false) { + String currentPath = paths.get(0); + prunedReadPaths.add(currentPath); + for (int i = 1; i < paths.size(); ++i) { + String nextPath = paths.get(i); + if (nextPath.startsWith(currentPath) == false) { + prunedReadPaths.add(nextPath); + currentPath = nextPath; + } + } + } + return prunedReadPaths; } public static FileAccessTree of(FilesEntitlement filesEntitlement, PathLookup pathLookup) { diff --git a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java index 218fc0c956723..4eb3620c276ff 100644 --- a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java +++ b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java @@ -96,6 +96,27 @@ public void testReadWriteUnderRead() { assertThat(tree.canWrite(path("foo/bar")), is(true)); } + public void testPrunedPaths() { + var tree = accessTree(entitlement("foo", "read", "foo/baz", "read", "foo/bar", "read")); + assertThat(tree.canRead(path("foo")), is(true)); + assertThat(tree.canWrite(path("foo")), is(false)); + assertThat(tree.canRead(path("foo/bar")), is(true)); + assertThat(tree.canWrite(path("foo/bar")), is(false)); + assertThat(tree.canRead(path("foo/baz")), is(true)); + assertThat(tree.canWrite(path("foo/baz")), is(false)); + // also test a non-existent subpath + assertThat(tree.canRead(path("foo/barf")), is(true)); + assertThat(tree.canWrite(path("foo/barf")), is(false)); + + tree = accessTree(entitlement("foo", "read", "foo/bar", "read_write")); + assertThat(tree.canRead(path("foo")), is(true)); + assertThat(tree.canWrite(path("foo")), is(false)); + assertThat(tree.canRead(path("foo/bar")), is(true)); + assertThat(tree.canWrite(path("foo/bar")), is(true)); + assertThat(tree.canRead(path("foo/baz")), is(true)); + assertThat(tree.canWrite(path("foo/baz")), is(false)); + } + public void testReadWithRelativePath() { for (var dir : List.of("config", "home")) { var tree = accessTree(entitlement(Map.of("relative_path", "foo", "mode", "read", "relative_to", dir)));