mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 11:05:56 +01:00
Check plugin dependencies after download
This commit is contained in:
@@ -19,11 +19,13 @@ class PluginInstaller {
|
||||
|
||||
private final SCMContextProvider context;
|
||||
private final AdvancedHttpClient client;
|
||||
private final SmpDDescriptorExtractor smpDDescriptorExtractor;
|
||||
|
||||
@Inject
|
||||
public PluginInstaller(SCMContextProvider context, AdvancedHttpClient client) {
|
||||
public PluginInstaller(SCMContextProvider context, AdvancedHttpClient client, SmpDDescriptorExtractor smpDDescriptorExtractor) {
|
||||
this.context = context;
|
||||
this.client = client;
|
||||
this.smpDDescriptorExtractor = smpDDescriptorExtractor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("squid:S4790") // hashing should be safe
|
||||
@@ -34,6 +36,17 @@ class PluginInstaller {
|
||||
Files.copy(input, file);
|
||||
|
||||
verifyChecksum(plugin, input.hash(), file);
|
||||
InstalledPluginDescriptor pluginDescriptor = smpDDescriptorExtractor.extractPluginDescriptor(file);
|
||||
if (!pluginDescriptor.getCondition().isSupported()) {
|
||||
cleanup(file);
|
||||
throw new PluginConditionFailedException(
|
||||
pluginDescriptor.getCondition(),
|
||||
String.format(
|
||||
"could not load plugin %s, the plugin condition does not match",
|
||||
pluginDescriptor.getInformation().getId()
|
||||
)
|
||||
);
|
||||
}
|
||||
return new PendingPluginInstallation(plugin.install(), file);
|
||||
} catch (IOException ex) {
|
||||
cleanup(file);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package sonia.scm.plugin;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
class SmpDDescriptorExtractor {
|
||||
|
||||
InstalledPluginDescriptor extractPluginDescriptor(Path file) throws IOException {
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8)) {
|
||||
ZipEntry nextEntry;
|
||||
while ((nextEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if ("META-INF/scm/plugin.xml".equals(nextEntry.getName())) {
|
||||
JAXBContext context = JAXBContext.newInstance(ScmModule.class, InstalledPluginDescriptor.class);
|
||||
return (InstalledPluginDescriptor) context.createUnmarshaller().unmarshal(zipInputStream);
|
||||
}
|
||||
}
|
||||
} catch (JAXBException e) {
|
||||
throw new IOException("failed to read descriptor file META-INF/scm/plugin.xml from plugin", e);
|
||||
}
|
||||
throw new IOException("Missing plugin descriptor META-INF/scm/plugin.xml in download package");
|
||||
}
|
||||
}
|
||||
@@ -32,18 +32,23 @@ class PluginInstallerTest {
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private AdvancedHttpClient client;
|
||||
|
||||
@Mock
|
||||
private SmpDDescriptorExtractor extractor;
|
||||
|
||||
@InjectMocks
|
||||
private PluginInstaller installer;
|
||||
|
||||
private Path directory;
|
||||
|
||||
@BeforeEach
|
||||
void setUpContext(@TempDirectory.TempDir Path directory) {
|
||||
void setUpContext(@TempDirectory.TempDir Path directory) throws IOException {
|
||||
this.directory = directory;
|
||||
lenient().when(context.resolve(any())).then(ic -> {
|
||||
Path arg = ic.getArgument(0);
|
||||
return directory.resolve(arg);
|
||||
});
|
||||
InstalledPluginDescriptor supportedPlugin = createPluginDescriptor(true);
|
||||
lenient().when(extractor.extractPluginDescriptor(any())).thenReturn(supportedPlugin);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,6 +110,15 @@ class PluginInstallerTest {
|
||||
assertThat(directory.resolve("plugins").resolve("scm-git-plugin.smp")).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailForUnsupportedPlugin() throws IOException {
|
||||
mockContent("42");
|
||||
InstalledPluginDescriptor supportedPlugin = createPluginDescriptor(false);
|
||||
when(extractor.extractPluginDescriptor(any())).thenReturn(supportedPlugin);
|
||||
|
||||
assertThrows(PluginConditionFailedException.class, () -> installer.install(createGitPlugin()));
|
||||
assertThat(directory.resolve("plugins").resolve("scm-git-plugin.smp")).doesNotExist();
|
||||
}
|
||||
|
||||
private AvailablePlugin createPlugin(String name, String url, String checksum) {
|
||||
PluginInformation information = new PluginInformation();
|
||||
@@ -114,4 +128,11 @@ class PluginInstallerTest {
|
||||
);
|
||||
return new AvailablePlugin(descriptor);
|
||||
}
|
||||
|
||||
private InstalledPluginDescriptor createPluginDescriptor(boolean supported) {
|
||||
InstalledPluginDescriptor installedPluginDescriptor = mock(InstalledPluginDescriptor.class, RETURNS_DEEP_STUBS);
|
||||
lenient().when(installedPluginDescriptor.getCondition().isSupported()).thenReturn(supported);
|
||||
lenient().when(installedPluginDescriptor.getInformation().getId()).thenReturn("scm-git-plugin");
|
||||
return installedPluginDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package sonia.scm.plugin;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junitpioneer.jupiter.TempDirectory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ExtendWith(TempDirectory.class)
|
||||
class SmpDDescriptorExtractorTest {
|
||||
|
||||
private static final String PLUGIN_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
||||
"<plugin>\n" +
|
||||
"\n" +
|
||||
" <scm-version>2</scm-version>\n" +
|
||||
"\n" +
|
||||
" <information>\n" +
|
||||
" <displayName>Test</displayName>\n" +
|
||||
" <author>Cloudogu GmbH</author>\n" +
|
||||
" <category>Testing</category>\n" +
|
||||
" <name>scm-test-plugin</name>\n" +
|
||||
"<version>2.0.0</version>\n" +
|
||||
"<description>Collects information for support cases</description>\n" +
|
||||
"</information>\n" +
|
||||
"\n" +
|
||||
" <conditions>\n" +
|
||||
" <min-version>2.0.0-rc1</min-version>\n" +
|
||||
" </conditions>\n" +
|
||||
"\n" +
|
||||
" <resources>\n" +
|
||||
" <script>assets/scm-support-plugin.bundle.js</script>\n" +
|
||||
" </resources>\n" +
|
||||
"\n" +
|
||||
"</plugin>\n";
|
||||
|
||||
@Test
|
||||
void shouldExtractPluginXml(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||
Path pluginFile = createZipFile(tempDir, "META-INF/scm/plugin.xml", PLUGIN_XML);
|
||||
|
||||
InstalledPluginDescriptor installedPluginDescriptor = new SmpDDescriptorExtractor().extractPluginDescriptor(pluginFile);
|
||||
|
||||
Assertions.assertThat(installedPluginDescriptor.getInformation().getName()).isEqualTo("scm-test-plugin");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailWithoutPluginXml(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||
Path pluginFile = createZipFile(tempDir, "META-INF/wrong/plugin.xml", PLUGIN_XML);
|
||||
|
||||
assertThrows(IOException.class, () -> new SmpDDescriptorExtractor().extractPluginDescriptor(pluginFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailWithIllegalPluginXml(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||
Path pluginFile = createZipFile(tempDir, "META-INF/scm/plugin.xml", "<not><parsable>content</parsable></not>");
|
||||
|
||||
assertThrows(IOException.class, () -> new SmpDDescriptorExtractor().extractPluginDescriptor(pluginFile));
|
||||
}
|
||||
|
||||
Path createZipFile(Path tempDir, String internalFileName, String content) throws IOException {
|
||||
Path pluginFile = tempDir.resolve("scm-test-plugin.smp");
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(pluginFile), UTF_8);
|
||||
zipOutputStream.putNextEntry(new ZipEntry(internalFileName));
|
||||
zipOutputStream.write(content.getBytes(UTF_8));
|
||||
zipOutputStream.closeEntry();
|
||||
zipOutputStream.close();
|
||||
return pluginFile;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user