mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 09:28:55 -04:00
Add java version variants of entitlements checker (#116878)
As each version of Java is released, there may be additional methods we want to instrument for entitlements. Since new methods won't exist in the base version of Java that Elasticsearch is compiled with, we need to hava different classes and compilation for each version. This commit adds a scaffolding for adding the classes for new versions of Java. Unfortunately it requires several classes in different locations. But hopefully these are infrequent enough that the boilerplate is ok. We could consider adding a helper Gradle task to templatize the new classes in the future if it is too cumbersome. Note that the example for Java23 does not have anything meaningful in it yet, it's only meant as an example until we find go through classes and methods that were added after Java 21.
This commit is contained in:
parent
f6ac6e1c3b
commit
b45564364b
10 changed files with 185 additions and 44 deletions
|
@ -21,6 +21,7 @@ import org.gradle.api.plugins.JavaPlugin;
|
|||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.compile.CompileOptions;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||
|
@ -87,6 +88,7 @@ public class MrjarPlugin implements Plugin<Project> {
|
|||
String mainSourceSetName = SourceSet.MAIN_SOURCE_SET_NAME + javaVersion;
|
||||
SourceSet mainSourceSet = addSourceSet(project, javaExtension, mainSourceSetName, mainSourceSets, javaVersion);
|
||||
configureSourceSetInJar(project, mainSourceSet, javaVersion);
|
||||
addJar(project, mainSourceSet, javaVersion);
|
||||
mainSourceSets.add(mainSourceSetName);
|
||||
testSourceSets.add(mainSourceSetName);
|
||||
|
||||
|
@ -147,6 +149,14 @@ public class MrjarPlugin implements Plugin<Project> {
|
|||
return sourceSet;
|
||||
}
|
||||
|
||||
private void addJar(Project project, SourceSet sourceSet, int javaVersion) {
|
||||
project.getConfigurations().register("java" + javaVersion);
|
||||
TaskProvider<Jar> jarTask = project.getTasks().register("java" + javaVersion + "Jar", Jar.class, task -> {
|
||||
task.from(sourceSet.getOutput());
|
||||
});
|
||||
project.getArtifacts().add("java" + javaVersion, jarTask);
|
||||
}
|
||||
|
||||
private void configurePreviewFeatures(Project project, SourceSet sourceSet, int javaVersion) {
|
||||
project.getTasks().withType(JavaCompile.class).named(sourceSet.getCompileJavaTaskName()).configure(compileTask -> {
|
||||
CompileOptions compileOptions = compileTask.getOptions();
|
||||
|
|
|
@ -36,6 +36,22 @@ import static org.objectweb.asm.Opcodes.INVOKESTATIC;
|
|||
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
||||
|
||||
public class InstrumenterImpl implements Instrumenter {
|
||||
|
||||
private static final String checkerClassDescriptor;
|
||||
private static final String handleClass;
|
||||
static {
|
||||
int javaVersion = Runtime.version().feature();
|
||||
final String classNamePrefix;
|
||||
if (javaVersion >= 23) {
|
||||
classNamePrefix = "Java23";
|
||||
} else {
|
||||
classNamePrefix = "";
|
||||
}
|
||||
String checkerClass = "org/elasticsearch/entitlement/bridge/" + classNamePrefix + "EntitlementChecker";
|
||||
handleClass = checkerClass + "Handle";
|
||||
checkerClassDescriptor = Type.getObjectType(checkerClass).getDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* To avoid class name collisions during testing without an agent to replace classes in-place.
|
||||
*/
|
||||
|
@ -269,13 +285,7 @@ public class InstrumenterImpl implements Instrumenter {
|
|||
}
|
||||
|
||||
protected void pushEntitlementChecker(MethodVisitor mv) {
|
||||
mv.visitMethodInsn(
|
||||
INVOKESTATIC,
|
||||
"org/elasticsearch/entitlement/bridge/EntitlementCheckerHandle",
|
||||
"instance",
|
||||
"()Lorg/elasticsearch/entitlement/bridge/EntitlementChecker;",
|
||||
false
|
||||
);
|
||||
mv.visitMethodInsn(INVOKESTATIC, handleClass, "instance", "()" + checkerClassDescriptor, false);
|
||||
}
|
||||
|
||||
public record ClassFileInfo(String fileName, byte[] bytecodes) {}
|
||||
|
|
|
@ -7,19 +7,18 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import org.elasticsearch.gradle.internal.precommit.CheckForbiddenApisTask
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
apply plugin: 'elasticsearch.mrjar'
|
||||
|
||||
configurations {
|
||||
bridgeJar {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
tasks.named('jar').configure {
|
||||
// guarding for intellij
|
||||
if (sourceSets.findByName("main23")) {
|
||||
from sourceSets.main23.output
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
bridgeJar(jar)
|
||||
}
|
||||
|
||||
tasks.named('forbiddenApisMain').configure {
|
||||
tasks.withType(CheckForbiddenApisTask).configureEach {
|
||||
replaceSignatureFiles 'jdk-signatures'
|
||||
}
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
|
||||
package org.elasticsearch.entitlement.bridge;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Makes the {@link EntitlementChecker} available to injected bytecode.
|
||||
*/
|
||||
|
@ -35,27 +32,7 @@ public class EntitlementCheckerHandle {
|
|||
* The {@code EntitlementInitialization} class is what actually instantiates it and makes it available;
|
||||
* here, we copy it into a static final variable for maximum performance.
|
||||
*/
|
||||
private static final EntitlementChecker instance;
|
||||
static {
|
||||
String initClazz = "org.elasticsearch.entitlement.initialization.EntitlementInitialization";
|
||||
final Class<?> clazz;
|
||||
try {
|
||||
clazz = ClassLoader.getSystemClassLoader().loadClass(initClazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError("java.base cannot find entitlement initialziation", e);
|
||||
}
|
||||
final Method checkerMethod;
|
||||
try {
|
||||
checkerMethod = clazz.getMethod("checker");
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError("EntitlementInitialization is missing checker() method", e);
|
||||
}
|
||||
try {
|
||||
instance = (EntitlementChecker) checkerMethod.invoke(null);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
private static final EntitlementChecker instance = HandleLoader.load(EntitlementChecker.class);
|
||||
}
|
||||
|
||||
// no construction
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.bridge;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
class HandleLoader {
|
||||
|
||||
static <T extends EntitlementChecker> T load(Class<T> checkerClass) {
|
||||
String initClassName = "org.elasticsearch.entitlement.initialization.EntitlementInitialization";
|
||||
final Class<?> initClazz;
|
||||
try {
|
||||
initClazz = ClassLoader.getSystemClassLoader().loadClass(initClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError("java.base cannot find entitlement initialization", e);
|
||||
}
|
||||
final Method checkerMethod;
|
||||
try {
|
||||
checkerMethod = initClazz.getMethod("checker");
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError("EntitlementInitialization is missing checker() method", e);
|
||||
}
|
||||
try {
|
||||
return checkerClass.cast(checkerMethod.invoke(null));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// no instance
|
||||
private HandleLoader() {}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.bridge;
|
||||
|
||||
public interface Java23EntitlementChecker extends EntitlementChecker {}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.bridge;
|
||||
|
||||
/**
|
||||
* Java23 variant of {@link EntitlementChecker} handle holder.
|
||||
*/
|
||||
public class Java23EntitlementCheckerHandle {
|
||||
|
||||
public static Java23EntitlementChecker instance() {
|
||||
return Holder.instance;
|
||||
}
|
||||
|
||||
private static class Holder {
|
||||
private static final Java23EntitlementChecker instance = HandleLoader.load(Java23EntitlementChecker.class);
|
||||
}
|
||||
|
||||
// no construction
|
||||
private Java23EntitlementCheckerHandle() {}
|
||||
}
|
|
@ -6,10 +6,13 @@
|
|||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import org.elasticsearch.gradle.internal.precommit.CheckForbiddenApisTask
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
apply plugin: 'elasticsearch.publish'
|
||||
|
||||
apply plugin: 'elasticsearch.embedded-providers'
|
||||
apply plugin: 'elasticsearch.mrjar'
|
||||
|
||||
embeddedProviders {
|
||||
impl 'entitlement', project(':libs:entitlement:asm-provider')
|
||||
|
@ -23,8 +26,13 @@ dependencies {
|
|||
testImplementation(project(":test:framework")) {
|
||||
exclude group: 'org.elasticsearch', module: 'entitlement'
|
||||
}
|
||||
|
||||
// guarding for intellij
|
||||
if (sourceSets.findByName("main23")) {
|
||||
main23CompileOnly project(path: ':libs:entitlement:bridge', configuration: 'java23')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('forbiddenApisMain').configure {
|
||||
tasks.withType(CheckForbiddenApisTask).configureEach {
|
||||
replaceSignatureFiles 'jdk-signatures'
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.io.IOException;
|
|||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
@ -59,7 +61,7 @@ public class EntitlementInitialization {
|
|||
|
||||
// Note: referenced by agent reflectively
|
||||
public static void initialize(Instrumentation inst) throws Exception {
|
||||
manager = new ElasticsearchEntitlementChecker(createPolicyManager());
|
||||
manager = initChecker();
|
||||
|
||||
Map<MethodKey, CheckerMethod> methodMap = INSTRUMENTER_FACTORY.lookupMethodsToInstrument(
|
||||
"org.elasticsearch.entitlement.bridge.EntitlementChecker"
|
||||
|
@ -137,6 +139,36 @@ public class EntitlementInitialization {
|
|||
return Set.of(ALL_UNNAMED);
|
||||
}
|
||||
|
||||
private static ElasticsearchEntitlementChecker initChecker() throws IOException {
|
||||
final PolicyManager policyManager = createPolicyManager();
|
||||
|
||||
int javaVersion = Runtime.version().feature();
|
||||
final String classNamePrefix;
|
||||
if (javaVersion >= 23) {
|
||||
classNamePrefix = "Java23";
|
||||
} else {
|
||||
classNamePrefix = "";
|
||||
}
|
||||
final String className = "org.elasticsearch.entitlement.runtime.api." + classNamePrefix + "ElasticsearchEntitlementChecker";
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError("entitlement lib cannot find entitlement impl", e);
|
||||
}
|
||||
Constructor<?> constructor;
|
||||
try {
|
||||
constructor = clazz.getConstructor(PolicyManager.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError("entitlement impl is missing no arg constructor", e);
|
||||
}
|
||||
try {
|
||||
return (ElasticsearchEntitlementChecker) constructor.newInstance(policyManager);
|
||||
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String internalName(Class<?> c) {
|
||||
return c.getName().replace('.', '/');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.runtime.api;
|
||||
|
||||
import org.elasticsearch.entitlement.bridge.Java23EntitlementChecker;
|
||||
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
|
||||
|
||||
public class Java23ElasticsearchEntitlementChecker extends ElasticsearchEntitlementChecker implements Java23EntitlementChecker {
|
||||
|
||||
public Java23ElasticsearchEntitlementChecker(PolicyManager policyManager) {
|
||||
super(policyManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check$java_lang_System$exit(Class<?> callerClass, int status) {
|
||||
// TODO: this is just an example, we shouldn't really override a method implemented in the superclass
|
||||
super.check$java_lang_System$exit(callerClass, status);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue