Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APIM] Add Support to Config Java Access Control through JS in Script Mediator #2133

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,36 @@
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.mediators.Value;
import org.apache.synapse.mediators.bsf.access.control.AccessControlUtils;
import org.apache.synapse.mediators.bsf.access.control.SandboxContextFactory;
import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig;
import org.apache.synapse.mediators.bsf.access.control.config.AccessControlListType;
import org.apache.synapse.mediators.eip.EIPUtils;
import org.mozilla.javascript.ClassShutter;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;

import javax.activation.DataHandler;
import javax.script.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.CLASS_PREFIXES;
import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.ENABLE;
import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.LIMIT_CLASS_ACCESS_PREFIX;
import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.LIMIT_NATIVE_OBJECT_ACCESS_PREFIX;
import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.LIST_TYPE;
import static org.apache.synapse.mediators.bsf.access.control.AccessControlConstants.OBJECT_NAMES;

/**
* A Synapse mediator that calls a function in any scripting language supported by the BSF.
* The ScriptMediator supports scripts specified in-line or those loaded through a registry
Expand Down Expand Up @@ -175,6 +190,16 @@ public class ScriptMediator extends AbstractMediator {
*/
private ScriptEngineFactory oracleNashornFactory;

/**
* Store java class access control config
*/
private AccessControlConfig classAccessControlConfig;

/**
* Store java method access config
*/
private AccessControlConfig nativeObjectAccessControlConfig;

/**
* Create a script mediator for the given language and given script source.
*
Expand Down Expand Up @@ -275,6 +300,9 @@ private boolean invokeScript(MessageContext synCtx) {
//if the engine is Rhino then needs to set the class loader specifically
if (language.equals("js")) {
Context cx = Context.enter();
if (classAccessControlConfig != null && classAccessControlConfig.isAccessControlEnabled()) {
cx.setClassShutter(createClassShutter());
}
cx.setApplicationClassLoader(this.loader);

}
Expand Down Expand Up @@ -646,6 +674,12 @@ protected void initScriptEngine() {
this.multiThreadedEngine = scriptEngine.getFactory().getParameter("THREADING") != null;
log.debug("Script mediator for language : " + language +
" supports multithreading? : " + multiThreadedEngine);

readAccessControlConfigurations(MiscellaneousUtil.loadProperties("synapse.properties"));
if (nativeObjectAccessControlConfig != null && nativeObjectAccessControlConfig.isAccessControlEnabled() &&
!ContextFactory.hasExplicitGlobal()) {
ContextFactory.initGlobal(new SandboxContextFactory(nativeObjectAccessControlConfig));
}
}

public String getLanguage() {
Expand Down Expand Up @@ -717,4 +751,60 @@ private ScriptEngineFactory getOracleNashornFactory() {
return null;
}

/**
* Creates a class shutter, which will be used inside the context that executes the script.
* This class shutter will be used to control the visibility of classes specified in the access control config,
* to the script.
* @return
*/
private ClassShutter createClassShutter() {
return new ClassShutter() {
public boolean visibleToScripts(String className) {
/*
This will be used to compare whether the current fully qualified class name starts with
any of the provided set of strings provided in the access control config.
*/
Comparator<String> startsWithComparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1 == null || o2 == null) {
return -1;
}
if (o1.startsWith(o2)) {
return 1;
}
return -1;
}
};
return AccessControlUtils.isAccessAllowed(className, classAccessControlConfig, startsWithComparator);
}
};
}

