--- /dev/null
+package org.pf4j;\r
+\r
+import java.nio.file.Path;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.stream.Collectors;\r
+\r
+/**\r
+ * Use this class to wrap the original plugin manager to prevent full access from within plugins.\r
+ * Override AbstractPluginManager.createPluginWrapper to use this class\r
+ * @author Wolfram Haussig\r
+ *\r
+ */\r
+public class SecurePluginManagerWrapper implements PluginManager {\r
+\r
+ private static final String PLUGIN_PREFIX = "Plugin ";\r
+ /**\r
+ * the current plugin\r
+ */\r
+ private String currentPluginId;\r
+ /**\r
+ * the original plugin manager\r
+ */\r
+ private PluginManager original;\r
+\r
+ /**\r
+ * The registered {@link PluginStateListener}s.\r
+ */\r
+ protected List<PluginStateListener> pluginStateListeners = new ArrayList<>();\r
+ /**\r
+ * wrapper for pluginStateListeners\r
+ */\r
+ private PluginStateListenerWrapper listenerWrapper = new PluginStateListenerWrapper();\r
+\r
+ /**\r
+ * constructor\r
+ * @param original the original plugin manager\r
+ * @param currentPlugin the current pluginId\r
+ */\r
+ public SecurePluginManagerWrapper(PluginManager original, String currentPluginId) {\r
+ this.original = original;\r
+ this.currentPluginId = currentPluginId;\r
+ }\r
+\r
+ @Override\r
+ public boolean isDevelopment() {\r
+ return original.isDevelopment();\r
+ }\r
+\r
+ @Override\r
+ public boolean isNotDevelopment() {\r
+ return original.isNotDevelopment();\r
+ }\r
+\r
+ @Override\r
+ public List<PluginWrapper> getPlugins() {\r
+ return Arrays.asList(getPlugin(currentPluginId));\r
+ }\r
+\r
+ @Override\r
+ public List<PluginWrapper> getPlugins(PluginState pluginState) {\r
+ return getPlugins().stream().filter(p -> p.getPluginState() == pluginState).collect(Collectors.toList());\r
+ }\r
+\r
+ @Override\r
+ public List<PluginWrapper> getResolvedPlugins() {\r
+ return getPlugins().stream().filter(p -> p.getPluginState().ordinal() >= PluginState.RESOLVED.ordinal()).collect(Collectors.toList());\r
+ }\r
+\r
+ @Override\r
+ public List<PluginWrapper> getUnresolvedPlugins() {\r
+ return Collections.emptyList();\r
+ }\r
+\r
+ @Override\r
+ public List<PluginWrapper> getStartedPlugins() {\r
+ return getPlugins(PluginState.STARTED);\r
+ }\r
+\r
+ @Override\r
+ public PluginWrapper getPlugin(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getPlugin(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPlugin for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void loadPlugins() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugins!");\r
+ }\r
+\r
+ @Override\r
+ public String loadPlugin(Path pluginPath) {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugin!");\r
+ }\r
+\r
+ @Override\r
+ public void startPlugins() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugins!");\r
+ }\r
+\r
+ @Override\r
+ public PluginState startPlugin(String pluginId) {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugin!");\r
+ }\r
+\r
+ @Override\r
+ public void stopPlugins() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugins!");\r
+ }\r
+\r
+ @Override\r
+ public PluginState stopPlugin(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.stopPlugin(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugin for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void unloadPlugins() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugins!");\r
+ }\r
+\r
+ @Override\r
+ public boolean unloadPlugin(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.unloadPlugin(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugin for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public boolean disablePlugin(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.disablePlugin(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute disablePlugin for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public boolean enablePlugin(String pluginId) {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute enablePlugin!");\r
+ }\r
+\r
+ @Override\r
+ public boolean deletePlugin(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.deletePlugin(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute deletePlugin for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public ClassLoader getPluginClassLoader(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getPluginClassLoader(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginClassLoader for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public List<Class<?>> getExtensionClasses(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getExtensionClasses(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type) {\r
+ return getExtensionClasses(type, currentPluginId);\r
+ }\r
+\r
+ @Override\r
+ public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type, String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getExtensionClasses(type, pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> List<T> getExtensions(Class<T> type) {\r
+ return getExtensions(type, currentPluginId);\r
+ }\r
+\r
+ @Override\r
+ public <T> List<T> getExtensions(Class<T> type, String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getExtensions(type, pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public List<?> getExtensions(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getExtensions(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Set<String> getExtensionClassNames(String pluginId) {\r
+ if (currentPluginId.equals(pluginId)) {\r
+ return original.getExtensionClassNames(pluginId);\r
+ } else {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClassNames for foreign pluginId!");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public ExtensionFactory getExtensionFactory() {\r
+ return original.getExtensionFactory();\r
+ }\r
+\r
+ @Override\r
+ public RuntimeMode getRuntimeMode() {\r
+ return original.getRuntimeMode();\r
+ }\r
+\r
+ @Override\r
+ public PluginWrapper whichPlugin(Class<?> clazz) {\r
+ ClassLoader classLoader = clazz.getClassLoader();\r
+ PluginWrapper plugin = getPlugin(currentPluginId);\r
+ if (plugin.getPluginClassLoader() == classLoader) {\r
+ return plugin;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public void addPluginStateListener(PluginStateListener listener) {\r
+ if (pluginStateListeners.isEmpty()) {\r
+ this.original.addPluginStateListener(listenerWrapper);\r
+ }\r
+ pluginStateListeners.add(listener);\r
+ }\r
+\r
+ @Override\r
+ public void removePluginStateListener(PluginStateListener listener) {\r
+ pluginStateListeners.remove(listener);\r
+ if (pluginStateListeners.isEmpty()) {\r
+ this.original.removePluginStateListener(listenerWrapper);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setSystemVersion(String version) {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute setSystemVersion!");\r
+ }\r
+\r
+ @Override\r
+ public String getSystemVersion() {\r
+ return original.getSystemVersion();\r
+ }\r
+\r
+ @Override\r
+ public Path getPluginsRoot() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoot!");\r
+ }\r
+\r
+ @Override\r
+ public List<Path> getPluginsRoots() {\r
+ throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoots!");\r
+ }\r
+\r
+ @Override\r
+ public VersionManager getVersionManager() {\r
+ return original.getVersionManager();\r
+ }\r
+\r
+ /**\r
+ * Wrapper for PluginStateListener events. will only propagate events if they match the current pluginId\r
+ * @author Wolfram Haussig\r
+ *\r
+ */\r
+ private class PluginStateListenerWrapper implements PluginStateListener {\r
+\r
+ @Override\r
+ public void pluginStateChanged(PluginStateEvent event) {\r
+ if (event.getPlugin().getPluginId().equals(currentPluginId)) {\r
+ for (PluginStateListener listener : pluginStateListeners) {\r
+ listener.pluginStateChanged(event);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+package org.pf4j;\r
+\r
+import static org.junit.jupiter.api.Assertions.assertTrue;\r
+import static org.junit.Assert.assertNotNull;\r
+import static org.junit.jupiter.api.Assertions.assertEquals;\r
+import static org.junit.jupiter.api.Assertions.assertThrows;\r
+\r
+import java.io.IOException;\r
+import java.nio.file.Path;\r
+import java.util.List;\r
+\r
+import org.junit.jupiter.api.AfterEach;\r
+import org.junit.jupiter.api.BeforeEach;\r
+import org.junit.jupiter.api.Test;\r
+import org.junit.jupiter.api.io.TempDir;\r
+import org.pf4j.test.PluginJar;\r
+import org.pf4j.test.TestExtension;\r
+import org.pf4j.test.TestExtensionPoint;\r
+import org.pf4j.test.TestPlugin;\r
+\r
+public class SecurePluginManagerWrapperTest {\r
+\r
+ private static final String OTHER_PLUGIN_ID = "test-plugin-2";\r
+ private static final String THIS_PLUGIN_ID = "test-plugin-1";\r
+ private PluginJar thisPlugin;\r
+ private PluginJar otherPlugin;\r
+ private PluginManager pluginManager;\r
+ private PluginManager wrappedPluginManager;\r
+ private int pluginManagerEvents = 0;\r
+ private int wrappedPluginManagerEvents = 0;\r
+\r
+ @TempDir\r
+ Path pluginsPath;\r
+\r
+ @BeforeEach\r
+ public void setUp() throws IOException {\r
+ pluginManagerEvents = 0;\r
+ wrappedPluginManagerEvents = 0;\r
+ thisPlugin = new PluginJar.Builder(pluginsPath.resolve("test-plugin1.jar"), THIS_PLUGIN_ID).pluginClass(TestPlugin.class.getName()).pluginVersion("1.2.3").extension(TestExtension.class.getName()).build();\r
+ otherPlugin = new PluginJar.Builder(pluginsPath.resolve("test-plugin2.jar"), OTHER_PLUGIN_ID).pluginClass(TestPlugin.class.getName()).pluginVersion("1.2.3").extension(TestExtension.class.getName()).build();\r
+\r
+ pluginManager = new JarPluginManager(pluginsPath);\r
+ wrappedPluginManager = new SecurePluginManagerWrapper(pluginManager, THIS_PLUGIN_ID);\r
+ }\r
+\r
+ @AfterEach\r
+ public void tearDown() {\r
+ pluginManager.unloadPlugins();\r
+\r
+ thisPlugin = null;\r
+ otherPlugin = null;\r
+ pluginManager = null;\r
+ }\r
+\r
+ @Test\r
+ public void pluginStateListeners() {\r
+ pluginManager.addPluginStateListener(new PluginStateListener() {\r
+ @Override\r
+ public void pluginStateChanged(PluginStateEvent event) {\r
+ pluginManagerEvents++;\r
+ }\r
+ });\r
+ wrappedPluginManager.addPluginStateListener(new PluginStateListener() {\r
+ @Override\r
+ public void pluginStateChanged(PluginStateEvent event) {\r
+ wrappedPluginManagerEvents++;\r
+ }\r
+ });\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertEquals(4, pluginManagerEvents);\r
+ assertEquals(2, wrappedPluginManagerEvents);\r
+ }\r
+\r
+ @Test\r
+ public void deletePlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.deletePlugin(OTHER_PLUGIN_ID));\r
+ assertTrue(wrappedPluginManager.deletePlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void disablePlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.disablePlugin(OTHER_PLUGIN_ID));\r
+ assertTrue(wrappedPluginManager.disablePlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void enablePlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.enablePlugin(OTHER_PLUGIN_ID));\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.enablePlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void getExtensionClasses() {\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertEquals(1, wrappedPluginManager.getExtensionClasses(TestExtensionPoint.class).size());\r
+\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getExtensionClasses(TestExtensionPoint.class, OTHER_PLUGIN_ID));\r
+ assertEquals(1, wrappedPluginManager.getExtensionClasses(TestExtensionPoint.class, THIS_PLUGIN_ID).size());\r
+\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getExtensionClasses(OTHER_PLUGIN_ID));\r
+ assertEquals(1, wrappedPluginManager.getExtensionClasses(THIS_PLUGIN_ID).size());\r
+ }\r
+\r
+ @Test\r
+ public void getExtensionClassNames() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getExtensionClassNames(OTHER_PLUGIN_ID));\r
+ assertEquals(1, wrappedPluginManager.getExtensionClassNames(THIS_PLUGIN_ID).size());\r
+ }\r
+\r
+ @Test\r
+ public void getExtensionFactory() {\r
+ pluginManager.loadPlugins();\r
+ assertEquals(pluginManager.getExtensionFactory(), wrappedPluginManager.getExtensionFactory());\r
+ }\r
+\r
+ @Test\r
+ public void getExtensions() {\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertEquals(1, wrappedPluginManager.getExtensions(TestExtensionPoint.class).size());\r
+\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getExtensions(TestExtensionPoint.class, OTHER_PLUGIN_ID));\r
+ assertEquals(1, wrappedPluginManager.getExtensions(TestExtensionPoint.class, THIS_PLUGIN_ID).size());\r
+\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getExtensions(OTHER_PLUGIN_ID));\r
+ assertEquals(1, wrappedPluginManager.getExtensions(THIS_PLUGIN_ID).size());\r
+ }\r
+\r
+ @Test\r
+ public void getPlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getPlugin(OTHER_PLUGIN_ID));\r
+ assertEquals(THIS_PLUGIN_ID, wrappedPluginManager.getPlugin(THIS_PLUGIN_ID).getPluginId());\r
+ }\r
+\r
+ @Test\r
+ public void getPluginClassLoader() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getPluginClassLoader(OTHER_PLUGIN_ID));\r
+ assertNotNull(wrappedPluginManager.getPluginClassLoader(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void getPlugins() {\r
+ pluginManager.loadPlugins();\r
+ assertEquals(2, pluginManager.getPlugins().size());\r
+ assertEquals(1, wrappedPluginManager.getPlugins().size());\r
+ }\r
+\r
+ @Test\r
+ public void getPluginsRoot() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getPluginsRoot());\r
+ }\r
+\r
+ @Test\r
+ public void getPluginsRoots() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.getPluginsRoots());\r
+ }\r
+\r
+ @Test\r
+ public void getResolvedPlugins() {\r
+ pluginManager.loadPlugins();\r
+ assertEquals(2, pluginManager.getResolvedPlugins().size());\r
+ assertEquals(1, wrappedPluginManager.getResolvedPlugins().size());\r
+ }\r
+\r
+ @Test\r
+ public void getRuntimeMode() {\r
+ assertEquals(pluginManager.getRuntimeMode(), wrappedPluginManager.getRuntimeMode());\r
+ }\r
+\r
+ @Test\r
+ public void getStartedPlugins() {\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertEquals(2, pluginManager.getStartedPlugins().size());\r
+ assertEquals(1, wrappedPluginManager.getStartedPlugins().size());\r
+ }\r
+\r
+ @Test\r
+ public void getSystemVersion() {\r
+ assertEquals(pluginManager.getSystemVersion(), wrappedPluginManager.getSystemVersion());\r
+ }\r
+\r
+ @Test\r
+ public void getUnresolvedPlugins() {\r
+ assertNotNull(wrappedPluginManager);\r
+ assertNotNull(wrappedPluginManager.getUnresolvedPlugins());\r
+ assertTrue(wrappedPluginManager.getUnresolvedPlugins().isEmpty());\r
+ }\r
+\r
+ @Test\r
+ public void getVersionManager() {\r
+ assertEquals(pluginManager.getVersionManager(), wrappedPluginManager.getVersionManager());\r
+ }\r
+\r
+ @Test\r
+ public void isDevelopment() {\r
+ assertEquals(pluginManager.isDevelopment(), wrappedPluginManager.isDevelopment());\r
+ }\r
+\r
+ @Test\r
+ public void isNotDevelopment() {\r
+ assertEquals(pluginManager.isNotDevelopment(), wrappedPluginManager.isNotDevelopment());\r
+ }\r
+\r
+ @Test\r
+ public void loadPlugin() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.loadPlugin(thisPlugin.path()));\r
+ }\r
+\r
+ @Test\r
+ public void loadPlugins() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.loadPlugins());\r
+ }\r
+\r
+ @Test\r
+ public void setSystemVersion() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.setSystemVersion("1.0.0"));\r
+ }\r
+\r
+ @Test\r
+ public void startPlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.startPlugin(OTHER_PLUGIN_ID));\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.startPlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void startPlugins() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.startPlugins());\r
+ }\r
+\r
+ @Test\r
+ public void stopPlugin() {\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.stopPlugin(OTHER_PLUGIN_ID));\r
+ assertEquals(PluginState.STOPPED, wrappedPluginManager.stopPlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void stopPlugins() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.stopPlugins());\r
+ }\r
+\r
+ @Test\r
+ public void unloadPlugin() {\r
+ pluginManager.loadPlugins();\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.unloadPlugin(OTHER_PLUGIN_ID));\r
+ assertTrue(wrappedPluginManager.unloadPlugin(THIS_PLUGIN_ID));\r
+ }\r
+\r
+ @Test\r
+ public void unloadPlugins() {\r
+ assertThrows(IllegalAccessError.class, () -> wrappedPluginManager.unloadPlugins());\r
+ }\r
+\r
+ @Test\r
+ public void whichPlugin() {\r
+ pluginManager.loadPlugins();\r
+ pluginManager.startPlugins();\r
+ assertEquals(null, wrappedPluginManager.whichPlugin(pluginManager.getExtensionClasses(OTHER_PLUGIN_ID).get(0)));\r
+ assertEquals(THIS_PLUGIN_ID, wrappedPluginManager.whichPlugin(pluginManager.getExtensionClasses(THIS_PLUGIN_ID).get(0)).getPluginId());\r
+ }\r
+}\r