[java-identity-provider] 03/03: IDP-1595 the work of installing a plugin

Rod Widdowson rdw at steadingsoftware.com
Mon Jul 13 13:31:14 UTC 2020


This is an automated email from the git hooks/post-receive script.

rdw pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=08f91a1c04dc8877a34b7dcb63f8e40a17307fa9

commit 08f91a1c04dc8877a34b7dcb63f8e40a17307fa9
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Mon Jul 13 14:30:10 2020 +0100

    IDP-1595 the work of installing a plugin
    
    https://issues.shibboleth.net/jira/browse/IDP-1595
    
    The code is linear and pretty self explanatory.  tests are still
    pending having a release plugin to play with.
---
 .../idp/installer/plugin/impl/PluginInstaller.java | 170 +++++++++++++++++++--
 .../installer/plugin/impl/PluginInstallerTest.java |  27 +++-
 .../idphome-test/dist/webapp/WEB-INF/lib/.gitkeep  |   0
 idp-installer/src/test/resources/logback-test.xml  |   1 +
 4 files changed, 179 insertions(+), 19 deletions(-)

diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
index 224aaac1c..abdcee379 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
@@ -51,15 +51,19 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.http.client.HttpClient;
 import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Copy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicates;
 
 import net.shibboleth.ext.spring.resource.HTTPResource;
+import net.shibboleth.idp.installer.BuildWar;
+import net.shibboleth.idp.installer.InstallerSupport;
 import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.httpclient.HttpClientBuilder;
@@ -88,6 +92,9 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
     /** Where we have downloaded. */
     private Path downloadDirectory;
     
+    /** The plugin's story about itself. */
+    private PluginDescription description;
+
     /** The callback before we install a certificate into the TrustStore. */
     @Nonnull private Predicate<String> acceptCert = Predicates.alwaysFalse();
 
@@ -159,24 +166,149 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         unpack(base, fileName);
         setupPluginId();
         checkSignature(base, fileName);
-        //doInstall();
+        getDescription();
+
+        final Path myWebApp = idpHome.resolve("dist").resolve("edit-webapp-" + pluginId);
+        deleteTree(myWebApp);
+        installWebapp(myWebApp);
+        installFiles();
+        downloadExternals();
+
+        final BuildWar builder = new BuildWar(idpHome);
+        try {
+            builder.initialize();
+        } catch (final ComponentInitializationException e) {
+            throw new BuildException(e);
+        }
+        builder.execute();
     }
 
-    /** Method to download a zip file to the {{@link #downloadDirectory}.
-     * @param baseURL Where the zip/tgz and signature file is
-     * @param fileName the name.
+    /** Download any files that should not be shipped.
      * @throws BuildException if badness is detected.
      */
