mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 01:22:26 -04:00
Move FilesEntitlements validation to a separate class (#127703)
Moves FilesEntitlements validation to a separate class. This is the final PR to make EntitlementsInitialization a simpler "orchestrator" of the various steps in the initialization phase.
This commit is contained in:
parent
64568ee59e
commit
f90b01597c
3 changed files with 104 additions and 77 deletions
|
@ -10,11 +10,9 @@
|
|||
package org.elasticsearch.entitlement.initialization;
|
||||
|
||||
import org.elasticsearch.core.Booleans;
|
||||
import org.elasticsearch.core.Strings;
|
||||
import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap;
|
||||
import org.elasticsearch.entitlement.bridge.EntitlementChecker;
|
||||
import org.elasticsearch.entitlement.runtime.api.ElasticsearchEntitlementChecker;
|
||||
import org.elasticsearch.entitlement.runtime.policy.FileAccessTree;
|
||||
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
|
||||
import org.elasticsearch.entitlement.runtime.policy.Policy;
|
||||
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
|
||||
|
@ -39,7 +37,6 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -248,7 +245,7 @@ public class EntitlementInitialization {
|
|||
)
|
||||
);
|
||||
|
||||
validateFilesEntitlements(pluginPolicies, pathLookup);
|
||||
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
|
||||
|
||||
return new PolicyManager(
|
||||
serverPolicy,
|
||||
|
@ -262,74 +259,6 @@ public class EntitlementInitialization {
|
|||
);
|
||||
}
|
||||
|
||||
// package visible for tests
|
||||
static void validateFilesEntitlements(Map<String, Policy> pluginPolicies, PathLookup pathLookup) {
|
||||
Set<Path> readAccessForbidden = new HashSet<>();
|
||||
pathLookup.getBaseDirPaths(PLUGINS).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
pathLookup.getBaseDirPaths(MODULES).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
pathLookup.getBaseDirPaths(LIB).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
Set<Path> writeAccessForbidden = new HashSet<>();
|
||||
pathLookup.getBaseDirPaths(CONFIG).forEach(p -> writeAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
for (var pluginPolicy : pluginPolicies.entrySet()) {
|
||||
for (var scope : pluginPolicy.getValue().scopes()) {
|
||||
var filesEntitlement = scope.entitlements()
|
||||
.stream()
|
||||
.filter(x -> x instanceof FilesEntitlement)
|
||||
.map(x -> ((FilesEntitlement) x))
|
||||
.findFirst();
|
||||
if (filesEntitlement.isPresent()) {
|
||||
var fileAccessTree = FileAccessTree.withoutExclusivePaths(filesEntitlement.get(), pathLookup, null);
|
||||
validateReadFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, readAccessForbidden);
|
||||
validateWriteFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, writeAccessForbidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalArgumentException buildValidationException(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
Path forbiddenPath,
|
||||
FilesEntitlement.Mode mode
|
||||
) {
|
||||
return new IllegalArgumentException(
|
||||
Strings.format(
|
||||
"policy for module [%s] in [%s] has an invalid file entitlement. Any path under [%s] is forbidden for mode [%s].",
|
||||
moduleName,
|
||||
componentName,
|
||||
forbiddenPath,
|
||||
mode
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static void validateReadFilesEntitlements(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
FileAccessTree fileAccessTree,
|
||||
Set<Path> readForbiddenPaths
|
||||
) {
|
||||
|
||||
for (Path forbiddenPath : readForbiddenPaths) {
|
||||
if (fileAccessTree.canRead(forbiddenPath)) {
|
||||
throw buildValidationException(componentName, moduleName, forbiddenPath, READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateWriteFilesEntitlements(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
FileAccessTree fileAccessTree,
|
||||
Set<Path> writeForbiddenPaths
|
||||
) {
|
||||
for (Path forbiddenPath : writeForbiddenPaths) {
|
||||
if (fileAccessTree.canWrite(forbiddenPath)) {
|
||||
throw buildValidationException(componentName, moduleName, forbiddenPath, READ_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If bytecode verification is enabled, ensure these classes get loaded before transforming/retransforming them.
|
||||
* For these classes, the order in which we transform and verify them matters. Verification during class transformation is at least an
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.entitlement.initialization;
|
||||
|
||||
import org.elasticsearch.core.Strings;
|
||||
import org.elasticsearch.entitlement.runtime.policy.FileAccessTree;
|
||||
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
|
||||
import org.elasticsearch.entitlement.runtime.policy.Policy;
|
||||
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.CONFIG;
|
||||
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.LIB;
|
||||
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.MODULES;
|
||||
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.PLUGINS;
|
||||
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ;
|
||||
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;
|
||||
|
||||
class FilesEntitlementsValidation {
|
||||
|
||||
static void validate(Map<String, Policy> pluginPolicies, PathLookup pathLookup) {
|
||||
Set<Path> readAccessForbidden = new HashSet<>();
|
||||
pathLookup.getBaseDirPaths(PLUGINS).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
pathLookup.getBaseDirPaths(MODULES).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
pathLookup.getBaseDirPaths(LIB).forEach(p -> readAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
Set<Path> writeAccessForbidden = new HashSet<>();
|
||||
pathLookup.getBaseDirPaths(CONFIG).forEach(p -> writeAccessForbidden.add(p.toAbsolutePath().normalize()));
|
||||
for (var pluginPolicy : pluginPolicies.entrySet()) {
|
||||
for (var scope : pluginPolicy.getValue().scopes()) {
|
||||
var filesEntitlement = scope.entitlements()
|
||||
.stream()
|
||||
.filter(x -> x instanceof FilesEntitlement)
|
||||
.map(x -> ((FilesEntitlement) x))
|
||||
.findFirst();
|
||||
if (filesEntitlement.isPresent()) {
|
||||
var fileAccessTree = FileAccessTree.withoutExclusivePaths(filesEntitlement.get(), pathLookup, null);
|
||||
validateReadFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, readAccessForbidden);
|
||||
validateWriteFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, writeAccessForbidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalArgumentException buildValidationException(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
Path forbiddenPath,
|
||||
FilesEntitlement.Mode mode
|
||||
) {
|
||||
return new IllegalArgumentException(
|
||||
Strings.format(
|
||||
"policy for module [%s] in [%s] has an invalid file entitlement. Any path under [%s] is forbidden for mode [%s].",
|
||||
moduleName,
|
||||
componentName,
|
||||
forbiddenPath,
|
||||
mode
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static void validateReadFilesEntitlements(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
FileAccessTree fileAccessTree,
|
||||
Set<Path> readForbiddenPaths
|
||||
) {
|
||||
|
||||
for (Path forbiddenPath : readForbiddenPaths) {
|
||||
if (fileAccessTree.canRead(forbiddenPath)) {
|
||||
throw buildValidationException(componentName, moduleName, forbiddenPath, READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateWriteFilesEntitlements(
|
||||
String componentName,
|
||||
String moduleName,
|
||||
FileAccessTree fileAccessTree,
|
||||
Set<Path> writeForbiddenPaths
|
||||
) {
|
||||
for (Path forbiddenPath : writeForbiddenPaths) {
|
||||
if (fileAccessTree.canWrite(forbiddenPath)) {
|
||||
throw buildValidationException(componentName, moduleName, forbiddenPath, READ_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ import static org.hamcrest.Matchers.both;
|
|||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
public class EntitlementInitializationTests extends ESTestCase {
|
||||
public class FilesEntitlementsValidationTests extends ESTestCase {
|
||||
|
||||
private static PathLookup TEST_PATH_LOOKUP;
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class EntitlementInitializationTests extends ESTestCase {
|
|||
)
|
||||
)
|
||||
);
|
||||
EntitlementInitialization.validateFilesEntitlements(Map.of("plugin", policy), TEST_PATH_LOOKUP);
|
||||
FilesEntitlementsValidation.validate(Map.of("plugin", policy), TEST_PATH_LOOKUP);
|
||||
}
|
||||
|
||||
public void testValidationFailForRead() {
|
||||
|
@ -94,7 +94,7 @@ public class EntitlementInitializationTests extends ESTestCase {
|
|||
|
||||
var ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> EntitlementInitialization.validateFilesEntitlements(Map.of("plugin", policy), TEST_PATH_LOOKUP)
|
||||
() -> FilesEntitlementsValidation.validate(Map.of("plugin", policy), TEST_PATH_LOOKUP)
|
||||
);
|
||||
assertThat(
|
||||
ex.getMessage(),
|
||||
|
@ -119,7 +119,7 @@ public class EntitlementInitializationTests extends ESTestCase {
|
|||
|
||||
ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> EntitlementInitialization.validateFilesEntitlements(Map.of("plugin2", policy2), TEST_PATH_LOOKUP)
|
||||
() -> FilesEntitlementsValidation.validate(Map.of("plugin2", policy2), TEST_PATH_LOOKUP)
|
||||
);
|
||||
assertThat(
|
||||
ex.getMessage(),
|
||||
|
@ -145,7 +145,7 @@ public class EntitlementInitializationTests extends ESTestCase {
|
|||
|
||||
var ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> EntitlementInitialization.validateFilesEntitlements(Map.of("plugin", policy), TEST_PATH_LOOKUP)
|
||||
() -> FilesEntitlementsValidation.validate(Map.of("plugin", policy), TEST_PATH_LOOKUP)
|
||||
);
|
||||
assertThat(
|
||||
ex.getMessage(),
|
Loading…
Add table
Add a link
Reference in a new issue