[Painless] Add a Map for java names to classes for use in the custom classloader (#34424)
This fixes a bug that wasn't including the class used for a static import where only the static import is whitelisted.
This commit is contained in:
parent
100a18bd8d
commit
a7c4dbbb0c
|
@ -96,7 +96,7 @@ final class Compiler {
|
|||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
found = painlessLookup.canonicalTypeNameToType(name.replace('$', '.'));
|
||||
found = painlessLookup.javaClassNameToClass(name);
|
||||
|
||||
return found != null ? found : super.findClass(name);
|
||||
}
|
||||
|
|
|
@ -34,22 +34,28 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToBoxe
|
|||
|
||||
public final class PainlessLookup {
|
||||
|
||||
private final Map<String, Class<?>> javaClassNamesToClasses;
|
||||
private final Map<String, Class<?>> canonicalClassNamesToClasses;
|
||||
private final Map<Class<?>, PainlessClass> classesToPainlessClasses;
|
||||
|
||||
private final Map<String, PainlessMethod> painlessMethodKeysToImportedPainlessMethods;
|
||||
private final Map<String, PainlessClassBinding> painlessMethodKeysToPainlessClassBindings;
|
||||
|
||||
PainlessLookup(Map<String, Class<?>> canonicalClassNamesToClasses, Map<Class<?>, PainlessClass> classesToPainlessClasses,
|
||||
PainlessLookup(
|
||||
Map<String, Class<?>> javaClassNamesToClasses,
|
||||
Map<String, Class<?>> canonicalClassNamesToClasses,
|
||||
Map<Class<?>, PainlessClass> classesToPainlessClasses,
|
||||
Map<String, PainlessMethod> painlessMethodKeysToImportedPainlessMethods,
|
||||
Map<String, PainlessClassBinding> painlessMethodKeysToPainlessClassBindings) {
|
||||
|
||||
Objects.requireNonNull(javaClassNamesToClasses);
|
||||
Objects.requireNonNull(canonicalClassNamesToClasses);
|
||||
Objects.requireNonNull(classesToPainlessClasses);
|
||||
|
||||
Objects.requireNonNull(painlessMethodKeysToImportedPainlessMethods);
|
||||
Objects.requireNonNull(painlessMethodKeysToPainlessClassBindings);
|
||||
|
||||
this.javaClassNamesToClasses = javaClassNamesToClasses;
|
||||
this.canonicalClassNamesToClasses = Collections.unmodifiableMap(canonicalClassNamesToClasses);
|
||||
this.classesToPainlessClasses = Collections.unmodifiableMap(classesToPainlessClasses);
|
||||
|
||||
|
@ -57,6 +63,10 @@ public final class PainlessLookup {
|
|||
this.painlessMethodKeysToPainlessClassBindings = Collections.unmodifiableMap(painlessMethodKeysToPainlessClassBindings);
|
||||
}
|
||||
|
||||
public Class<?> javaClassNameToClass(String javaClassName) {
|
||||
return javaClassNamesToClasses.get(javaClassName);
|
||||
}
|
||||
|
||||
public boolean isValidCanonicalClassName(String canonicalClassName) {
|
||||
Objects.requireNonNull(canonicalClassName);
|
||||
|
||||
|
|
|
@ -120,6 +120,15 @@ public final class PainlessLookupBuilder {
|
|||
return painlessLookupBuilder.build();
|
||||
}
|
||||
|
||||
// javaClassNamesToClasses is all the classes that need to be available to the custom classloader
|
||||
// including classes used as part of imported methods and class bindings but not necessarily whitelisted
|
||||
// individually. The values of javaClassNamesToClasses are a superset of the values of
|
||||
// canonicalClassNamesToClasses.
|
||||
private final Map<String, Class<?>> javaClassNamesToClasses;
|
||||
// canonicalClassNamesToClasses is all the whitelisted classes available in a Painless script including
|
||||
// classes with imported canonical names but does not include classes from imported methods or class
|
||||
// bindings unless also whitelisted separately. The values of canonicalClassNamesToClasses are a subset
|
||||
// of the values of javaClassNamesToClasses.
|
||||
private final Map<String, Class<?>> canonicalClassNamesToClasses;
|
||||
private final Map<Class<?>, PainlessClassBuilder> classesToPainlessClassBuilders;
|
||||
|
||||
|
@ -127,6 +136,7 @@ public final class PainlessLookupBuilder {
|
|||
private final Map<String, PainlessClassBinding> painlessMethodKeysToPainlessClassBindings;
|
||||
|
||||
public PainlessLookupBuilder() {
|
||||
javaClassNamesToClasses = new HashMap<>();
|
||||
canonicalClassNamesToClasses = new HashMap<>();
|
||||
classesToPainlessClassBuilders = new HashMap<>();
|
||||
|
||||
|
@ -189,7 +199,16 @@ public final class PainlessLookupBuilder {
|
|||
throw new IllegalArgumentException("invalid class name [" + canonicalClassName + "]");
|
||||
}
|
||||
|
||||
Class<?> existingClass = canonicalClassNamesToClasses.get(canonicalClassName);
|
||||
Class<?> existingClass = javaClassNamesToClasses.get(clazz.getName());
|
||||
|
||||
if (existingClass == null) {
|
||||
javaClassNamesToClasses.put(clazz.getName(), clazz);
|
||||
} else if (existingClass != clazz) {
|
||||
throw new IllegalArgumentException("class [" + canonicalClassName + "] " +
|
||||
"cannot represent multiple java classes with the same name from different class loaders");
|
||||
}
|
||||
|
||||
existingClass = canonicalClassNamesToClasses.get(canonicalClassName);
|
||||
|
||||
if (existingClass != null && existingClass != clazz) {
|
||||
throw new IllegalArgumentException("class [" + canonicalClassName + "] " +
|
||||
|
@ -685,6 +704,14 @@ public final class PainlessLookupBuilder {
|
|||
}
|
||||
|
||||
String targetCanonicalClassName = typeToCanonicalTypeName(targetClass);
|
||||
Class<?> existingTargetClass = javaClassNamesToClasses.get(targetClass.getName());
|
||||
|
||||
if (existingTargetClass == null) {
|
||||
javaClassNamesToClasses.put(targetClass.getName(), targetClass);
|
||||
} else if (existingTargetClass != targetClass) {
|
||||
throw new IllegalArgumentException("class [" + targetCanonicalClassName + "] " +
|
||||
"cannot represent multiple java classes with the same name from different class loaders");
|
||||
}
|
||||
|
||||
if (METHOD_NAME_PATTERN.matcher(methodName).matches() == false) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -818,6 +845,14 @@ public final class PainlessLookupBuilder {
|
|||
}
|
||||
|
||||
String targetCanonicalClassName = typeToCanonicalTypeName(targetClass);
|
||||
Class<?> existingTargetClass = javaClassNamesToClasses.get(targetClass.getName());
|
||||
|
||||
if (existingTargetClass == null) {
|
||||
javaClassNamesToClasses.put(targetClass.getName(), targetClass);
|
||||
} else if (existingTargetClass != targetClass) {
|
||||
throw new IllegalArgumentException("class [" + targetCanonicalClassName + "] " +
|
||||
"cannot represent multiple java classes with the same name from different class loaders");
|
||||
}
|
||||
|
||||
Constructor<?>[] javaConstructors = targetClass.getConstructors();
|
||||
Constructor<?> javaConstructor = null;
|
||||
|
@ -952,7 +987,23 @@ public final class PainlessLookupBuilder {
|
|||
classesToPainlessClasses.put(painlessClassBuilderEntry.getKey(), painlessClassBuilderEntry.getValue().build());
|
||||
}
|
||||
|
||||
return new PainlessLookup(canonicalClassNamesToClasses, classesToPainlessClasses,
|
||||
if (javaClassNamesToClasses.values().containsAll(canonicalClassNamesToClasses.values()) == false) {
|
||||
throw new IllegalArgumentException("the values of java class names to classes " +
|
||||
"must be a superset of the values of canonical class names to classes");
|
||||
}
|
||||
|
||||
if (javaClassNamesToClasses.values().containsAll(classesToPainlessClasses.keySet()) == false) {
|
||||
throw new IllegalArgumentException("the values of java class names to classes " +
|
||||
"must be a superset of the keys of classes to painless classes");
|
||||
}
|
||||
|
||||
if (canonicalClassNamesToClasses.values().containsAll(classesToPainlessClasses.keySet()) == false ||
|
||||
classesToPainlessClasses.keySet().containsAll(canonicalClassNamesToClasses.values()) == false) {
|
||||
throw new IllegalArgumentException("the values of canonical class names to classes " +
|
||||
"must have the same classes as the keys of classes to painless classes");
|
||||
}
|
||||
|
||||
return new PainlessLookup(javaClassNamesToClasses, canonicalClassNamesToClasses, classesToPainlessClasses,
|
||||
painlessMethodKeysToImportedPainlessMethods, painlessMethodKeysToPainlessClassBindings);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example.painlesswhitelist;
|
||||
|
||||
public class ExampleStaticMethodClass {
|
||||
public static int exampleAddInts(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
}
|
|
@ -40,3 +40,7 @@ class java.lang.String {
|
|||
# to operate on as the first argument to a static method
|
||||
int org.elasticsearch.example.painlesswhitelist.ExampleWhitelistedClass toInt()
|
||||
}
|
||||
|
||||
static_import {
|
||||
int exampleAddInts(int, int) from_class org.elasticsearch.example.painlesswhitelist.ExampleStaticMethodClass
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
# Example test using whitelisted statically imported method
|
||||
|
||||
"custom static imported method":
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: test
|
||||
id: 1
|
||||
body: { "num1": 1 }
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
index: test
|
||||
search:
|
||||
body:
|
||||
query:
|
||||
match_all: {}
|
||||
script_fields:
|
||||
sNum1:
|
||||
script:
|
||||
source: "exampleAddInts(2, (int)doc['num1'][0])"
|
||||
lang: painless
|
||||
|
||||
- match: { hits.total: 1 }
|
||||
- match: { hits.hits.0.fields.sNum1.0: 3 }
|
Loading…
Reference in New Issue