-    private void download(final URL baseURL, final String fileName) throws BuildException {
-        if (httpClient == null) {
-            log.debug("No HttpClient built, creating default builder");
+    private void downloadExternals() throws BuildException {
+        try {
+            for (final Pair<URL, Path> pair : description.getExternalFilePathsToCopy()) {
+                final Path to = idpHome.resolve(pair.getSecond());
+                if (Files.exists(to)) {
+                    log.warn("{} exists, not copied", to);
+                    continue;
+                }
+                buildHttpClient();
+                createParent(to);
+                log.debug("Copying from {} to {}", pair.getFirst(), to);
+                final Resource from  = new HTTPResource(httpClient, pair.getFirst());
+                try (final InputStream in = new BufferedInputStream(from.getInputStream());
+                     final OutputStream out =  new BufferedOutputStream(new FileOutputStream(to.toFile()))) {
+
+                    in.transferTo(out);
+
+                } catch (final IOException e) {
+                    log.error("Could not copy from {} to {}",  from, to, e);
+                    throw new BuildException(e);
+                }
+            }
+        } catch (final IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /** Get hold of the {@link PluginDescription} for this plugin.
+     * @throws BuildException if badness is happens.
+     */
+    private void getDescription() throws BuildException {
+        final List<URL> urls = new ArrayList<>();
+        final Path libDir = distribution.resolve("edit-webapp").resolve("WEB-INF").resolve("lib");
+
+        try {
+            for (final Path jar : Files.newDirectoryStream(libDir)) {
+                urls.add(jar.toUri().toURL());
+            }
+           try (final URLClassLoader loader = new URLClassLoader(urls.toArray(URL[]::new))){
+
+               final ServiceLoader<PluginDescription> plugins = ServiceLoader.load(PluginDescription.class, loader);
+               for (final PluginDescription plugin:plugins) {
+                   log.debug("Found Service announcing itself as {}", plugin.getPluginId() );
+                   if (pluginId.equals(plugin.getPluginId())) {
+                       description = plugin;
+                       return;
+                   }
+               }
+           }
+           log.error("Could not locate description for {} in distribution {}", pluginId, libDir);
+           throw new BuildException("Could not locate PluginDescription");
+        } catch (final IOException e) {
+            log.error("Could not get description of {} from {}", pluginId, libDir, e);
+            throw new BuildException(e);
+        }
+    }
+
+    /** Copy the files the distribution tells us to.
+     * @throws BuildException if badness is happens.
+     */
+    private void installFiles() throws BuildException {
+        for (final Path p : description.getFilePathsToCopy()) {
+            final Path from = distribution.resolve(p);
+            final Path to = idpHome.resolve(p);
+            if (Files.exists(to)) {
+                log.debug("File {} exists, skipping", to);
+                continue;
+            }
+            if (!Files.exists(from)) {
+                log.warn("Source File {} does not exists, skipping", from);
+                continue;
+            }
             try {
-                httpClient = new HttpClientBuilder().buildClient();
-            } catch (final Exception e) {
-                log.error("Could not create HttpClient", e);
+                createParent(to);
+                log.debug("Copying from {} to {}", from, to);
+                try (final InputStream in = new BufferedInputStream(new FileInputStream(from.toFile()));
+                     final OutputStream out =  new BufferedOutputStream(new FileOutputStream(to.toFile()))) {
+                    in.transferTo(out);
+                }
+            } catch (final IOException e) {
+                log.error("Could not copy from {} to {}",  from, to, e);
                 throw new BuildException(e);
             }
         }
+    }
+
+    /** If the parent dir of the provided path doesn't exist, create it.
+     * @param file where the file will go
+     * @throws IOException if the directory couldn't be created
+     * @throws BuildException if the parent wasnt a directory
+     */
+    private void createParent(final Path file) throws IOException, BuildException {
+        final Path parent = file.resolve("..");
+        if (!Files.exists(parent)) {
+            log.debug("Creating parent directory {}", parent);
+            Files.createDirectories(parent);
+        } else if (!Files.isDirectory(parent)) {
+            log.error("{} exists and is not a directory", parent);
+            throw new BuildException("Parent of target file was not a directory");
+        } else {
+            log.trace("Parent directory {} existed", parent);
+        }  
+    }
+
+    /** Copy the webapp folder from the distribution to the per plugin
+     * location inside dist.
+     * @param myWebApp Where to put it.
+     * @throws BuildException if badness is detected.
+     */
+    private void installWebapp(final Path myWebApp) throws BuildException {
+        final Path from = distribution.resolve("edit-webapp");
+        log.debug("Copying distribution from {} to {}", from, myWebApp);
+        final Copy copy = InstallerSupport.getCopyTask(from, myWebApp);
+        copy.execute();
+    }
+
+    /** Method to download a zip file to the {{@link #downloadDirectory}.
+     * @param baseURL Where the zip/tgz and signature file is
+     * @param fileName the name.
+     * @throws BuildException if badness is detected.
+     */
+    private void download(final URL baseURL, final String fileName) throws BuildException {
+        buildHttpClient();
         try {
             downloadDirectory = Files.createTempDirectory("plugin-installer-download");
             final Resource baseResource = new HTTPResource(httpClient, baseURL);
@@ -188,6 +320,19 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         }
     }
 
+    /** Build the Http Client if it doesn't exist. */
+    private void buildHttpClient() {
+        if (httpClient == null) {
+            log.debug("No HttpClient built, creating default");
+            try {
+                httpClient = new HttpClientBuilder().buildClient();
+            } catch (final Exception e) {
+                log.error("Could not create HttpClient", e);
+                throw new BuildException(e);
+            }
+        }
+    }
+
     /** Download helper method.
      * @param baseResource where to go for the file
      * @param fileName the file name
@@ -256,7 +401,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
             throw new BuildException(e);
         }
     }
-    // CheckStyle:  CyclomaticComplexity OFF
+    // CheckStyle:  CyclomaticComplexity ON
     
     /** does the file name end in .zip?
      * @param fileName the name to consider
@@ -390,9 +535,10 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @param directory what to delete
      */
     private void deleteTree(@Nullable final Path directory) {
-        if (directory == null) {
+        if (directory == null || !Files.exists(directory)) {
             return;
         }
+        log.debug("Deleting directory {}", directory);
         try {
             Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
                 @Override 
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
index b26c97c76..ff15e8b9b 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
@@ -24,14 +24,15 @@ import java.io.IOException;
 import java.net.URL;
 import java.security.Security;
 import java.util.List;
+import java.util.function.Predicate;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.core.io.ClassPathResource;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Predicates;
-
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.plugin.AbstractPluginDescription;
 import net.shibboleth.utilities.java.support.plugin.PluginDescription;
@@ -39,13 +40,15 @@ import net.shibboleth.utilities.java.support.plugin.PluginDescription;
 @SuppressWarnings("javadoc")
 public class PluginInstallerTest {
 
+    private final Logger log = LoggerFactory.getLogger(PluginInstallerTest.class);
+
     @BeforeClass public void setup() throws IOException {
         if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
             Security.addProvider(new BouncyCastleProvider());
         }
     }
 
-    @Test public void testListing() throws ComponentInitializationException, IOException {
+    @Test(enabled = false) public void testListing() throws ComponentInitializationException, IOException {
         
         try (final PluginInstaller inst = new PluginInstaller()) {
             inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
@@ -58,16 +61,17 @@ public class PluginInstallerTest {
     @Test(enabled = false) public void testUnpackZip() throws ComponentInitializationException, IOException {
         try (final PluginInstaller inst = new PluginInstaller()) {
             inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
+            inst.setAcceptCert(new LoggingAcceptor());
             inst.initialize();
-            final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\nashorn-dist\\target");
-            inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.zip");
+            final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\rhino-dist\\target");
+            inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-rhino-0.0.1-SNAPSHOT.zip");
         }
     }
     
     @Test(enabled = false) public void testUnpackTgz() throws ComponentInitializationException, IOException {
         try (final PluginInstaller inst = new PluginInstaller()) {
             inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
-            inst.setAcceptCert(Predicates.alwaysTrue());
+            inst.setAcceptCert(new LoggingAcceptor());
             inst.initialize();
             final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\nashorn-dist\\target");
             inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.tar.gz");
@@ -77,7 +81,7 @@ public class PluginInstallerTest {
     @Test(enabled = false) public void testDownload() throws ComponentInitializationException, IOException {
         try (final PluginInstaller inst = new PluginInstaller()) {
             inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
-            inst.setAcceptCert(Predicates.alwaysTrue());
+            inst.setAcceptCert(new LoggingAcceptor());
             inst.initialize();
             final URL url = new URL("http://iis.steadingsoftware.net/plugins/");
             inst.installPlugin(url,"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.tar.gz");
@@ -108,4 +112,13 @@ public class PluginInstallerTest {
         }
         
     }
+
+    public class LoggingAcceptor implements Predicate<String> {
+
+        /** {@inheritDoc} */
+        public boolean test(String what) {
+            log.debug("Accepting the cetrtificate {}", what);
+            return true;
+        }
+    }
 }
diff --git a/idp-installer/src/test/resources/idphome-test/dist/webapp/WEB-INF/lib/.gitkeep b/idp-installer/src/test/resources/idphome-test/dist/webapp/WEB-INF/lib/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/idp-installer/src/test/resources/logback-test.xml b/idp-installer/src/test/resources/logback-test.xml
index d3b8a58c9..042bb588c 100644
--- a/idp-installer/src/test/resources/logback-test.xml
+++ b/idp-installer/src/test/resources/logback-test.xml
@@ -3,6 +3,7 @@
 <configuration>
 
     <logger name="org" level="INFO"/>
+    <logger name="net.shibboleth.idp.installer" level="TRACE"/>
 
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
         <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list