Skip to content

Commit

Permalink
Merge pull request keycloak#3189 from thomasdarimont/issue/KEYCLOAK-3…
Browse files Browse the repository at this point in the history
…491-revise-scripting-support

KEYCLOAK-3491 Revise Scripting Support
  • Loading branch information
stianst authored Sep 29, 2016
2 parents eed71a4 + 8e11338 commit 5d34b7e
Show file tree
Hide file tree
Showing 18 changed files with 693 additions and 158 deletions.
23 changes: 22 additions & 1 deletion server-spi/src/main/java/org/keycloak/models/ScriptModel.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.models;

/**
* Denotes an executable Script with metadata.
* A representation of a Script with some additional meta-data.
*
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
public interface ScriptModel {

/**
* MIME-Type for JavaScript
*/
String TEXT_JAVASCRIPT = "text/javascript";

/**
* Returns the unique id of the script. {@literal null} for ad-hoc created scripts.
*/
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.scripting;

import org.keycloak.models.ScriptModel;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

/**
* Wraps a {@link ScriptModel} and makes it {@link Invocable}.
*
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
public class InvocableScriptAdapter implements Invocable {

/**
* Holds the {@ScriptModel}
*/
private final ScriptModel scriptModel;

/**
* Holds the {@link ScriptEngine} instance initialized with the script code.
*/
private final ScriptEngine scriptEngine;

/**
* Creates a new {@link InvocableScriptAdapter} instance.
*
* @param scriptModel must not be {@literal null}
* @param scriptEngine must not be {@literal null}
*/
public InvocableScriptAdapter(ScriptModel scriptModel, ScriptEngine scriptEngine) {

if (scriptModel == null) {
throw new IllegalArgumentException("scriptModel must not be null");
}

if (scriptEngine == null) {
throw new IllegalArgumentException("scriptEngine must not be null");
}

this.scriptModel = scriptModel;
this.scriptEngine = loadScriptIntoEngine(scriptModel, scriptEngine);
}

@Override
public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptExecutionException {

try {
return getInvocableEngine().invokeMethod(thiz, name, args);
} catch (ScriptException | NoSuchMethodException e) {
throw new ScriptExecutionException(scriptModel, e);
}
}

@Override
public Object invokeFunction(String name, Object... args) throws ScriptExecutionException {
try {
return getInvocableEngine().invokeFunction(name, args);
} catch (ScriptException | NoSuchMethodException e) {
throw new ScriptExecutionException(scriptModel, e);
}
}

@Override
public <T> T getInterface(Class<T> clazz) {
return getInvocableEngine().getInterface(clazz);
}

@Override
public <T> T getInterface(Object thiz, Class<T> clazz) {
return getInvocableEngine().getInterface(thiz, clazz);
}

/**
* Returns {@literal true} if the {@link ScriptEngine} has a definition with the given {@code name}.
*
* @param name
* @return
*/
public boolean isDefined(String name) {

Object candidate = scriptEngine.getContext().getAttribute(name);

return candidate != null;
}

private ScriptEngine loadScriptIntoEngine(ScriptModel script, ScriptEngine engine) {

try {
engine.eval(script.getCode());
} catch (ScriptException se) {
throw new ScriptExecutionException(script, se);
}

return engine;
}

private Invocable getInvocableEngine() {
return (Invocable) scriptEngine;
}
}
18 changes: 18 additions & 0 deletions server-spi/src/main/java/org/keycloak/scripting/Script.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.scripting;

import org.keycloak.models.ScriptModel;

/**
* A {@link ScriptModel} which holds some meta-data.
*
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
public class Script implements ScriptModel {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.scripting;

import javax.script.Bindings;

/**
* Callback interface for customization of {@link Bindings} for a {@link javax.script.ScriptEngine}.
*
* <p>Used by {@link ScriptingProvider}</p>
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
@FunctionalInterface
public interface ScriptBindingsConfigurer {

/**
* A default {@link ScriptBindingsConfigurer} leaves the Bindings empty.
* A default {@link ScriptBindingsConfigurer} that provides no Bindings.
*/
ScriptBindingsConfigurer EMPTY = new ScriptBindingsConfigurer() {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.scripting;

import org.keycloak.models.ScriptModel;
Expand All @@ -11,7 +27,7 @@
*/
public class ScriptExecutionException extends RuntimeException {

public ScriptExecutionException(ScriptModel script, ScriptException se) {
super("Error executing script '" + script.getName() + "'", se);
public ScriptExecutionException(ScriptModel script, Exception ex) {
super("Could not execute script '" + script.getName() + "' problem was: " + ex.getMessage(), ex);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.scripting;

import org.keycloak.models.ScriptModel;
Expand All @@ -13,20 +29,23 @@
public interface ScriptingProvider extends Provider {

/**
* Returns an {@link InvocableScript} based on the given {@link ScriptModel}.
* <p>The {@code InvocableScript} wraps a dedicated {@link ScriptEngine} that was populated with the provided {@link ScriptBindingsConfigurer}</p>
* Returns an {@link InvocableScriptAdapter} based on the given {@link ScriptModel}.
* <p>The {@code InvocableScriptAdapter} wraps a dedicated {@link ScriptEngine} that was populated with the provided {@link ScriptBindingsConfigurer}</p>
*
* @param script the script to wrap
* @param scriptModel the scriptModel to wrap
* @param bindingsConfigurer populates the {@link javax.script.Bindings}
* @return
*/
InvocableScript prepareScript(ScriptModel script, ScriptBindingsConfigurer bindingsConfigurer);
InvocableScriptAdapter prepareInvocableScript(ScriptModel scriptModel, ScriptBindingsConfigurer bindingsConfigurer);

/**
* Returns an {@link InvocableScript} based on the given {@link ScriptModel} with an {@link ScriptBindingsConfigurer#EMPTY} {@code ScriptBindingsConfigurer}.
* @see #prepareScript(ScriptModel, ScriptBindingsConfigurer)
* @param script
* Creates a new {@link ScriptModel} instance.
*
* @param realmId
* @param scriptName
* @param scriptCode
* @param scriptDescription
* @return
*/
InvocableScript prepareScript(ScriptModel script);
ScriptModel createScript(String realmId, String mimeType, String scriptName, String scriptCode, String scriptDescription);
}
Loading

0 comments on commit 5d34b7e

Please sign in to comment.