#java #string #grails #groovy #dsl
#java #строка #grails #groovy #dsl
Вопрос:
У меня есть Java-приложение с API. API позволяет использовать гибкий синтаксис даты:
PUT /monthly-jobs/job1
{
"startExpression": "now 1.week 4.days 2.hours"
}
Для этой задачи я хочу использовать класс Groovy TimeCategory (http://groovycookbook.org/basic_types/dates_times /).
На мой взгляд, для этого должен быть вспомогательный класс Java в соответствии с интерфейсом:
interface DateExpressionEvaluator {
Date evaluateDateExpression(String expr);
}
Каков правильный подход к этому? Как использовать Groovy в таких требованиях?
Пожалуйста, обратите внимание, что я не могу просто оценить ввод как groovy code, так как пользователь API может вставить groovy code и взломать сервер.
Комментарии:
1. Возможно, было бы проще использовать библиотеку, подобную prettytime-nlp , которая может анализировать такие вещи, как «через три дня»
Ответ №1:
Мне пришлось создать решение, используя groovy script sandboxing (https://github.com/kohsuke/groovy-sandbox ):
Тестовый пример:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.Date;
import org.apache.commons.lang3.time.FastDateFormat;
import org.junit.Test;
public class DateExpressionUtilsTest {
@Test
public void test_parse() {
assertEquals(
FastDateFormat.getInstance("yyyy-MM-dd hh:mm").format(
new Date(System.currentTimeMillis() 7 * 24 * 60 * 60 * 1000)),
FastDateFormat.getInstance("yyyy-MM-dd hh:mm").format(DateExpressionUtils.eval("now 1.week")));
try {
DateExpressionUtils.eval("now 1.week; Thread.sleep(1000);");
fail();
} catch (SecurityException e) {
// ok
}
}
}
DateExpressionUtils:
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import java.util.Date;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.kohsuke.groovy.sandbox.SandboxTransformer;
/**
* Sandboxing: https://github.com/kohsuke/groovy-sandbox
* http://groovy-sandbox.kohsuke.org/
*
*/
public class DateExpressionUtils {
public static Date eval(String expr) {
try {
CompilerConfiguration cc = new CompilerConfiguration();
cc.addCompilationCustomizers(new SandboxTransformer());
Binding binding = new Binding();
binding.setProperty("now", new Date());
GroovyShell sh = new GroovyShell(binding, cc);
DateExpressionSandbox sandbox = new DateExpressionSandbox();
sandbox.register();
try {
Object resObj = sh.evaluate("use(groovy.time.TimeCategory){" expr "}");
Date res = (Date) resObj;
return res;
} finally {
sandbox.unregister();
}
} catch (SecurityException e) {
throw new SecurityException(String.format("Possible date expression sandbox jailbreak with '%s': '%s'.",
expr, e.getMessage()));
} catch (Exception e) {
throw new RuntimeException(String.format("Unable to evaluate date expression '%s': '%s'.", expr,
e.getMessage()));
}
}
}
DateExpressionSandbox:
import groovy.lang.Closure;
import groovy.lang.Script;
import groovy.time.Duration;
import groovy.time.TimeCategory;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.kohsuke.groovy.sandbox.GroovyValueFilter;
/**
* Example sandbox: https://github.com/kohsuke/groovy-sandbox/blob/master/src/test/groovy/org/
* kohsuke/groovy/sandbox/robot/RobotSandbox.groovy
*
*/
public class DateExpressionSandbox extends GroovyValueFilter {
@Override
public Object filter(Object o) {
if (o == null || ALLOWED_TYPES.contains(o.getClass()))
return o;
if (Class.class.equals(o.getClass()) amp;amp; ALLOWED_STATIC_CLASSES.contains(o.toString())) {
return o;
}
if (o instanceof Script || o instanceof Closure)
return o; // access to properties of compiled groovy script
throw new SecurityException(String.format("Unexpected type: '%s', '%s'.", o.getClass(), o));
}
private static final Set<Class<?>> ALLOWED_TYPES = new HashSet<Class<?>>(Arrays.asList(String.class, Integer.class,
Long.class, Double.class, Boolean.class, Date.class, TimeCategory.class, Duration.class));
private static final Set<String> ALLOWED_STATIC_CLASSES = new HashSet<>();
static {
for (Class<?> cl : ALLOWED_TYPES) {
ALLOWED_STATIC_CLASSES.add("class " cl.getCanonicalName());
}
}
}