/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.scripting.util;
import java.io.Reader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collection;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
/**
* This is a decorator class for any other {@link ScriptEngine} implementation
* that runs any of the eval methods with the defined set of {@link Permission}s.
* <p>
* For the permissions to have any effect, a SecurityManager has to be installed
* in the current VM.
* <p>
* This class is provided in hopes that it can help provide security to script engines
* that do not directly implement some kind of security measures.
*
* @author Lukas Krejci
*/
public class SandboxedScriptEngine implements ScriptEngine {
private ScriptEngine engine;
private AccessControlContext accessControlContext;
public SandboxedScriptEngine(ScriptEngine engine) {
this.engine = engine;
}
public SandboxedScriptEngine(ScriptEngine engine, PermissionCollection permissions) {
this(engine);
setPermissions(permissions);
}
public SandboxedScriptEngine(ScriptEngine engine, Collection<? extends Permission> permissions) {
this(engine);
setPermissions(permissions);
}
public void setPermissions(Permission... permissions) {
setPermissions(Arrays.asList(permissions));
}
public void setPermissions(Collection<? extends Permission> permissions) {
Permissions ps = new Permissions();
for(Permission p : permissions) {
ps.add(p);
}
setPermissions(ps);
}
public void setPermissions(PermissionCollection permissions) {
CodeSource cs = new CodeSource(null, (Certificate[]) null);
ProtectionDomain domain = new ProtectionDomain(cs, permissions);
accessControlContext = new AccessControlContext(new ProtectionDomain[] { domain });
}
@Override
public Object eval(final String script, final ScriptContext context) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(script, context);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(reader, context);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public Object eval(final String script) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(script);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public Object eval(final Reader reader) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(reader);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public Object eval(final String script, final Bindings n) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(script, n);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public Object eval(final Reader reader, final Bindings n) throws ScriptException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return engine.eval(reader, n);
}
}, accessControlContext);
} catch (PrivilegedActionException e) {
throw transfer(e);
}
}
@Override
public void put(String key, Object value) {
engine.put(key, value);
}
@Override
public Object get(String key) {
return engine.get(key);
}
@Override
public Bindings getBindings(int scope) {
return engine.getBindings(scope);
}
@Override
public void setBindings(Bindings bindings, int scope) {
engine.setBindings(bindings, scope);
}
@Override
public Bindings createBindings() {
return engine.createBindings();
}
@Override
public ScriptContext getContext() {
return engine.getContext();
}
@Override
public void setContext(ScriptContext context) {
engine.setContext(context);
}
@Override
public ScriptEngineFactory getFactory() {
return engine.getFactory();
}
private static ScriptException transfer(PrivilegedActionException e) {
if (e.getCause() instanceof ScriptException) {
return (ScriptException) e.getCause();
} else {
return new ScriptException(e);
}
}
}