Skip to content

Commit 0bc3cad

Browse files
committed
PathMatchTests
1 parent 7245632 commit 0bc3cad

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.runtime.policy;
11+
12+
import org.elasticsearch.test.ESTestCase;
13+
14+
import java.util.Arrays;
15+
import java.util.Collection;
16+
import java.util.List;
17+
18+
public class PathMatchTests extends ESTestCase {
19+
20+
enum Operation {
21+
READ,
22+
WRITE
23+
};
24+
25+
record FilePermission(String path, Operation operation) {
26+
public boolean isRead() {
27+
return true;
28+
}
29+
30+
public boolean isWrite() {
31+
return operation == Operation.WRITE;
32+
}
33+
}
34+
35+
static class PathMatcher {
36+
final String[] readPaths, writePaths;
37+
38+
PathMatcher(Collection<FilePermission> permissions) {
39+
readPaths = permissions.stream().filter(FilePermission::isRead).map(FilePermission::path).sorted().toArray(String[]::new);
40+
writePaths = permissions.stream().filter(FilePermission::isWrite).map(FilePermission::path).sorted().toArray(String[]::new);
41+
}
42+
43+
public boolean checkRead(String path) {
44+
return check(path, readPaths);
45+
}
46+
47+
public boolean checkWrite(String path) {
48+
return check(path, writePaths);
49+
}
50+
51+
private boolean check(String needle, String[] haystack) {
52+
int candidate;
53+
int searchResult = Arrays.binarySearch(haystack, needle);
54+
if (searchResult >= 0) {
55+
// Exact match
56+
candidate = searchResult;
57+
} else {
58+
int insertionPoint = -(searchResult + 1);
59+
// The longest prefix is the item that would precede needle if needle were inserted in the list.
60+
candidate = insertionPoint - 1;
61+
}
62+
if (candidate < 0) {
63+
// This happens if all haystack entries come after needle lexicographically, so none can be a prefix.
64+
return false;
65+
} else {
66+
return needle.startsWith(haystack[candidate]);
67+
}
68+
}
69+
70+
}
71+
72+
public void testPathMatcher() {
73+
PathMatcher pathMatcher = new PathMatcher(
74+
List.of(
75+
new FilePermission("/etc/test", Operation.READ),
76+
new FilePermission("/tmp", Operation.WRITE),
77+
new FilePermission("/etc/test/test.conf", Operation.WRITE)
78+
)
79+
);
80+
81+
check("/", false, false, pathMatcher);
82+
check("/etc", false, false, pathMatcher);
83+
check("/etc/passed", false, false, pathMatcher);
84+
check("/etc/test", true, false, pathMatcher);
85+
check("/etc/test/some.file", true, false, pathMatcher);
86+
check("/etc/test/subdir/some.file", true, false, pathMatcher);
87+
check("/etc/test/test.conf", true, true, pathMatcher);
88+
check("/tmp", true, true, pathMatcher);
89+
check("/tmp/some.file", true, true, pathMatcher);
90+
}
91+
92+
void check(String needle, boolean canRead, boolean canWrite, PathMatcher pathMatcher) {
93+
assertEquals("Read permission for: " + needle, canRead, pathMatcher.checkRead(needle));
94+
assertEquals("Write permission for: " + needle, canWrite, pathMatcher.checkWrite(needle));
95+
}
96+
}

0 commit comments

Comments
 (0)