From 0fdd1cea178e67357652c50a15640c327c6dd3b8 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Wed, 11 Sep 2019 16:46:27 +0200 Subject: [PATCH] Detect core plugins and prevent installation --- .../sonia/scm/plugin/InstalledPlugin.java | 13 +++++++--- .../sonia/scm/api/v2/resources/PluginDto.java | 2 ++ .../scm/api/v2/resources/PluginDtoMapper.java | 5 +++- .../scm/plugin/DefaultPluginManager.java | 9 +++++++ .../sonia/scm/plugin/PluginProcessor.java | 5 +++- .../scm/plugin/DefaultPluginManagerTest.java | 26 +++++++++++++++++++ .../DefaultUberWebResourceLoaderTest.java | 2 +- 7 files changed, 56 insertions(+), 6 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java index 2021d4d00f..80ad1da7b8 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java @@ -47,19 +47,20 @@ public final class InstalledPlugin implements Plugin /** * Constructs a new plugin wrapper. - * - * @param descriptor wrapped plugin + * @param descriptor wrapped plugin * @param classLoader plugin class loader * @param webResourceLoader web resource loader * @param directory plugin directory + * @param core marked as core or not */ public InstalledPlugin(InstalledPluginDescriptor descriptor, ClassLoader classLoader, - WebResourceLoader webResourceLoader, Path directory) + WebResourceLoader webResourceLoader, Path directory, boolean core) { this.descriptor = descriptor; this.classLoader = classLoader; this.webResourceLoader = webResourceLoader; this.directory = directory; + this.core = core; } //~--- get methods ---------------------------------------------------------- @@ -120,6 +121,10 @@ public final class InstalledPlugin implements Plugin return webResourceLoader; } + public boolean isCore() { + return core; + } + //~--- fields --------------------------------------------------------------- /** plugin class loader */ @@ -133,4 +138,6 @@ public final class InstalledPlugin implements Plugin /** plugin web resource loader */ private final WebResourceLoader webResourceLoader; + + private final boolean core; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java index 41d285d59c..d9782643b2 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java @@ -25,6 +25,8 @@ public class PluginDto extends HalRepresentation { private String category; private String avatarUrl; private boolean pending; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean core; private Set dependencies; public PluginDto(Links links) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 00b363c6a3..222e34832a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -67,7 +67,8 @@ public abstract class PluginDtoMapper { Links.Builder links = linkingTo() .self(resourceLinks.installedPlugin() .self(information.getName())); - if (availablePlugin.isPresent() + if (!plugin.isCore() + && availablePlugin.isPresent() && !availablePlugin.get().isPending() && PluginPermissions.manage().isPermitted() ) { @@ -81,6 +82,8 @@ public abstract class PluginDtoMapper { dto.setPending(value.isPending()); }); + dto.setCore(plugin.isCore()); + return dto; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 56850758a2..2c3e92d618 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -40,6 +40,7 @@ import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.NotFoundException; +import sonia.scm.ScmConstraintViolationException; import sonia.scm.event.ScmEventBus; import sonia.scm.lifecycle.RestartEvent; import sonia.scm.version.Version; @@ -54,6 +55,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; /** * @@ -139,6 +141,13 @@ public class DefaultPluginManager implements PluginManager { @Override public void install(String name, boolean restartAfterInstallation) { PluginPermissions.manage().check(); + + getInstalled(name) + .map(InstalledPlugin::isCore) + .ifPresent( + core -> doThrow().violation("plugin is a core plugin and cannot be updated").when(core) + ); + List plugins = collectPluginsToInstall(name); List pendingInstallations = new ArrayList<>(); for (AvailablePlugin plugin : plugins) { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java index fced7a01ed..e1f0367948 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -461,13 +461,16 @@ public final class PluginProcessor Path descriptorPath = directory.resolve(PluginConstants.FILE_DESCRIPTOR); if (Files.exists(descriptorPath)) { + + boolean core = Files.exists(directory.resolve("core")); + ClassLoader cl = createClassLoader(classLoader, smp); InstalledPluginDescriptor descriptor = createDescriptor(cl, descriptorPath); WebResourceLoader resourceLoader = createWebResourceLoader(directory); - plugin = new InstalledPlugin(descriptor, cl, resourceLoader, directory); + plugin = new InstalledPlugin(descriptor, cl, resourceLoader, directory, core); } else { logger.warn("found plugin directory without plugin descriptor"); } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index 705319f63f..6551b7d51d 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -16,6 +16,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.NotFoundException; +import sonia.scm.ScmConstraintViolationException; import sonia.scm.event.ScmEventBus; import sonia.scm.lifecycle.RestartEvent; @@ -177,6 +178,31 @@ class DefaultPluginManagerTest { verify(eventBus, never()).post(any()); } + @Test + void shouldUpdateNormalPlugin() { + AvailablePlugin available = createAvailable("scm-git-plugin", "2"); + InstalledPlugin installed = createInstalled("scm-git-plugin", "1"); + when(installed.isCore()).thenReturn(false); + lenient().when(center.getAvailable()).thenReturn(ImmutableSet.of(available)); + when(loader.getInstalledPlugins()).thenReturn(ImmutableSet.of(installed)); + + manager.install("scm-git-plugin", false); + + verify(installer).install(available); + verify(eventBus, never()).post(any()); + } + + @Test + void shouldNotUpdateCorePlugin() { + AvailablePlugin available = createAvailable("scm-git-plugin", "2"); + InstalledPlugin installed = createInstalled("scm-git-plugin", "1"); + when(installed.isCore()).thenReturn(true); + lenient().when(center.getAvailable()).thenReturn(ImmutableSet.of(available)); + when(loader.getInstalledPlugins()).thenReturn(ImmutableSet.of(installed)); + + assertThrows(ScmConstraintViolationException.class, () -> manager.install("scm-git-plugin", false)); + } + @Test void shouldInstallDependingPlugins() { AvailablePlugin review = createAvailable("scm-review-plugin", "1"); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java index 7cb534c7ba..438d1ab305 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java @@ -248,7 +248,7 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase private InstalledPlugin createPluginWrapper(Path directory) { return new InstalledPlugin(null, null, new PathWebResourceLoader(directory), - directory); + directory, false); } //~--- fields ---------------------------------------------------------------