Detect core plugins and prevent installation

This commit is contained in:
Rene Pfeuffer
2019-09-11 16:46:27 +02:00
parent a7cb1d3116
commit 0fdd1cea17
7 changed files with 56 additions and 6 deletions

View File

@@ -47,19 +47,20 @@ public final class InstalledPlugin implements Plugin
/** /**
* Constructs a new plugin wrapper. * Constructs a new plugin wrapper.
* * @param descriptor wrapped plugin
* @param descriptor wrapped plugin
* @param classLoader plugin class loader * @param classLoader plugin class loader
* @param webResourceLoader web resource loader * @param webResourceLoader web resource loader
* @param directory plugin directory * @param directory plugin directory
* @param core marked as core or not
*/ */
public InstalledPlugin(InstalledPluginDescriptor descriptor, ClassLoader classLoader, public InstalledPlugin(InstalledPluginDescriptor descriptor, ClassLoader classLoader,
WebResourceLoader webResourceLoader, Path directory) WebResourceLoader webResourceLoader, Path directory, boolean core)
{ {
this.descriptor = descriptor; this.descriptor = descriptor;
this.classLoader = classLoader; this.classLoader = classLoader;
this.webResourceLoader = webResourceLoader; this.webResourceLoader = webResourceLoader;
this.directory = directory; this.directory = directory;
this.core = core;
} }
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
@@ -120,6 +121,10 @@ public final class InstalledPlugin implements Plugin
return webResourceLoader; return webResourceLoader;
} }
public boolean isCore() {
return core;
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** plugin class loader */ /** plugin class loader */
@@ -133,4 +138,6 @@ public final class InstalledPlugin implements Plugin
/** plugin web resource loader */ /** plugin web resource loader */
private final WebResourceLoader webResourceLoader; private final WebResourceLoader webResourceLoader;
private final boolean core;
} }

View File

@@ -25,6 +25,8 @@ public class PluginDto extends HalRepresentation {
private String category; private String category;
private String avatarUrl; private String avatarUrl;
private boolean pending; private boolean pending;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean core;
private Set<String> dependencies; private Set<String> dependencies;
public PluginDto(Links links) { public PluginDto(Links links) {

View File

@@ -67,7 +67,8 @@ public abstract class PluginDtoMapper {
Links.Builder links = linkingTo() Links.Builder links = linkingTo()
.self(resourceLinks.installedPlugin() .self(resourceLinks.installedPlugin()
.self(information.getName())); .self(information.getName()));
if (availablePlugin.isPresent() if (!plugin.isCore()
&& availablePlugin.isPresent()
&& !availablePlugin.get().isPending() && !availablePlugin.get().isPending()
&& PluginPermissions.manage().isPermitted() && PluginPermissions.manage().isPermitted()
) { ) {
@@ -81,6 +82,8 @@ public abstract class PluginDtoMapper {
dto.setPending(value.isPending()); dto.setPending(value.isPending());
}); });
dto.setCore(plugin.isCore());
return dto; return dto;
} }

View File

@@ -40,6 +40,7 @@ import com.google.inject.Singleton;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.NotFoundException; import sonia.scm.NotFoundException;
import sonia.scm.ScmConstraintViolationException;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent; import sonia.scm.lifecycle.RestartEvent;
import sonia.scm.version.Version; import sonia.scm.version.Version;
@@ -54,6 +55,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.ScmConstraintViolationException.Builder.doThrow;
/** /**
* *
@@ -139,6 +141,13 @@ public class DefaultPluginManager implements PluginManager {
@Override @Override
public void install(String name, boolean restartAfterInstallation) { public void install(String name, boolean restartAfterInstallation) {
PluginPermissions.manage().check(); PluginPermissions.manage().check();
getInstalled(name)
.map(InstalledPlugin::isCore)
.ifPresent(
core -> doThrow().violation("plugin is a core plugin and cannot be updated").when(core)
);
List<AvailablePlugin> plugins = collectPluginsToInstall(name); List<AvailablePlugin> plugins = collectPluginsToInstall(name);
List<PendingPluginInstallation> pendingInstallations = new ArrayList<>(); List<PendingPluginInstallation> pendingInstallations = new ArrayList<>();
for (AvailablePlugin plugin : plugins) { for (AvailablePlugin plugin : plugins) {

View File

@@ -461,13 +461,16 @@ public final class PluginProcessor
Path descriptorPath = directory.resolve(PluginConstants.FILE_DESCRIPTOR); Path descriptorPath = directory.resolve(PluginConstants.FILE_DESCRIPTOR);
if (Files.exists(descriptorPath)) { if (Files.exists(descriptorPath)) {
boolean core = Files.exists(directory.resolve("core"));
ClassLoader cl = createClassLoader(classLoader, smp); ClassLoader cl = createClassLoader(classLoader, smp);
InstalledPluginDescriptor descriptor = createDescriptor(cl, descriptorPath); InstalledPluginDescriptor descriptor = createDescriptor(cl, descriptorPath);
WebResourceLoader resourceLoader = createWebResourceLoader(directory); WebResourceLoader resourceLoader = createWebResourceLoader(directory);
plugin = new InstalledPlugin(descriptor, cl, resourceLoader, directory); plugin = new InstalledPlugin(descriptor, cl, resourceLoader, directory, core);
} else { } else {
logger.warn("found plugin directory without plugin descriptor"); logger.warn("found plugin directory without plugin descriptor");
} }

View File

@@ -16,6 +16,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.NotFoundException; import sonia.scm.NotFoundException;
import sonia.scm.ScmConstraintViolationException;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent; import sonia.scm.lifecycle.RestartEvent;
@@ -177,6 +178,31 @@ class DefaultPluginManagerTest {
verify(eventBus, never()).post(any()); 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 @Test
void shouldInstallDependingPlugins() { void shouldInstallDependingPlugins() {
AvailablePlugin review = createAvailable("scm-review-plugin", "1"); AvailablePlugin review = createAvailable("scm-review-plugin", "1");

View File

@@ -248,7 +248,7 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase
private InstalledPlugin createPluginWrapper(Path directory) private InstalledPlugin createPluginWrapper(Path directory)
{ {
return new InstalledPlugin(null, null, new PathWebResourceLoader(directory), return new InstalledPlugin(null, null, new PathWebResourceLoader(directory),
directory); directory, false);
} }
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------