/**
* Reads and sets access control configurations.
* @param properties Synapse properties.
*/
private void readAccessControlConfigurations(Properties properties) {
String limitClassAccessEnabled = properties.getProperty(LIMIT_CLASS_ACCESS_PREFIX + ENABLE);
if (Boolean.parseBoolean(limitClassAccessEnabled)) {
String limitClassAccessListType = properties.getProperty(LIMIT_CLASS_ACCESS_PREFIX + LIST_TYPE);
String limitClassAccessClassPrefixes = properties.getProperty(LIMIT_CLASS_ACCESS_PREFIX + CLASS_PREFIXES);
this.classAccessControlConfig = new AccessControlConfig(true,
AccessControlListType.valueOf(limitClassAccessListType),
Arrays.asList(limitClassAccessClassPrefixes.split(",")));
}

String limitNativeObjectAccessEnabled = properties.getProperty(LIMIT_NATIVE_OBJECT_ACCESS_PREFIX + ENABLE);
if (Boolean.parseBoolean(limitNativeObjectAccessEnabled)) {
String limitNativeObjectAccessListType =
properties.getProperty(LIMIT_NATIVE_OBJECT_ACCESS_PREFIX + LIST_TYPE);
String limitNativeObjectAccessClassPrefixes =
properties.getProperty(LIMIT_NATIVE_OBJECT_ACCESS_PREFIX + OBJECT_NAMES);
this.nativeObjectAccessControlConfig = new AccessControlConfig(true,
AccessControlListType.valueOf(limitNativeObjectAccessListType),
Arrays.asList(limitNativeObjectAccessClassPrefixes.split(",")));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.apache.synapse.mediators.bsf.access.control;

/**
* Constants related to Script Mediator access control.
*/
public class AccessControlConstants {
public static String LIMIT_CLASS_ACCESS_PREFIX = "limit_java_class_access_in_scripts.";
public static String LIMIT_NATIVE_OBJECT_ACCESS_PREFIX = "limit_java_native_object_access_in_scripts.";
public static String ENABLE = "enable";
public static String LIST_TYPE = "list_type";
public static String CLASS_PREFIXES = "class_prefixes";
public static String OBJECT_NAMES = "object_names";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.apache.synapse.mediators.bsf.access.control;

import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig;
import org.apache.synapse.mediators.bsf.access.control.config.AccessControlListType;

import java.util.Comparator;
import java.util.List;

/**
* Utility methods related to Script Mediator access control.
*/
public class AccessControlUtils {

/**
* Returns whether the provided string which represents a Java class or native object is accessible or not.
* The allowing/blocking will be determined by the provided AccessControlConfig, based on the matching/comparing
* done as specified in the comparator.
* @param string Java class name or native object name.
* @param accessControlConfig Access control config of the Script Mediator.
* @param comparator The comparator based on which, the provided Java class/native object name is
* matched against the provided access control config.
* @return Whether the access is allowed or not.
*/
public static boolean isAccessAllowed(String string, AccessControlConfig accessControlConfig,
Comparator<String> comparator) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look into the indentations in here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation seems to be ok on IntelliJ

if (accessControlConfig == null || !accessControlConfig.isAccessControlEnabled()) {
return true; // Access control is not applicable
}

List<String> accessControlList = accessControlConfig.getAccessControlList();
boolean doesMatchExist = false;
for (String item : accessControlList) {
if (comparator.compare(string, item) > -1) {
doesMatchExist = true;
break;
}
}

if (accessControlConfig.getAccessControlListType() == AccessControlListType.BLOCK_LIST) {
return !doesMatchExist;
}
if (accessControlConfig.getAccessControlListType() == AccessControlListType.ALLOW_LIST) {
return doesMatchExist;
}
return true; // Ideally we won't reach here
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.apache.synapse.mediators.bsf.access.control;

import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;

/**
* Represents the sandbox context factory - which is used with access control of the Script Mediator.
*/
public class SandboxContextFactory extends ContextFactory {
private AccessControlConfig nativeObjectAccessControlConfig;

public SandboxContextFactory(AccessControlConfig nativeObjectAccessControlConfig) {
this.nativeObjectAccessControlConfig = nativeObjectAccessControlConfig;
}

@Override
protected Context makeContext() {
Context cx = super.makeContext();
cx.setWrapFactory(new SandboxWrapFactory(nativeObjectAccessControlConfig));
return cx;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.apache.synapse.mediators.bsf.access.control;

import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Scriptable;

import java.util.Comparator;

/**
* Provides native Java objects to the sandbox, after necessary access control filtering.
*/
public class SandboxNativeJavaObject extends NativeJavaObject {
private AccessControlConfig nativeObjectAccessControlConfig;

public SandboxNativeJavaObject(Scriptable scope, Object javaObject, Class staticType,
AccessControlConfig nativeObjectAccessControlConfig) {
super(scope, javaObject, staticType);
this.nativeObjectAccessControlConfig = nativeObjectAccessControlConfig;
}

@Override
public Object get(String name, Scriptable start) {
Comparator<String> equalsComparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1 != null && o1.equals(o2)) {
return 0;
}
return -1;
}
};
if (AccessControlUtils.isAccessAllowed(name, nativeObjectAccessControlConfig, equalsComparator)) {
return super.get(name, start);
}
return NOT_FOUND;
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.apache.synapse.mediators.bsf.access.control;

import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrapFactory;

/**
* Wraps sandbox native Java objects that are used in Script Mediator access control.
*/
public class SandboxWrapFactory extends WrapFactory {
private AccessControlConfig nativeObjectAccessControlConfig;

public SandboxWrapFactory(AccessControlConfig nativeObjectAccessControlConfig) {
this.nativeObjectAccessControlConfig = nativeObjectAccessControlConfig;
}

@Override
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class staticType) {
return new SandboxNativeJavaObject(scope, javaObject, staticType, nativeObjectAccessControlConfig);
}
}


Loading
Loading