JS policy should use ScriptingSPI

This commit is contained in:
Pedro Igor 2017-06-16 11:49:32 -03:00
parent 93105a2182
commit 0b5e6b0d49
3 changed files with 36 additions and 24 deletions

View file

@ -17,37 +17,27 @@
*/
package org.keycloak.authorization.policy.provider.js;
import java.util.function.Supplier;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.ScriptModel;
import org.keycloak.scripting.InvocableScriptAdapter;
import org.keycloak.scripting.ScriptingProvider;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class JSPolicyProvider implements PolicyProvider {
private Supplier<ScriptEngine> engineProvider;
public JSPolicyProvider(Supplier<ScriptEngine> engineProvider) {
this.engineProvider = engineProvider;
}
@Override
public void evaluate(Evaluation evaluation) {
ScriptEngine engine = engineProvider.get();
engine.put("$evaluation", evaluation);
Policy policy = evaluation.getPolicy();
try {
engine.eval(policy.getConfig().get("code"));
} catch (ScriptException e) {
getInvocableScriptAdapter(policy, evaluation).eval();
} catch (Exception e) {
throw new RuntimeException("Error evaluating JS Policy [" + policy.getName() + "].", e);
}
}
@ -56,4 +46,24 @@ public class JSPolicyProvider implements PolicyProvider {
public void close() {
}
private InvocableScriptAdapter getInvocableScriptAdapter(Policy policy, Evaluation evaluation) {
String scriptName = policy.getName();
String scriptCode = policy.getConfig().get("code");
String scriptDescription = policy.getDescription();
AuthorizationProvider authorization = evaluation.getAuthorizationProvider();
RealmModel realm = authorization.getRealm();
ScriptingProvider scripting = authorization.getKeycloakSession().getProvider(ScriptingProvider.class);
//TODO lookup script by scriptId instead of creating it every time
ScriptModel script = scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, scriptName, scriptCode, scriptDescription);
//how to deal with long running scripts -> timeout?
return scripting.prepareInvocableScript(script, bindings -> {
bindings.put("script", script);
bindings.put("$evaluation", evaluation);
});
}
}

View file

@ -1,9 +1,5 @@
package org.keycloak.authorization.policy.provider.js;
import java.util.Map;
import javax.script.ScriptEngineManager;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@ -19,9 +15,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
*/
public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRepresentation> {
private static final String ENGINE = "nashorn";
private JSPolicyProvider provider = new JSPolicyProvider(() -> new ScriptEngineManager().getEngineByName(ENGINE));
private JSPolicyProvider provider = new JSPolicyProvider();
@Override
public String getName() {

View file

@ -78,6 +78,14 @@ public class InvocableScriptAdapter implements Invocable {
}
}
public Object eval() throws ScriptExecutionException {
try {
return scriptEngine.eval(scriptModel.getCode());
} catch (ScriptException e) {
throw new ScriptExecutionException(scriptModel, e);
}
}
@Override
public <T> T getInterface(Class<T> clazz) {
return getInvocableEngine().getInterface(clazz);