Skip to content

Commit

Permalink
Merge branch 'release/7.2.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
Marco Boeck committed Aug 31, 2016
1 parent 6191885 commit 85d3bee
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 8 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=7.2.1
version=7.2.2
group=com.rapidminer.studio
18 changes: 17 additions & 1 deletion src/main/java/com/rapidminer/Process.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
import com.rapidminer.repository.RepositoryException;
import com.rapidminer.repository.RepositoryLocation;
import com.rapidminer.repository.RepositoryManager;
import com.rapidminer.studio.internal.ProcessFlowFilterRegistry;
import com.rapidminer.tools.AbstractObservable;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.LoggingHandler;
Expand Down Expand Up @@ -713,6 +714,9 @@ public boolean shouldPause() {
/**
* Add a new {@link ProcessFlowFilter} to this process. The filter will be called directly
* before and after each operator. Refer to {@link ProcessFlowFilter} for more information.
* <p>
* If the given filter instance is already registered, it will not be added a second time.
* </p>
*
* @param filter
* the filter instance to add
Expand All @@ -721,7 +725,9 @@ public void addProcessFlowFilter(ProcessFlowFilter filter) {
if (filter == null) {
throw new IllegalArgumentException("filter must not be null!");
}
processFlowFilters.add(filter);
if (!processFlowFilters.contains(filter)) {
processFlowFilters.add(filter);
}
}

/**
Expand Down Expand Up @@ -801,6 +807,10 @@ public void fireProcessFlowAfterOperator(Operator previousOperator, Operator nex
* process instance
*/
public void copyProcessFlowListenersToOtherProcess(Process otherProcess) {
if (otherProcess == null) {
throw new IllegalArgumentException("otherProcess must not be null!");
}

synchronized (processFlowFilters) {
for (ProcessFlowFilter filter : processFlowFilters) {
otherProcess.addProcessFlowFilter(filter);
Expand Down Expand Up @@ -1097,6 +1107,12 @@ public final IOContainer run(final IOContainer input, final int logVerbosity, fi
*/
public final IOContainer run(final IOContainer input, int logVerbosity, final Map<String, String> macroMap,
final boolean storeOutput) throws OperatorException {
// make sure the process flow filter is registered
ProcessFlowFilter filter = ProcessFlowFilterRegistry.INSTANCE.getProcessFlowFilter();
if (filter != null) {
addProcessFlowFilter(filter);
}

// make sure licensing constraints are not violated
// iterate over all operators in the process
for (Operator op : rootOperator.getAllInnerOperators()) {
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/com/rapidminer/operator/ExecutionUnit.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/
package com.rapidminer.operator;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -39,6 +42,7 @@

import com.rapidminer.Process;
import com.rapidminer.operator.execution.UnitExecutionFactory;
import com.rapidminer.operator.execution.UnitExecutor;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.InputPorts;
import com.rapidminer.operator.ports.OutputPort;
Expand Down Expand Up @@ -795,7 +799,25 @@ protected List<String> createProcessTreeList(int indent, String selfPrefix, Stri

/** Executes the inner operators. */
public void execute() throws OperatorException {
UnitExecutionFactory.getInstance().getExecutor(this).execute(this);
UnitExecutor executor = UnitExecutionFactory.getInstance().getExecutor(this);
// check only the callstack of nested operators, otherwise execution units of
// unsigned extensions might not be able to execute trusted operators
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {

@Override
public Void run() throws OperatorException {

executor.execute(ExecutionUnit.this);
return null;
}
});
} catch (PrivilegedActionException e) {
// e.getException() should be an instance of OperatorException,
// as only checked exceptions will be wrapped in a
// PrivilegedActionException.
throw (OperatorException) e.getException();
}
}

/** Frees memory used by inner sinks. */
Expand Down
49 changes: 46 additions & 3 deletions src/main/java/com/rapidminer/security/PluginSandboxPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
*/
public final class PluginSandboxPolicy extends Policy {

/** Internal permission for {@link RuntimePermission}s */
public static final String RAPIDMINER_INTERNAL_PERMISSION = "accessClassInPackage.rapidminer.internal";

/** The key pair algorithm for our signed extensions */
private static final String KEY_ALGORITHM = "RSA";

Expand All @@ -54,9 +57,20 @@ public final class PluginSandboxPolicy extends Policy {
+ "anxWScOfVW6yDxEjgEHJvMiMzZkGNklYC3ULBCkHfIrih5hO83k5FileuUWDNO4BrLrawmjo9AmYksPVOMmd4/DtDpnehpLy0hQtjBJsz61h"
+ "AGVDnPGpvbsW0rjFAjE4fR5+4RwUNo+SsD/44Jc8bui5seVH5vZuTj02XokybGR4BikrqvJZ4rHe4OGowl8uIr9sEN/+0eIJXQIDAQAB";

/**
* the system property which can be set to {@code true} to enforce plugin sandboxing even on
* SNAPSHOT versions
*/
private static final String PROPERTY_SECURITY_ENFORCED = "com.rapidminer.security.enforce";

/** Our public key used to verify the certificates */
private static PublicKey key;

/**
* if {@code true}, plugin sandboxing is enforced even on SNAPSHOT versions
*/
private static volatile Boolean enforced;

static {
try {
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
Expand Down Expand Up @@ -125,7 +139,8 @@ private static boolean isUnsignedPlugin(ProtectionDomain domain) {
return true;
}
// special case for SNAPSHOT version: grant all permissions for all extensions
if (RapidMiner.getVersion().isSnapshot()) {
// unless security is enforced via system property
if (RapidMiner.getVersion().isSnapshot() && !isSecurityEnforced()) {
return false;
}
if (domain.getCodeSource().getCertificates() == null) {
Expand Down Expand Up @@ -200,11 +215,12 @@ private static PermissionCollection createUnsignedPermissions(final PluginClassL
@Override
public Void run() {
String userHome = System.getProperty("user.home");
String tempDir = System.getProperty("java.io.tempdir");
String tmpDir = System.getProperty("java.io.tmpdir");
String pluginKey = loader.getPluginKey();

// delete access to the general temp directory
permissions.add(new FilePermission(tempDir + "/-", "read, write, delete"));
permissions.add(new FilePermission(tmpDir, "read, write"));
permissions.add(new FilePermission(tmpDir + "/-", "read, write, delete"));

// extensions can only delete files in their own subfolder of the
// .RapidMiner/extensions/workspace folder
Expand Down Expand Up @@ -325,4 +341,31 @@ private static void verifyCertificates(Certificate[] certificates) throws Genera
throw lastException;
}
}

/**
* Checks whether the system property {@value #PROPERTY_SECURITY_ENFORCED} is set to
* {@code true}. This property is used to enable the full plugin sandbox security even on
* SNAPSHOT versions.
*
* @return {@code true} if the system property is set to 'true', {@code false} otherwise
*/
private static boolean isSecurityEnforced() {
// no need to synchronize, if this is entered multiple times it's fine
if (enforced == null) {
enforced = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {

@Override
public Boolean run() {
return Boolean.parseBoolean(System.getProperty(PROPERTY_SECURITY_ENFORCED));
}
});

if (enforced) {
LogService.getRoot().log(Level.INFO,
"Plugin sandboxing enforced via '" + PROPERTY_SECURITY_ENFORCED + "' property.");
}
}

return enforced;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/
package com.rapidminer.studio.internal;

import java.security.AccessController;

import com.rapidminer.security.PluginSandboxPolicy;
import com.rapidminer.tools.ParameterService;


Expand All @@ -41,8 +44,14 @@ public enum ParameterServiceRegistry {
*
* @param provider
* the provider to register
* @throws SecurityException
* if caller does not have {@link RuntimePermission} for
* {@code accessClassInPackage.rapidminer.internal}
*/
public void register(ParameterServiceProvider provider) {
if (System.getSecurityManager() != null) {
AccessController.checkPermission(new RuntimePermission(PluginSandboxPolicy.RAPIDMINER_INTERNAL_PERMISSION));
}
if (provider == null) {
throw new IllegalArgumentException("Provider cannot be null");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright (C) 2001-2016 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.studio.internal;

import java.security.AccessController;

import com.rapidminer.operator.execution.ProcessFlowFilter;
import com.rapidminer.security.PluginSandboxPolicy;


/**
* Registry for a {@link ProcessFlowFilter}.
*
* @author Marcel Michel
* @since 7.2.2
*/
public enum ProcessFlowFilterRegistry {

INSTANCE;

private ProcessFlowFilter filter;

/**
* Registers the filter.
*
* Note: Only one registration is allowed. All following request will result in an
* {@link IllegalStateException}.
*
* @param filter
* the filter to register
* @throws SecurityException
* if caller does not have {@link RuntimePermission} for
* {@code accessClassInPackage.rapidminer.internal}
*/
public void register(ProcessFlowFilter filter) {
if (System.getSecurityManager() != null) {
AccessController.checkPermission(new RuntimePermission(PluginSandboxPolicy.RAPIDMINER_INTERNAL_PERMISSION));
}
if (filter == null) {
throw new IllegalArgumentException("Filter cannot be null");
}
if (this.filter != null) {
throw new IllegalStateException("Filter already defined");
}
this.filter = filter;
}

/**
* Getter for the registered {@link ProcessFlowFilter}.
*
* @return The the registered filter or {@code null}
*/
public ProcessFlowFilter getProcessFlowFilter() {
return filter;
}
}
24 changes: 22 additions & 2 deletions src/main/java/com/rapidminer/tools/plugin/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -239,6 +240,13 @@ public ClassLoader run() throws Exception {
return p1.getName().compareTo(p2.getName());
};

/**
* An ordered collection of plugins sorted by the dependency order. IMPORTANT: This collection
* does not respect failures during initialization, so it might contain more plugins than
* {@link #ALL_PLUGINS}.
*/
private static final Collection<Plugin> PLUGIN_INITIALIZATION_ORDER = new ArrayList<>();

/** An ordered set of all plugins sorted lexically based on the plugin name. */
private static final Collection<Plugin> ALL_PLUGINS = new TreeSet<>(PLUGIN_COMPARATOR);

Expand Down Expand Up @@ -1002,6 +1010,9 @@ public static void finalizePluginLoading() {
// then we have one more extension that is initialized, next round might find
// more
found = true;

// remember the initialization order globally
PLUGIN_INITIALIZATION_ORDER.add(plugin);
}
recordLoadingTime(plugin.getExtensionId(), start);
}
Expand Down Expand Up @@ -1088,9 +1099,18 @@ public static void initPluginTests() {

private static void callPluginInitMethods(String methodName, Class<?>[] arguments, Object[] argumentValues,
boolean useOriginalJarClassLoader) {
List<Plugin> plugins = new LinkedList<>(getAllPlugins());
for (Plugin plugin : PLUGIN_INITIALIZATION_ORDER) {
if (!ALL_PLUGINS.contains(plugin)) {
// plugin may be removed in the meantime,
// so skip the initialization
continue;
}
if (!plugin.checkDependencies(plugin, ALL_PLUGINS)) {
getAllPlugins().remove(plugin);
INCOMPATIBLE_PLUGINS.add(plugin);
continue;
}

for (Plugin plugin : plugins) {
long start = System.currentTimeMillis();
if (!plugin.callInitMethod(methodName, arguments, argumentValues, useOriginalJarClassLoader)) {
getAllPlugins().remove(plugin);
Expand Down

0 comments on commit 85d3bee

Please sign in to comment.