Skip to content

Commit 7ec8fcc

Browse files
authored
Refactor before entitlements for testing (#129099)
* Support multiple plugin source paths * Refactor: remove unncessary PathLookup method. It's only called in one place, and there's no need to override it for testing. Removing it just makes things simpler. * Refactor: local var for pathLookup * Fix bugs in test build info parsing * Fix representative_class in test * Move BridgeUtilTests. Tests in org.elasticsearch.entitlement.bridge are going to be uniquely hard to test once we patch the bridge into java.base, due to Java's prohibition on split packages. Let's just move this guy to another package. * Upcast (?!) Java23EntitlementChecker to EntitlementChecker * Empty TestPathLookup * Create PolicyManager during bootstrap, allowing us to share initialization * Use empty component path list instead of null * Downcast to the class of the check method. In our unit test, we have a mock checker that doesn't extend EntitlementChecker, so downcasting to that would require us to needlessly rework the unit test. * Fix javadoc typos
1 parent b214fbf commit 7ec8fcc

File tree

22 files changed

+290
-320
lines changed

22 files changed

+290
-320
lines changed

libs/entitlement/asm-provider/src/main/java/org/elasticsearch/entitlement/instrumentation/impl/InstrumenterImpl.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@
3737
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
3838
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
3939
import static org.objectweb.asm.Opcodes.ACC_STATIC;
40+
import static org.objectweb.asm.Opcodes.CHECKCAST;
4041
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
4142
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
4243

43-
public class InstrumenterImpl implements Instrumenter {
44+
public final class InstrumenterImpl implements Instrumenter {
4445
private static final Logger logger = LogManager.getLogger(InstrumenterImpl.class);
4546

4647
private final String getCheckerClassMethodDescriptor;
@@ -271,7 +272,8 @@ public void visitCode() {
271272
}
272273

273274
private void pushEntitlementChecker() {
274-
InstrumenterImpl.this.pushEntitlementChecker(mv);
275+
mv.visitMethodInsn(INVOKESTATIC, handleClass, "instance", getCheckerClassMethodDescriptor, false);
276+
mv.visitTypeInsn(CHECKCAST, checkMethod.className());
275277
}
276278

277279
private void pushCallerClass() {
@@ -319,10 +321,7 @@ private void invokeInstrumentationMethod() {
319321
true
320322
);
321323
}
322-
}
323324

324-
protected void pushEntitlementChecker(MethodVisitor mv) {
325-
mv.visitMethodInsn(INVOKESTATIC, handleClass, "instance", getCheckerClassMethodDescriptor, false);
326325
}
327326

328327
record ClassFileInfo(String fileName, byte[] bytecodes) {}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
import com.sun.tools.attach.AttachNotSupportedException;
1515
import com.sun.tools.attach.VirtualMachine;
1616

17+
import org.elasticsearch.core.Nullable;
1718
import org.elasticsearch.core.PathUtils;
1819
import org.elasticsearch.core.SuppressForbidden;
1920
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
21+
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
2022
import org.elasticsearch.entitlement.runtime.policy.PathLookupImpl;
2123
import org.elasticsearch.entitlement.runtime.policy.Policy;
2224
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
@@ -26,6 +28,7 @@
2628
import java.io.IOException;
2729
import java.nio.file.Files;
2830
import java.nio.file.Path;
31+
import java.util.Collection;
2932
import java.util.Map;
3033
import java.util.Set;
3134
import java.util.function.Function;
@@ -38,20 +41,20 @@ public class EntitlementBootstrap {
3841
* calls to methods protected by entitlements from classes without a valid
3942
* policy will throw {@link org.elasticsearch.entitlement.runtime.api.NotEntitledException}.
4043
*
41-
* @param serverPolicyPatch a policy with additional entitlements to patch the embedded server layer policy
42-
* @param pluginPolicies a map holding policies for plugins (and modules), by plugin (or module) name.
43-
* @param scopeResolver a functor to map a Java Class to the component and module it belongs to.
44-
* @param settingResolver a functor to resolve a setting name pattern for one or more Elasticsearch settings.
45-
* @param dataDirs data directories for Elasticsearch
46-
* @param sharedRepoDirs shared repository directories for Elasticsearch
47-
* @param configDir the config directory for Elasticsearch
48-
* @param libDir the lib directory for Elasticsearch
49-
* @param modulesDir the directory where Elasticsearch modules are
50-
* @param pluginsDir the directory where plugins are installed for Elasticsearch
51-
* @param sourcePaths a map holding the path to each plugin or module jars, by plugin (or module) name.
52-
* @param tempDir the temp directory for Elasticsearch
53-
* @param logsDir the log directory for Elasticsearch
54-
* @param pidFile path to a pid file for Elasticsearch, or {@code null} if one was not specified
44+
* @param serverPolicyPatch additional entitlements to patch the embedded server layer policy
45+
* @param pluginPolicies maps each plugin name to the corresponding {@link Policy}
46+
* @param scopeResolver a functor to map a Java Class to the component and module it belongs to.
47+
* @param settingResolver a functor to resolve a setting name pattern for one or more Elasticsearch settings.
48+
* @param dataDirs data directories for Elasticsearch
49+
* @param sharedRepoDirs shared repository directories for Elasticsearch
50+
* @param configDir the config directory for Elasticsearch
51+
* @param libDir the lib directory for Elasticsearch
52+
* @param modulesDir the directory where Elasticsearch modules are
53+
* @param pluginsDir the directory where plugins are installed for Elasticsearch
54+
* @param pluginSourcePaths maps each plugin name to the location of that plugin's code
55+
* @param tempDir the temp directory for Elasticsearch
56+
* @param logsDir the log directory for Elasticsearch
57+
* @param pidFile path to a pid file for Elasticsearch, or {@code null} if one was not specified
5558
* @param suppressFailureLogPackages packages for which we do not need or want to log Entitlements failures
5659
*/
5760
public static void bootstrap(
@@ -65,35 +68,33 @@ public static void bootstrap(
6568
Path libDir,
6669
Path modulesDir,
6770
Path pluginsDir,
68-
Map<String, Path> sourcePaths,
71+
Map<String, Collection<Path>> pluginSourcePaths,
6972
Path logsDir,
7073
Path tempDir,
71-
Path pidFile,
74+
@Nullable Path pidFile,
7275
Set<Package> suppressFailureLogPackages
7376
) {
7477
logger.debug("Loading entitlement agent");
7578
if (EntitlementInitialization.initializeArgs != null) {
7679
throw new IllegalStateException("initialization data is already set");
7780
}
81+
PathLookupImpl pathLookup = new PathLookupImpl(
82+
getUserHome(),
83+
configDir,
84+
dataDirs,
85+
sharedRepoDirs,
86+
libDir,
87+
modulesDir,
88+
pluginsDir,
89+
logsDir,
90+
tempDir,
91+
pidFile,
92+
settingResolver
93+
);
7894
EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(
79-
serverPolicyPatch,
80-
pluginPolicies,
81-
scopeResolver,
82-
new PathLookupImpl(
83-
getUserHome(),
84-
configDir,
85-
dataDirs,
86-
sharedRepoDirs,
87-
libDir,
88-
modulesDir,
89-
pluginsDir,
90-
logsDir,
91-
tempDir,
92-
pidFile,
93-
settingResolver
94-
),
95-
sourcePaths,
96-
suppressFailureLogPackages
95+
pathLookup,
96+
suppressFailureLogPackages,
97+
createPolicyManager(pluginPolicies, pathLookup, serverPolicyPatch, scopeResolver, pluginSourcePaths)
9798
);
9899
exportInitializationToAgent();
99100
loadAgent(findAgentJar(), EntitlementInitialization.class.getName());
@@ -151,5 +152,24 @@ static String findAgentJar() {
151152
}
152153
}
153154

155+
private static PolicyManager createPolicyManager(
156+
Map<String, Policy> pluginPolicies,
157+
PathLookup pathLookup,
158+
Policy serverPolicyPatch,
159+
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
160+
Map<String, Collection<Path>> pluginSourcePaths
161+
) {
162+
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
163+
164+
return new PolicyManager(
165+
HardcodedEntitlements.serverPolicy(pathLookup.pidFile(), serverPolicyPatch),
166+
HardcodedEntitlements.agentEntitlements(),
167+
pluginPolicies,
168+
scopeResolver,
169+
pluginSourcePaths,
170+
pathLookup
171+
);
172+
}
173+
154174
private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
155175
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.entitlement.initialization;
10+
package org.elasticsearch.entitlement.bootstrap;
1111

1212
import org.elasticsearch.core.Strings;
1313
import org.elasticsearch.entitlement.runtime.policy.FileAccessTree;
@@ -17,6 +17,7 @@
1717

1818
import java.nio.file.Path;
1919
import java.util.HashSet;
20+
import java.util.List;
2021
import java.util.Map;
2122
import java.util.Set;
2223

@@ -44,7 +45,7 @@ static void validate(Map<String, Policy> pluginPolicies, PathLookup pathLookup)
4445
.map(x -> ((FilesEntitlement) x))
4546
.findFirst();
4647
if (filesEntitlement.isPresent()) {
47-
var fileAccessTree = FileAccessTree.withoutExclusivePaths(filesEntitlement.get(), pathLookup, null);
48+
var fileAccessTree = FileAccessTree.withoutExclusivePaths(filesEntitlement.get(), pathLookup, List.of());
4849
validateReadFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, readAccessForbidden);
4950
validateWriteFilesEntitlements(pluginPolicy.getKey(), scope.moduleName(), fileAccessTree, writeAccessForbidden);
5051
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.entitlement.initialization;
10+
package org.elasticsearch.entitlement.bootstrap;
1111

1212
import org.elasticsearch.core.Booleans;
1313
import org.elasticsearch.entitlement.runtime.policy.Policy;

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

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,17 @@
1010
package org.elasticsearch.entitlement.initialization;
1111

1212
import org.elasticsearch.core.Booleans;
13-
import org.elasticsearch.core.Nullable;
1413
import org.elasticsearch.entitlement.bridge.EntitlementChecker;
1514
import org.elasticsearch.entitlement.runtime.policy.ElasticsearchEntitlementChecker;
1615
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
17-
import org.elasticsearch.entitlement.runtime.policy.Policy;
1816
import org.elasticsearch.entitlement.runtime.policy.PolicyChecker;
1917
import org.elasticsearch.entitlement.runtime.policy.PolicyCheckerImpl;
2018
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
2119

2220
import java.lang.instrument.Instrumentation;
2321
import java.lang.reflect.Constructor;
2422
import java.lang.reflect.InvocationTargetException;
25-
import java.nio.file.Path;
26-
import java.util.Map;
2723
import java.util.Set;
28-
import java.util.function.Function;
2924

3025
import static java.util.Objects.requireNonNull;
3126

@@ -69,35 +64,23 @@ public static EntitlementChecker checker() {
6964
*/
7065
public static void initialize(Instrumentation inst) throws Exception {
7166
// the checker _MUST_ be set before _any_ instrumentation is done
72-
checker = initChecker(createPolicyManager());
67+
checker = initChecker(initializeArgs.policyManager());
7368
initInstrumentation(inst);
7469
}
7570

7671
/**
7772
* Arguments to {@link #initialize}. Since that's called in a static context from the agent,
7873
* we have no way to pass arguments directly, so we stuff them in here.
7974
*
80-
* @param serverPolicyPatch
81-
* @param pluginPolicies
82-
* @param scopeResolver
8375
* @param pathLookup
84-
* @param sourcePaths
8576
* @param suppressFailureLogPackages
77+
* @param policyManager
8678
*/
87-
public record InitializeArgs(
88-
@Nullable Policy serverPolicyPatch,
89-
Map<String, Policy> pluginPolicies,
90-
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
91-
PathLookup pathLookup,
92-
Map<String, Path> sourcePaths,
93-
Set<Package> suppressFailureLogPackages
94-
) {
79+
public record InitializeArgs(PathLookup pathLookup, Set<Package> suppressFailureLogPackages, PolicyManager policyManager) {
9580
public InitializeArgs {
96-
requireNonNull(pluginPolicies);
97-
requireNonNull(scopeResolver);
9881
requireNonNull(pathLookup);
99-
requireNonNull(sourcePaths);
10082
requireNonNull(suppressFailureLogPackages);
83+
requireNonNull(policyManager);
10184
}
10285
}
10386

@@ -110,22 +93,6 @@ private static PolicyCheckerImpl createPolicyChecker(PolicyManager policyManager
11093
);
11194
}
11295

113-
private static PolicyManager createPolicyManager() {
114-
Map<String, Policy> pluginPolicies = initializeArgs.pluginPolicies();
115-
PathLookup pathLookup = initializeArgs.pathLookup();
116-
117-
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
118-
119-
return new PolicyManager(
120-
HardcodedEntitlements.serverPolicy(pathLookup.pidFile(), initializeArgs.serverPolicyPatch()),
121-
HardcodedEntitlements.agentEntitlements(),
122-
pluginPolicies,
123-
initializeArgs.scopeResolver(),
124-
initializeArgs.sourcePaths(),
125-
pathLookup
126-
);
127-
}
128-
12996
/**
13097
* If bytecode verification is enabled, ensure these classes get loaded before transforming/retransforming them.
13198
* For these classes, the order in which we transform and verify them matters. Verification during class transformation is at least an

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12-
import org.elasticsearch.core.Nullable;
1312
import org.elasticsearch.core.Strings;
1413
import org.elasticsearch.core.SuppressForbidden;
1514
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
@@ -25,6 +24,7 @@
2524
import java.nio.file.Paths;
2625
import java.util.ArrayList;
2726
import java.util.Arrays;
27+
import java.util.Collection;
2828
import java.util.HashMap;
2929
import java.util.HashSet;
3030
import java.util.List;
@@ -219,7 +219,7 @@ private static String[] buildUpdatedAndSortedExclusivePaths(
219219
FileAccessTree(
220220
FilesEntitlement filesEntitlement,
221221
PathLookup pathLookup,
222-
Path componentPath,
222+
Collection<Path> componentPaths,
223223
String[] sortedExclusivePaths,
224224
FileAccessTreeComparison comparison
225225
) {
@@ -267,9 +267,7 @@ private static String[] buildUpdatedAndSortedExclusivePaths(
267267
pathLookup.getBaseDirPaths(TEMP).forEach(tempPath -> addPathAndMaybeLink.accept(tempPath, READ_WRITE));
268268
// TODO: this grants read access to the config dir for all modules until explicit read entitlements can be added
269269
pathLookup.getBaseDirPaths(CONFIG).forEach(configPath -> addPathAndMaybeLink.accept(configPath, Mode.READ));
270-
if (componentPath != null) {
271-
addPathAndMaybeLink.accept(componentPath, Mode.READ);
272-
}
270+
componentPaths.forEach(p -> addPathAndMaybeLink.accept(p, Mode.READ));
273271

274272
// TODO: watcher uses javax.activation which looks for known mime types configuration, should this be global or explicit in watcher?
275273
Path jdk = Paths.get(System.getProperty("java.home"));
@@ -314,13 +312,13 @@ static FileAccessTree of(
314312
String moduleName,
315313
FilesEntitlement filesEntitlement,
316314
PathLookup pathLookup,
317-
@Nullable Path componentPath,
315+
Collection<Path> componentPaths,
318316
List<ExclusivePath> exclusivePaths
319317
) {
320318
return new FileAccessTree(
321319
filesEntitlement,
322320
pathLookup,
323-
componentPath,
321+
componentPaths,
324322
buildUpdatedAndSortedExclusivePaths(componentName, moduleName, exclusivePaths, DEFAULT_COMPARISON),
325323
DEFAULT_COMPARISON
326324
);
@@ -332,9 +330,9 @@ static FileAccessTree of(
332330
public static FileAccessTree withoutExclusivePaths(
333331
FilesEntitlement filesEntitlement,
334332
PathLookup pathLookup,
335-
@Nullable Path componentPath
333+
Collection<Path> componentPaths
336334
) {
337-
return new FileAccessTree(filesEntitlement, pathLookup, componentPath, new String[0], DEFAULT_COMPARISON);
335+
return new FileAccessTree(filesEntitlement, pathLookup, componentPaths, new String[0], DEFAULT_COMPARISON);
338336
}
339337

340338
public boolean canRead(Path path) {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ enum BaseDir {
3232

3333
Stream<Path> getBaseDirPaths(BaseDir baseDir);
3434

35-
Stream<Path> resolveRelativePaths(BaseDir baseDir, Path relativePath);
36-
35+
/**
36+
* @return all paths obtained by resolving all values of the given setting under all
37+
* paths of the given {@code baseDir}.
38+
*/
3739
Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName);
3840
}

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,6 @@ public Stream<Path> getBaseDirPaths(BaseDir baseDir) {
6666
};
6767
}
6868

69-
@Override
70-
public Stream<Path> resolveRelativePaths(BaseDir baseDir, Path relativePath) {
71-
return getBaseDirPaths(baseDir).map(path -> path.resolve(relativePath));
72-
}
73-
7469
@Override
7570
public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
7671
List<Path> relativePaths = settingResolver.apply(settingName)

0 commit comments

Comments
 (0)