Skip to content

Commit 3119e3e

Browse files
committed
using FileAccessTree
1 parent e35c78c commit 3119e3e

File tree

4 files changed

+87
-50
lines changed

4 files changed

+87
-50
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.entitlement.instrumentation.MethodKey;
2222
import org.elasticsearch.entitlement.instrumentation.Transformer;
2323
import org.elasticsearch.entitlement.runtime.api.ElasticsearchEntitlementChecker;
24+
import org.elasticsearch.entitlement.runtime.policy.FileAccessTree;
2425
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
2526
import org.elasticsearch.entitlement.runtime.policy.Policy;
2627
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
@@ -353,45 +354,64 @@ static void validateFilesEntitlements(
353354
Path modulesDir,
354355
Path libDir
355356
) {
356-
var pluginReadAccessForbidden = pathSet(pluginsDir, modulesDir, libDir);
357-
var pluginWriteAccessForbidden = pathSet(configDir);
357+
var readAccessForbidden = pathSet(pluginsDir, modulesDir, libDir);
358+
var writeAccessForbidden = pathSet(configDir);
358359
for (var pluginPolicy : pluginPolicies.entrySet()) {
359-
List<Path> readPaths = getFileDataStream(pluginPolicy.getValue()).flatMap(x -> x.resolvePaths(pathLookup)).toList();
360-
List<Path> writePaths = getFileDataStream(pluginPolicy.getValue()).filter(x -> x.mode().equals(READ_WRITE))
361-
.flatMap(x -> x.resolvePaths(pathLookup))
362-
.toList();
363-
validateLayerFilesEntitlements(pluginPolicy.getKey(), readPaths, pluginReadAccessForbidden, READ);
364-
validateLayerFilesEntitlements(pluginPolicy.getKey(), writePaths, pluginWriteAccessForbidden, READ_WRITE);
360+
for (var scope : pluginPolicy.getValue().scopes()) {
361+
var filesEntitlement = scope.entitlements()
362+
.stream()
363+
.filter(x -> x instanceof FilesEntitlement)
364+
.map(x -> ((FilesEntitlement) x))
365+
.findFirst();
366+
if (filesEntitlement.isPresent()) {
367+
var fileAccessTree = FileAccessTree.withoutExclusivePaths(filesEntitlement.get(), pathLookup, null);
368+
validateReadFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, readAccessForbidden);
369+
validateWriteFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, writeAccessForbidden);
370+
}
371+
}
365372
}
366373
}
367374

