mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-28 02:48:38 +00:00
Painless: Add whitelist extensions (#28161)
This commit adds a PainlessExtension which may be plugged in via SPI to add additional classes, methods and members to the painless whitelist on a per context basis. An example plugin adding and using a whitelist is also added.
This commit is contained in:
parent
b82017cbfe
commit
18463e7e9f
modules/lang-painless/src
main
java/org/elasticsearch/painless
plugin-metadata
resources/org/elasticsearch/painless/spi
test/java/org/elasticsearch/painless
plugins/examples/painless-whitelist
build.gradle
src
main
java/org/elasticsearch/example/painlesswhitelist
resources
META-INF/services
org/elasticsearch/example/painlesswhitelist
test/resources/rest-api-spec/test/painless_whitelist
@ -22,6 +22,7 @@ package org.elasticsearch.painless;
|
||||
import org.elasticsearch.bootstrap.BootstrapInfo;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.painless.node.SSource;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.objectweb.asm.util.Printer;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
@ -20,6 +20,7 @@
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.apache.lucene.util.SetOnce;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -46,29 +47,6 @@ public final class Definition {
|
||||
|
||||
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
|
||||
|
||||
public static final String[] DEFINITION_FILES = new String[] {
|
||||
"org.elasticsearch.txt",
|
||||
"java.lang.txt",
|
||||
"java.math.txt",
|
||||
"java.text.txt",
|
||||
"java.time.txt",
|
||||
"java.time.chrono.txt",
|
||||
"java.time.format.txt",
|
||||
"java.time.temporal.txt",
|
||||
"java.time.zone.txt",
|
||||
"java.util.txt",
|
||||
"java.util.function.txt",
|
||||
"java.util.regex.txt",
|
||||
"java.util.stream.txt",
|
||||
"joda.time.txt"
|
||||
};
|
||||
|
||||
/**
|
||||
* Whitelist that is "built in" to Painless and required by all scripts.
|
||||
*/
|
||||
public static final Definition DEFINITION = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, DEFINITION_FILES)));
|
||||
|
||||
/** Some native types as constants: */
|
||||
public final Type voidType;
|
||||
public final Type booleanType;
|
||||
|
@ -22,28 +22,56 @@ package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.painless.spi.PainlessExtension;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.plugins.ExtensiblePlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.plugins.ScriptPlugin;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptEngine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Registers Painless as a plugin.
|
||||
*/
|
||||
public final class PainlessPlugin extends Plugin implements ScriptPlugin, ExtensiblePlugin {
|
||||
|
||||
private final Map<ScriptContext<?>, List<Whitelist>> extendedWhitelists = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
|
||||
return new PainlessScriptEngine(settings, contexts);
|
||||
Map<ScriptContext<?>, List<Whitelist>> contextsWithWhitelists = new HashMap<>();
|
||||
for (ScriptContext<?> context : contexts) {
|
||||
// we might have a context that only uses the base whitelists, so would not have been filled in by reloadSPI
|
||||
List<Whitelist> whitelists = extendedWhitelists.get(context);
|
||||
if (whitelists == null) {
|
||||
whitelists = new ArrayList<>(Whitelist.BASE_WHITELISTS);
|
||||
}
|
||||
contextsWithWhitelists.put(context, whitelists);
|
||||
}
|
||||
return new PainlessScriptEngine(settings, contextsWithWhitelists);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Setting<?>> getSettings() {
|
||||
return Arrays.asList(CompilerSettings.REGEX_ENABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadSPI(ClassLoader loader) {
|
||||
for (PainlessExtension extension : ServiceLoader.load(PainlessExtension.class, loader)) {
|
||||
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : extension.getContextWhitelists().entrySet()) {
|
||||
List<Whitelist> existing = extendedWhitelists.computeIfAbsent(entry.getKey(),
|
||||
c -> new ArrayList<>(Whitelist.BASE_WHITELISTS));
|
||||
existing.addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,12 @@
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.apache.logging.log4j.core.tools.Generate;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.elasticsearch.SpecialPermission;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.painless.Compiler.Loader;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptEngine;
|
||||
@ -45,7 +45,6 @@ import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -82,7 +81,7 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
||||
|
||||
/**
|
||||
* Default compiler settings to be used. Note that {@link CompilerSettings} is mutable but this instance shouldn't be mutated outside
|
||||
* of {@link PainlessScriptEngine#PainlessScriptEngine(Settings, Collection)}.
|
||||
* of {@link PainlessScriptEngine#PainlessScriptEngine(Settings, Map)}.
|
||||
*/
|
||||
private final CompilerSettings defaultCompilerSettings = new CompilerSettings();
|
||||
|
||||
@ -92,23 +91,19 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
||||
* Constructor.
|
||||
* @param settings The settings to initialize the engine with.
|
||||
*/
|
||||
public PainlessScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
|
||||
public PainlessScriptEngine(Settings settings, Map<ScriptContext<?>, List<Whitelist>> contexts) {
|
||||
super(settings);
|
||||
|
||||
defaultCompilerSettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(settings));
|
||||
|
||||
Map<ScriptContext<?>, Compiler> contextsToCompilers = new HashMap<>();
|
||||
|
||||
// Placeholder definition used for all contexts until SPI is fully integrated. Reduces memory foot print
|
||||
// by re-using the same definition since caching isn't implemented at this time.
|
||||
Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
|
||||
for (ScriptContext<?> context : contexts) {
|
||||
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
|
||||
ScriptContext<?> context = entry.getKey();
|
||||
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, definition));
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new Definition(entry.getValue())));
|
||||
} else {
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz, definition));
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz, new Definition(entry.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
|
30
modules/lang-painless/src/main/java/org/elasticsearch/painless/spi/PainlessExtension.java
Normal file
30
modules/lang-painless/src/main/java/org/elasticsearch/painless/spi/PainlessExtension.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.painless.spi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
||||
public interface PainlessExtension {
|
||||
|
||||
Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists();
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -34,6 +34,26 @@ import java.util.Objects;
|
||||
*/
|
||||
public final class Whitelist {
|
||||
|
||||
private static final String[] BASE_WHITELIST_FILES = new String[] {
|
||||
"org.elasticsearch.txt",
|
||||
"java.lang.txt",
|
||||
"java.math.txt",
|
||||
"java.text.txt",
|
||||
"java.time.txt",
|
||||
"java.time.chrono.txt",
|
||||
"java.time.format.txt",
|
||||
"java.time.temporal.txt",
|
||||
"java.time.zone.txt",
|
||||
"java.util.txt",
|
||||
"java.util.function.txt",
|
||||
"java.util.regex.txt",
|
||||
"java.util.stream.txt",
|
||||
"joda.time.txt"
|
||||
};
|
||||
|
||||
public static final List<Whitelist> BASE_WHITELISTS =
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Whitelist.class, BASE_WHITELIST_FILES));
|
||||
|
||||
/**
|
||||
* Struct represents the equivalent of a Java class in Painless complete with super classes,
|
||||
* constructors, methods, and fields. In Painless a class is known as a struct primarily to avoid
|
@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
package org.elasticsearch.painless.spi;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
@ -25,6 +25,8 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -296,8 +298,9 @@ public final class WhitelistLoader {
|
||||
throw new RuntimeException("error in [" + filepath + "] at line [" + number + "]", exception);
|
||||
}
|
||||
}
|
||||
ClassLoader loader = AccessController.doPrivileged((PrivilegedAction<ClassLoader>)resource::getClassLoader);
|
||||
|
||||
return new Whitelist(resource.getClassLoader(), whitelistStructs);
|
||||
return new Whitelist(loader, whitelistStructs);
|
||||
}
|
||||
|
||||
private WhitelistLoader() {}
|
@ -20,4 +20,7 @@
|
||||
grant {
|
||||
// needed to generate runtime classes
|
||||
permission java.lang.RuntimePermission "createClassLoader";
|
||||
|
||||
// needed to find the classloader to load whitelisted classes from
|
||||
permission java.lang.RuntimePermission "getClassLoader";
|
||||
};
|
||||
|
@ -21,16 +21,12 @@ package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.painless.Definition.DEFINITION_FILES;
|
||||
|
||||
public class AnalyzerCasterTests extends ESTestCase {
|
||||
|
||||
private static final Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, DEFINITION_FILES)));
|
||||
private static final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
|
||||
private static void assertCast(Type actual, Type expected, boolean mustBeExplicit) {
|
||||
Location location = new Location("dummy", 0);
|
||||
|
@ -19,13 +19,12 @@
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
@ -37,8 +36,7 @@ import static org.hamcrest.Matchers.startsWith;
|
||||
*/
|
||||
public class BaseClassTests extends ScriptTestCase {
|
||||
|
||||
private final Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
|
||||
public abstract static class Gets {
|
||||
|
||||
|
@ -22,10 +22,10 @@ package org.elasticsearch.painless;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
@ -35,8 +35,7 @@ import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
public class DebugTests extends ScriptTestCase {
|
||||
private final Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
|
||||
public void testExplain() {
|
||||
// Debug.explain can explain an object
|
||||
|
@ -20,11 +20,11 @@
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.objectweb.asm.util.Textifier;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
|
||||
/** quick and dirty tools for debugging */
|
||||
final class Debugger {
|
||||
@ -40,8 +40,7 @@ final class Debugger {
|
||||
PrintWriter outputWriter = new PrintWriter(output);
|
||||
Textifier textifier = new Textifier();
|
||||
try {
|
||||
new Compiler(iface, new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES))))
|
||||
new Compiler(iface, new Definition(Whitelist.BASE_WHITELISTS))
|
||||
.compile("<debugging>", source, settings, textifier);
|
||||
} catch (Exception e) {
|
||||
textifier.print(outputWriter);
|
||||
|
@ -27,11 +27,11 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
public class DefBootstrapTests extends ESTestCase {
|
||||
private final Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
|
||||
/** calls toString() on integers, twice */
|
||||
public void testOneType() throws Throwable {
|
||||
|
@ -19,21 +19,23 @@
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.TemplateScript;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FactoryTests extends ScriptTestCase {
|
||||
|
||||
protected Collection<ScriptContext<?>> scriptContexts() {
|
||||
Collection<ScriptContext<?>> contexts = super.scriptContexts();
|
||||
contexts.add(StatefulFactoryTestScript.CONTEXT);
|
||||
contexts.add(FactoryTestScript.CONTEXT);
|
||||
contexts.add(EmptyTestScript.CONTEXT);
|
||||
contexts.add(TemplateScript.CONTEXT);
|
||||
@Override
|
||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
|
||||
contexts.put(StatefulFactoryTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(FactoryTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(EmptyTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(TemplateScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
@ -22,14 +22,17 @@ package org.elasticsearch.painless;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.shard.IndexShard;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Test that needsScores() is reported correctly depending on whether _score is used
|
||||
@ -40,8 +43,10 @@ public class NeedsScoreTests extends ESSingleNodeTestCase {
|
||||
public void testNeedsScores() {
|
||||
IndexService index = createIndex("test", Settings.EMPTY, "type", "d", "type=double");
|
||||
|
||||
PainlessScriptEngine service = new PainlessScriptEngine(Settings.EMPTY,
|
||||
Arrays.asList(SearchScript.CONTEXT, ExecutableScript.CONTEXT));
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(ExecutableScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
PainlessScriptEngine service = new PainlessScriptEngine(Settings.EMPTY, contexts);
|
||||
|
||||
QueryShardContext shardContext = index.newQueryShardContext(0, null, () -> 0, null);
|
||||
SearchLookup lookup = new SearchLookup(index.mapperService(), shardContext::getForField, null);
|
||||
|
@ -27,7 +27,7 @@ import org.elasticsearch.painless.Definition.Field;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.Definition.Type;
|
||||
import org.elasticsearch.painless.api.Augmentation;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
@ -36,7 +36,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -68,8 +67,7 @@ public class PainlessDocGenerator {
|
||||
Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
|
||||
false, StandardCharsets.UTF_8.name())) {
|
||||
emitGeneratedWarning(indexStream);
|
||||
List<Type> types = new Definition(Collections.singletonList(
|
||||
WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES))).
|
||||
List<Type> types = new Definition(Whitelist.BASE_WHITELISTS).
|
||||
allSimpleTypes().stream().sorted(comparing(t -> t.name)).collect(toList());
|
||||
for (Type type : types) {
|
||||
if (type.clazz.isPrimitive()) {
|
||||
|
@ -24,6 +24,7 @@ import org.apache.lucene.search.Scorer;
|
||||
import org.elasticsearch.common.lucene.ScorerAware;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
@ -31,10 +32,8 @@ import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.painless.node.SSource.MainMethodReserved;
|
||||
@ -63,11 +62,10 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||
/**
|
||||
* Script contexts used to build the script engine. Override to customize which script contexts are available.
|
||||
*/
|
||||
protected Collection<ScriptContext<?>> scriptContexts() {
|
||||
Collection<ScriptContext<?>> contexts = new ArrayList<>();
|
||||
contexts.add(SearchScript.CONTEXT);
|
||||
contexts.add(ExecutableScript.CONTEXT);
|
||||
|
||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(ExecutableScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
@ -92,8 +90,7 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, Scorer scorer, boolean picky) {
|
||||
// test for ambiguity errors before running the actual script if picky is true
|
||||
if (picky) {
|
||||
Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, GenericElasticsearchScript.class);
|
||||
CompilerSettings pickySettings = new CompilerSettings();
|
||||
pickySettings.setPicky(true);
|
||||
|
@ -37,20 +37,25 @@ import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.elasticsearch.index.similarity.ScriptedSimilarity;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SimilarityScript;
|
||||
import org.elasticsearch.script.SimilarityWeightScript;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SimilarityScriptTests extends ScriptTestCase {
|
||||
|
||||
@Override
|
||||
protected Collection<ScriptContext<?>> scriptContexts() {
|
||||
return Arrays.asList(SimilarityScript.CONTEXT, SimilarityWeightScript.CONTEXT);
|
||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SimilarityScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(SimilarityWeightScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public void testBasics() throws IOException {
|
||||
|
@ -33,12 +33,11 @@ import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.Operation;
|
||||
import org.elasticsearch.painless.ScriptClassInfo;
|
||||
import org.elasticsearch.painless.WhitelistLoader;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -50,8 +49,7 @@ import static org.elasticsearch.painless.node.SSource.MainMethodReserved;
|
||||
* Tests {@link Object#toString} implementations on all extensions of {@link ANode}.
|
||||
*/
|
||||
public class NodeToStringTests extends ESTestCase {
|
||||
private final Definition definition = new Definition(
|
||||
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Definition.class, Definition.DEFINITION_FILES)));
|
||||
private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
|
||||
|
||||
public void testEAssignment() {
|
||||
assertToString(
|
||||
|
@ -26,6 +26,10 @@ esplugin {
|
||||
extendedPlugins = ['lang-painless']
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':modules:lang-painless')
|
||||
}
|
||||
|
||||
integTestCluster {
|
||||
distribution = 'zip'
|
||||
}
|
||||
|
42
plugins/examples/painless-whitelist/src/main/java/org/elasticsearch/example/painlesswhitelist/ExampleWhitelistExtension.java
Normal file
42
plugins/examples/painless-whitelist/src/main/java/org/elasticsearch/example/painlesswhitelist/ExampleWhitelistExtension.java
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.painless.spi.PainlessExtension;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.painless.spi.WhitelistLoader;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
/** An extension of painless which adds a whitelist. */
|
||||
public class ExampleWhitelistExtension implements PainlessExtension {
|
||||
|
||||
private static final Whitelist WHITELIST =
|
||||
WhitelistLoader.loadFromResourceFiles(ExampleWhitelistExtension.class, "example_whitelist.txt");
|
||||
|
||||
@Override
|
||||
public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
|
||||
return Collections.singletonMap(SearchScript.CONTEXT, Collections.singletonList(WHITELIST));
|
||||
}
|
||||
}
|
57
plugins/examples/painless-whitelist/src/main/java/org/elasticsearch/example/painlesswhitelist/ExampleWhitelistedClass.java
Normal file
57
plugins/examples/painless-whitelist/src/main/java/org/elasticsearch/example/painlesswhitelist/ExampleWhitelistedClass.java
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An example of a class to be whitelisted for use by painless scripts
|
||||
*
|
||||
* Each of the members and methods below are whitelisted for use in search scripts.
|
||||
* See <a href="file:example_whitelist.txt">example_whitelist.txt</a>.
|
||||
*/
|
||||
public class ExampleWhitelistedClass {
|
||||
|
||||
public static final int CONSTANT = 42;
|
||||
|
||||
public int publicMember;
|
||||
|
||||
private int privateMember;
|
||||
|
||||
public ExampleWhitelistedClass(int publicMember, int privateMember) {
|
||||
this.publicMember = publicMember;
|
||||
this.privateMember = privateMember;
|
||||
}
|
||||
|
||||
public int getPrivateMemberAccessor() {
|
||||
return this.privateMember;
|
||||
}
|
||||
|
||||
public void setPrivateMemberAccessor(int privateMember) {
|
||||
this.privateMember = privateMember;
|
||||
}
|
||||
|
||||
public static void staticMethod() {
|
||||
// electricity
|
||||
}
|
||||
|
||||
// example augmentation method
|
||||
public static int toInt(String x) {
|
||||
return Integer.parseInt(x);
|
||||
}
|
||||
}
|
@ -22,4 +22,5 @@ package org.elasticsearch.example.painlesswhitelist;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
|
||||
public class MyWhitelistPlugin extends Plugin {
|
||||
// we don't actually need anything here, since whitelists are extended through SPI
|
||||
}
|
||||
|
1
plugins/examples/painless-whitelist/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension
Normal file
1
plugins/examples/painless-whitelist/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension
Normal file
@ -0,0 +1 @@
|
||||
org.elasticsearch.example.painlesswhitelist.ExampleWhitelistExtension
|
42
plugins/examples/painless-whitelist/src/main/resources/org/elasticsearch/example/painlesswhitelist/example_whitelist.txt
Normal file
42
plugins/examples/painless-whitelist/src/main/resources/org/elasticsearch/example/painlesswhitelist/example_whitelist.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# This file contains a whitelist for an example class which may be access from painless
|
||||
|
||||
class org.elasticsearch.example.painlesswhitelist.ExampleWhitelistedClass {
|
||||
# constructor
|
||||
(int, int)
|
||||
|
||||
# static constants and methods look the same as instance members and methods
|
||||
int CONSTANT
|
||||
void staticMethod()
|
||||
|
||||
# members lack parenthesis that methods have
|
||||
int publicMember
|
||||
|
||||
# getter and setter for private member
|
||||
int getPrivateMemberAccessor()
|
||||
void setPrivateMemberAccessor(int)
|
||||
}
|
||||
|
||||
class java.lang.String {
|
||||
# existing classes can be "augmented" to have additional methods, which take the object
|
||||
# to operate on as the first argument to a static method
|
||||
int org.elasticsearch.example.painlesswhitelist.ExampleWhitelistedClass toInt()
|
||||
}
|
26
plugins/examples/painless-whitelist/src/test/resources/rest-api-spec/test/painless_whitelist/20_whitelist.yml
Normal file
26
plugins/examples/painless-whitelist/src/test/resources/rest-api-spec/test/painless_whitelist/20_whitelist.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# Example test using whitelisted members and methods
|
||||
|
||||
"Whitelisted custom class":
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: test
|
||||
id: 1
|
||||
body: { "num1": 1.0 }
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
index: test
|
||||
search:
|
||||
body:
|
||||
query:
|
||||
match_all: {}
|
||||
script_fields:
|
||||
sNum1:
|
||||
script:
|
||||
source: "def e = new ExampleWhitelistedClass(6, 42); ExampleWhitelistedClass.staticMethod(); return e.publicMember + e.privateMemberAccessor + ExampleWhitelistedClass.CONSTANT + '2'.toInt()"
|
||||
lang: painless
|
||||
|
||||
- match: { hits.total: 1 }
|
||||
- match: { hits.hits.0.fields.sNum1.0: 92 }
|
Loading…
x
Reference in New Issue
Block a user