368-
private static Stream<FileData> getFileDataStream(Policy policy) {
369-
return getFileDataStream(policy.scopes().stream().flatMap(x -> x.entitlements().stream()));
375+
private static IllegalArgumentException buildValidationException(
376+
String componentName,
377+
String moduleName,
378+
Path forbiddenPath,
379+
FilesEntitlement.Mode mode
380+
) {
381+
return new IllegalArgumentException(
382+
Strings.format(
383+
"policy for module [%s] in [%s] has an invalid file entitlement. Any path under [%s] is forbidden for mode [%s].",
384+
moduleName,
385+
componentName,
386+
forbiddenPath,
387+
mode
388+
)
389+
);
370390
}
371391

372-
private static Stream<FileData> getFileDataStream(Stream<Entitlement> entitlements) {
373-
return entitlements.filter(x -> x instanceof FilesEntitlement).flatMap(x -> ((FilesEntitlement) x).filesData().stream());
392+
private static void validateReadFilesEntitlements(
393+
String componentName,
394+
String moduleName,
395+
FileAccessTree fileAccessTree,
396+
Set<Path> readForbiddenPaths
397+
) {
398+
399+
for (Path forbiddenPath : readForbiddenPaths) {
400+
if (fileAccessTree.canRead(forbiddenPath)) {
401+
throw buildValidationException(componentName, moduleName, forbiddenPath, READ);
402+
}
403+
}
374404
}
375405

376-
private static void validateLayerFilesEntitlements(
377-
String layerName,
378-
List<Path> paths,
379-
Set<Path> forbiddenPaths,
380-
FilesEntitlement.Mode mode
406+
private static void validateWriteFilesEntitlements(
407+
String componentName,
408+
String moduleName,
409+
FileAccessTree fileAccessTree,
410+
Set<Path> writeForbiddenPaths
381411
) {
382-
for (var path : paths) {
383-
for (Path forbiddenPath : forbiddenPaths) {
384-
if (path.startsWith(forbiddenPath)) {
385-
throw new IllegalArgumentException(
386-
Strings.format(
387-
"policy for [%s] cannot contain a file entitlement for [%s]. Any path under [%s] is forbidden for mode [%s].",
388-
layerName,
389-
path,
390-
forbiddenPath,
391-
mode
392-
)
393-
);
394-
}
412+
for (Path forbiddenPath : writeForbiddenPaths) {
413+
if (fileAccessTree.canWrite(forbiddenPath)) {
414+
throw buildValidationException(componentName, moduleName, forbiddenPath, READ_WRITE);
395415
}
396416
}
397417
}

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,9 @@ static void validateExclusivePaths(List<ExclusivePath> exclusivePaths) {
111111
private final String[] readPaths;
112112
private final String[] writePaths;
113113

114-
private FileAccessTree(
114+
private static String[] buildUpdatedAndSortedExclusivePaths(
115115
String componentName,
116116
String moduleName,
117-
FilesEntitlement filesEntitlement,
118-
PathLookup pathLookup,
119-
Path componentPath,
120117
List<ExclusivePath> exclusivePaths
121118
) {
122119
List<String> updatedExclusivePaths = new ArrayList<>();
@@ -125,7 +122,11 @@ private FileAccessTree(
125122
updatedExclusivePaths.add(exclusivePath.path());
126123
}
127124
}
125+
updatedExclusivePaths.sort(PATH_ORDER);
126+
return updatedExclusivePaths.toArray(new String[0]);
127+
}
128128

129+
private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup, Path componentPath, String[] sortedExclusivePaths) {
129130
List<String> readPaths = new ArrayList<>();
130131
List<String> writePaths = new ArrayList<>();
131132
BiConsumer<Path, Mode> addPath = (path, mode) -> {
@@ -177,11 +178,10 @@ private FileAccessTree(
177178
Path jdk = Paths.get(System.getProperty("java.home"));
178179
addPathAndMaybeLink.accept(jdk.resolve("conf"), Mode.READ);
179180

180-
updatedExclusivePaths.sort(PATH_ORDER);
181181
readPaths.sort(PATH_ORDER);
182182
writePaths.sort(PATH_ORDER);
183183

184-
this.exclusivePaths = updatedExclusivePaths.toArray(new String[0]);
184+
this.exclusivePaths = sortedExclusivePaths;
185185
this.readPaths = pruneSortedPaths(readPaths).toArray(new String[0]);
186186
this.writePaths = pruneSortedPaths(writePaths).toArray(new String[0]);
187187
}
@@ -211,14 +211,30 @@ static FileAccessTree of(
211211
@Nullable Path componentPath,
212212
List<ExclusivePath> exclusivePaths
213213
) {
214-
return new FileAccessTree(componentName, moduleName, filesEntitlement, pathLookup, componentPath, exclusivePaths);
214+
return new FileAccessTree(
215+
filesEntitlement,
216+
pathLookup,
217+
componentPath,
218+
buildUpdatedAndSortedExclusivePaths(componentName, moduleName, exclusivePaths)
219+
);
220+
}
221+
222+
/**
223+
* A special factory method to create a FileAccessTree with no ExclusivePaths, e.g. for quick validation or for default file access
224+
*/
225+
public static FileAccessTree withoutExclusivePaths(
226+
FilesEntitlement filesEntitlement,
227+
PathLookup pathLookup,
228+
@Nullable Path componentPath
229+
) {
230+
return new FileAccessTree(filesEntitlement, pathLookup, componentPath, new String[0]);
215231
}
216232

217-
boolean canRead(Path path) {
233+
public boolean canRead(Path path) {
218234
return checkPath(normalizePath(path), readPaths);
219235
}
220236

221-
boolean canWrite(Path path) {
237+
public boolean canWrite(Path path) {
222238
return checkPath(normalizePath(path), writePaths);
223239
}
224240

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,13 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
101101
}
102102
}
103103

104-
private FileAccessTree getDefaultFileAccess(String componentName, Path componentPath) {
105-
return FileAccessTree.of(componentName, UNKNOWN_COMPONENT_NAME, FilesEntitlement.EMPTY, pathLookup, componentPath, List.of());
104+
private FileAccessTree getDefaultFileAccess(Path componentPath) {
105+
return FileAccessTree.withoutExclusivePaths(FilesEntitlement.EMPTY, pathLookup, componentPath);
106106
}
107107

108108
// pkg private for testing
109109
ModuleEntitlements defaultEntitlements(String componentName, Path componentPath, String moduleName) {
110-
return new ModuleEntitlements(
111-
componentName,
112-
Map.of(),
113-
getDefaultFileAccess(componentName, componentPath),
114-
getLogger(componentName, moduleName)
115-
);
110+
return new ModuleEntitlements(componentName, Map.of(), getDefaultFileAccess(componentPath), getLogger(componentName, moduleName));
116111
}
117112

118113
// pkg private for testing

libs/entitlement/src/test/java/org/elasticsearch/entitlement/initialization/EntitlementInitializationTests.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ public void testValidationFailForRead() {
108108
);
109109
assertThat(
110110
ex.getMessage(),
111-
both(startsWith("policy for [plugin] cannot contain a file entitlement for")).and(endsWith("is forbidden for mode [READ]."))
111+
both(startsWith("policy for module [module2] in [plugin] has an invalid file entitlement")).and(
112+
endsWith("is forbidden for mode [READ].")
113+
)
112114
);
113115

114116
// check fails for mode READ_WRITE too
@@ -138,7 +140,9 @@ public void testValidationFailForRead() {
138140
);
139141
assertThat(
140142
ex.getMessage(),
141-
both(startsWith("policy for [plugin2] cannot contain a file entitlement")).and(endsWith("is forbidden for mode [READ]."))
143+
both(startsWith("policy for module [module1] in [plugin2] has an invalid file entitlement")).and(
144+
endsWith("is forbidden for mode [READ].")
145+
)
142146
);
143147
}
144148

@@ -169,7 +173,9 @@ public void testValidationFailForWrite() {
169173
);
170174
assertThat(
171175
ex.getMessage(),
172-
both(startsWith("policy for [plugin] cannot contain a file entitlement")).and(endsWith("is forbidden for mode [READ_WRITE]."))
176+
both(startsWith("policy for module [module1] in [plugin] has an invalid file entitlement")).and(
177+
endsWith("is forbidden for mode [READ_WRITE].")
178+
)
173179
);
174180
}
175181
}

0 commit comments

Comments
 (0)