[java-identity-provider] 04/04: IDP-2117, IDP-2122, IDP-2073 Plugin Installer fixes

Rod Widdowson rdw at steadingsoftware.com
Sun May 28 15:05:06 UTC 2023


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

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

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

commit 5ab838affc71ec21b5173baa6b3c3d9cba80d577
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sun May 28 15:52:14 2023 +0100

    IDP-2117, IDP-2122, IDP-2073 Plugin Installer fixes
    
    https://shibboleth.atlassian.net/browse/IDP-2122
    https://shibboleth.atlassian.net/browse/IDP-2117
    https://shibboleth.atlassian.net/browse/IDP-2073
    
    Add a lot of nullability annotations
    
    IDP-2112 Fix Plugin Verion.equals method
    IDP-2117 Correctly pass the security handler down for download
    IDP-2073 Start to exbend some function that the command line update
    program can use
---
 .../idp/installer/impl/IdPInstallerCLI.java        |   1 -
 .../idp/installer/plugin/impl/PluginInstaller.java |  42 ++----
 .../installer/plugin/impl/PluginInstallerCLI.java  | 130 +++----------------
 .../plugin/impl/PluginInstallerSupport.java        | 141 +++++++++++++++++++++
 .../idp/installer/plugin/impl/PluginState.java     |   4 +-
 .../shibboleth/idp/installer/TestInstallerCLI.java |  49 ++++++-
 .../idp/installer/plugin/impl/PluginStateTest.java |   5 +-
 .../idp/installer/plugin/impl/RollbackTester.java  |   7 +-
 .../idp/installer/plugin/impl/TrustStoreTest.java  |  30 +++--
 9 files changed, 249 insertions(+), 160 deletions(-)

diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/IdPInstallerCLI.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/IdPInstallerCLI.java
index 762edfe5f..bfcb4da7a 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/IdPInstallerCLI.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/IdPInstallerCLI.java
@@ -50,7 +50,6 @@ public class IdPInstallerCLI extends AbstractCommandLine<IdPInstallerArguments>
     @Override
     @Nonnull
     protected Class<IdPInstallerArguments> getArgumentClass() {
-        // TODO Auto-generated method stub
         return IdPInstallerArguments.class;
     }
 
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 17e61b6ac..520785f09 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
@@ -67,7 +67,6 @@ import org.slf4j.Logger;
 
 import net.shibboleth.idp.Version;
 import net.shibboleth.idp.installer.InstallerSupport;
-import net.shibboleth.idp.installer.ProgressReportingOutputStream;
 import net.shibboleth.idp.installer.impl.BuildWar;
 import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
 import net.shibboleth.idp.module.IdPModule;
@@ -86,7 +85,6 @@ import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.resource.Resource;
 import net.shibboleth.shared.spring.httpclient.resource.HTTPResource;
 
 /**
@@ -335,6 +333,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
     public void uninstall() throws BuildException {
 
         String moduleId = null;
+        assert pluginId != null;
         description = getInstalledPlugin(pluginId);
         if (description == null) {
             LOG.warn("Description for {} not found", pluginId);
@@ -673,14 +672,15 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      */
     private void download(@Nonnull final URL baseURL, @Nonnull final String fileName) throws BuildException {
         try {
-            downloadDirectory = Files.createTempDirectory("plugin-installer-download");
+            final Path dir = downloadDirectory = Files.createTempDirectory("plugin-installer-download");
+            assert dir != null;
             final HTTPResource baseResource = new HTTPResource(httpClient, baseURL);
             final HttpClientSecurityContextHandler handler = new HttpClientSecurityContextHandler();
             handler.setHttpClientSecurityParameters(securityParams);
             handler.initialize();
             baseResource.setHttpClientContextHandler(handler);
-            download(baseResource, fileName);
-            download(baseResource, fileName + ".asc");
+            PluginInstallerSupport.download(baseResource, handler, dir,  fileName);
+            PluginInstallerSupport.download(baseResource, handler, dir,  fileName + ".asc");
         } catch (final IOException | ComponentInitializationException e) {
             LOG.error("Error in download", e);
             throw new BuildException(e);
@@ -740,21 +740,6 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         }
     }
 
-    /** Download helper method.
-     * @param baseResource where to go for the file
-     * @param fileName the file name
-     * @throws IOException as required
-     */
-    private void download(final Resource baseResource, @Nonnull final String fileName) throws IOException {
-        final Resource fileResource = baseResource.createRelativeResource(fileName);
-        final Path filePath = downloadDirectory.resolve(fileName);
-        LOG.info("Downloading from {}", fileResource.getDescription());
-        LOG.debug("Downloading to {}", filePath);
-        try (final OutputStream fileOut = new ProgressReportingOutputStream(new FileOutputStream(filePath.toFile()))) {
-            fileResource.getInputStream().transferTo(fileOut);
-        }
-    }
-
     /** Method to unpack a zip or tgz file into out {{@link #unpackDirectory}.
      * @param base Where the zip/tgz file is
      * @param fileName the name.
@@ -839,7 +824,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @return the the appropriate  {@link ArchiveInputStream} 
      * @throws IOException  if we trip over an unpack
      */
-    private ArchiveInputStream getStreamFor(final Path fullName, final boolean isZip) throws IOException {
+    @Nonnull private ArchiveInputStream getStreamFor(final Path fullName, final boolean isZip) throws IOException {
         final InputStream inStream = new BufferedInputStream(new FileInputStream(fullName.toFile()));
         if (isZip) {
             return new ZipArchiveInputStream(inStream);
@@ -881,7 +866,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @param fileName the name.
      * @throws BuildException if badness is detected.
      */
-    private void checkSignature(final Path base, final String fileName) throws BuildException {
+    private void checkSignature(@Nonnull final Path base, @Nonnull final String fileName) throws BuildException {
         try (final InputStream sigStream = new BufferedInputStream(
                 new FileInputStream(base.resolve(fileName + ".asc").toFile()))) {
             final TrustStore trust = new TrustStore();
@@ -955,7 +940,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @return an appropriate loader
      * @throws BuildException if a directory traversal fails.
      */
-    private synchronized URLClassLoader getInstalledPluginsLoader() throws BuildException {
+    @Nonnull private synchronized URLClassLoader getInstalledPluginsLoader() throws BuildException {
 
         if (installedPluginsLoader != null) {
             return installedPluginsLoader;
@@ -1001,7 +986,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @return an appropriate loader
      * @throws BuildException if a directory traversal fails.
      */
-    private synchronized URLClassLoader getDistributionLoader() throws BuildException {
+    @Nonnull private synchronized URLClassLoader getDistributionLoader() throws BuildException {
         if (installingPluginLoader!= null) {
             return installingPluginLoader;
         }
@@ -1027,18 +1012,17 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @return All the plugins.
      * @throws BuildException if loafing the classpath fails.
      */
-    public List<IdPPlugin> getInstalledPlugins() throws BuildException {
+    @Nonnull public List<IdPPlugin> getInstalledPlugins() throws BuildException {
        final Stream<Provider<IdPPlugin>> loaderStream =
                ServiceLoader.load(IdPPlugin.class, getInstalledPluginsLoader()).stream();
-       return loaderStream.map(ServiceLoader.Provider::get).collect(Collectors.toList());
+       return loaderStream.map(ServiceLoader.Provider::get).collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
     }
 
     /** Find the {@link IdPPlugin} with the provided Id.
      * @param name what to find
      * @return the {@link IdPPlugin} or null if not found.
      */
-    @Nullable public IdPPlugin getInstalledPlugin(final String name) {
-        Constraint.isNotNull(name, "Plugin Name must not be null");
+    @Nullable public IdPPlugin getInstalledPlugin(@Nonnull final String name) {
         final List<IdPPlugin> plugins = getInstalledPlugins();
         for (final IdPPlugin plugin: plugins) {
             if (plugin.getPluginId().equals(name)) {
@@ -1075,7 +1059,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
     /** Return a version we can use in a test proof manner.
      * @return the IdP version or a fixed value
      */
-    protected static PluginVersion getIdPVersion() {
+    @Nonnull protected static PluginVersion getIdPVersion() {
         final String version  = Version.getVersion();
 
         if (version == null) {
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
index 12df2d90a..c3a6ff09a 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
@@ -40,18 +40,15 @@ import javax.annotation.Nullable;
 import org.apache.hc.client5.http.classic.HttpClient;
 import org.apache.tools.ant.BuildException;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.opensaml.security.httpclient.HttpClientSecurityContextHandler;
 import org.slf4j.Logger;
 import org.springframework.beans.BeansException;
 import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.FileSystemResource;
 import org.springframework.core.io.Resource;
 
 import net.shibboleth.idp.Version;
 import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
 import net.shibboleth.idp.installer.plugin.impl.PluginState.VersionInfo;
 import net.shibboleth.idp.plugin.IdPPlugin;
-import net.shibboleth.idp.plugin.PluginSupport.SupportLevel;
 import net.shibboleth.idp.plugin.PluginVersion;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.cli.AbstractCommandLine;
@@ -60,7 +57,6 @@ import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.spring.httpclient.resource.HTTPResource;
 
 /**
  * Command line for Plugin Installation.
@@ -74,7 +70,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
     @Nullable private PluginInstaller installer;
 
     /** Update URLs. */
-    private List<URL> updateURLs;
+    @Nonnull private List<URL> updateURLs = CollectionSupport.emptyList();
 
     /**
       * Constrained Constructor.
@@ -116,7 +112,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
     /** {@inheritDoc} */
     //CheckStyle: CyclomaticComplexity|MethodLength OFF
     protected int doRun(@Nonnull final PluginInstallerArguments args) {
-        
+
         if (args.getHttpClientName() == null) {
             args.setHttpClientName("shibboleth.InternalHttpClient");
         }
@@ -140,8 +136,6 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
                 log.error("Could not convert update URL {}", args.getUpdateURL(), e);
                 return RC_INIT;
             }
-        } else {
-            updateURLs = CollectionSupport.emptyList();
         }
 
         try (final PluginInstaller inst = new PluginInstaller(Constraint.isNotNull(getHttpClient(), "HJttpClient cannot be non null (by construction"))) {
@@ -394,7 +388,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
      * @return whether it worked
      */
     private int doListAvailable() {
-        final Properties props = loadPluginInfo();
+        final Properties props = PluginInstallerSupport.loadPluginInfo(updateURLs, this);
         if (props == null) {
             return RC_IO;
         }
@@ -412,27 +406,30 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
             }
         }
 
-        for (final Entry<String, PluginInfo> e: plugins.entrySet()) {
+        for (final Entry<String, PluginInfo> entry: plugins.entrySet()) {
             final PluginVersion nullVersion = new PluginVersion(0, 0, 0);
+            final String key = entry.getKey();
+            final PluginInfo value = entry.getValue();
+            assert key!=null && value !=null;
             assert installer != null;
-            final IdPPlugin existingPlugin = installer.getInstalledPlugin(e.getKey());
+            final IdPPlugin existingPlugin = installer.getInstalledPlugin(key);
             if (existingPlugin == null) {
-                final PluginVersion version = getBestVersion(nullVersion, e.getValue());
+                final PluginVersion version = PluginInstallerSupport.getBestVersion(nullVersion, value);
                 if (version == null) {
-                    log.debug("Plugin {} has no version available", e.getKey());
+                    log.debug("Plugin {} has no version available", entry.getKey());
                 } else {
-                    outOrLog(String.format("Plugin %s: version %s available for install", e.getKey(), version));
+                    outOrLog(String.format("Plugin %s: version %s available for install", entry.getKey(), version));
                 }
             } else {
                 final PluginVersion existingVersion = new PluginVersion(existingPlugin);
-                final PluginVersion version = getBestVersion(existingVersion, e.getValue());
+                final PluginVersion version = PluginInstallerSupport.getBestVersion(existingVersion, value);
                 if (version == null) {
                     outOrLog(String.format("Plugin %s: Installed version %s: No update available",
-                            e.getKey(),
+                            entry.getKey(),
                             existingVersion));
                 } else {
                     outOrLog(String.format("Plugin %s: Installed version %s: Update to %s available",
-                            e.getKey(),
+                            entry.getKey(),
                             existingVersion,
                             version));
                 }
@@ -441,59 +438,6 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
         return RC_OK;
     }
 
-    /** Load the property file describing all the plugin we know about from a known location.
-     * @return the property files plugins.
-     */
-    private Properties loadPluginInfo() {
-        final List<URL> urls;
-        final Properties props = new Properties();
-        try {
-            if (updateURLs == null || updateURLs.isEmpty()) {
-                urls = List.of(
-                        new URL("https://shibboleth.net/downloads/identity-provider/plugins/plugins.properties"),
-                        new URL("http://plugins.shibboleth.net/plugins.properties"));
-            } else {
-                urls = updateURLs;
-            }
-        } catch (final IOException e) {
-            log.error("Could not load update URLs", e);
-            return null;
-        }
-        for (final URL url: urls) {
-            final Resource propertyResource;
-            try {
-                if ("file".equals(url.getProtocol())) {
-                    final String path =url.getPath();
-                    assert path != null;
-                    propertyResource = new FileSystemResource(path);
-                } else if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) {
-                    final HttpClient client = getHttpClient();
-                    assert client != null;
-                    final HTTPResource httpResource;
-                    propertyResource = httpResource = new HTTPResource(client , url);
-                    final HttpClientSecurityContextHandler handler = new HttpClientSecurityContextHandler();
-                    handler.setHttpClientSecurityParameters(getHttpClientSecurityParameters());
-                    handler.initialize();
-                    httpResource.setHttpClientContextHandler(handler);
-                } else {
-                    log.error("Only file and http[s] URLs are allowed");
-                    continue;
-                }
-                log.debug("Plugin Listing: Looking for update at {}", propertyResource.getDescription());
-                if (!propertyResource.exists()) {
-                    log.info("{} could not be located", propertyResource.getDescription());
-                    continue;
-                }
-                props.load(propertyResource.getInputStream());
-                return props;
-            } catch (final IOException | ComponentInitializationException e) {
-                log.error("Could not open Update URL {} :", url, e);
-                continue;
-            }
-        }
-        log.error("Could not locate any active update servers");
-        return null;
-    }
 
     /** Given the pluginId find the best version and install.
      * If already installed whine
@@ -509,7 +453,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
             log.error("Plugin {} is already installed", pluginId);
             return RC_INIT;
         }
-        final Properties props = loadPluginInfo();
+        final Properties props = PluginInstallerSupport.loadPluginInfo(updateURLs, this);
         if (props == null) {
             log.error("AutoInstall not possible");
             return RC_INIT;
@@ -519,7 +463,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
             log.error("Plugin {}: Information not found", pluginId);
             return RC_INIT;
         }
-        final PluginVersion versionToInstall = getBestVersion(new PluginVersion(0,0,0), info);
+        final PluginVersion versionToInstall = PluginInstallerSupport.getBestVersion(new PluginVersion(0,0,0), info);
         if (versionToInstall == null) {
             log.error("Plugin {}: No version available to install", pluginId);
             return RC_INIT;
@@ -532,46 +476,6 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
         return RC_OK;
     }
 
-
-
-    /** Find the best update version.  Helper function for {@linkplain #doUpdate(String, PluginVersion, boolean)}.
-     * @param pluginVersion The Plugin version
-     * @param pluginInfo all about the plugin
-     * @return the best version (or null)
-     */
-    @Nullable private PluginVersion getBestVersion(final PluginVersion pluginVersion , final PluginInfo pluginInfo) {
-
-        final PluginVersion idPVersion = PluginInstaller.getIdPVersion();
-
-        final List<PluginVersion> availableVersions = new ArrayList<>(pluginInfo.getAvailableVersions().keySet());
-        availableVersions.sort(null);
-        log.debug("Considering versions: {}", availableVersions);
-
-        for (int i = availableVersions.size()-1; i >= 0; i--) {
-            final PluginVersion version = availableVersions.get(i);
-            if (version.compareTo(pluginVersion) <= 0) {
-                log.debug("Version {} is less than or the same as {}. All done", version, pluginVersion);
-                return null;
-            }
-            final VersionInfo versionInfo = pluginInfo.getAvailableVersions().get(version);
-            if (versionInfo.getSupportLevel() != SupportLevel.Current) {
-                log.debug("Version {} has support level {}, ignoring", version, versionInfo.getSupportLevel());
-                continue;
-            }
-            if (!pluginInfo.isSupportedWithIdPVersion(version, idPVersion)) {
-                log.debug("Version {} is not supported with idpVersion {}", version, idPVersion);
-                continue;
-            }
-            log.debug("Version {} is supported with idpVersion {}", version, idPVersion);
-            if (pluginInfo.getUpdateURL(version) == null || pluginInfo.getUpdateBaseName(version) == null) {
-                log.debug("Version {} is does not have update information", version);
-                continue;
-            }
-            return version;
-        }
-        return null;
-    }
-
     /** Update the plugin.
      * @param pluginId the pluginId or null.
      * @param pluginVersion (optionally) the version to update to.
@@ -603,7 +507,7 @@ public final class PluginInstallerCLI extends AbstractIdPHomeAwareCommandLine<Pl
         }
         final PluginVersion installVersion;
         if (pluginVersion == null) {
-            installVersion = getBestVersion(new PluginVersion(plugin), state.getPluginInfo());
+            installVersion = PluginInstallerSupport.getBestVersion(new PluginVersion(plugin), state.getPluginInfo());
             if (installVersion == null) {
                 log.info("No suitable update version available");
                 return;
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java
index bb185e548..76ca64ff2 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java
@@ -18,25 +18,41 @@
 package net.shibboleth.idp.installer.plugin.impl;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.apache.hc.client5.http.classic.HttpClient;
 import org.apache.tools.ant.BuildException;
+import org.opensaml.security.httpclient.HttpClientSecurityContextHandler;
 import org.slf4j.Logger;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
 
+import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
 import net.shibboleth.idp.installer.InstallerSupport;
+import net.shibboleth.idp.installer.ProgressReportingOutputStream;
+import net.shibboleth.idp.installer.plugin.impl.PluginState.VersionInfo;
+import net.shibboleth.idp.plugin.PluginSupport.SupportLevel;
+import net.shibboleth.idp.plugin.PluginVersion;
 import net.shibboleth.shared.annotation.constraint.Live;
 import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.spring.httpclient.resource.HTTPResource;
 
 /**
  * Support for copying files during plugin manipulation.
@@ -222,4 +238,129 @@ public final class PluginInstallerSupport {
             return FileVisitResult.CONTINUE;
         }
     }
+
+    /** Find the best update version (plugin or IdP).
+     * @param pluginVersion The Plugin version
+     * @param pluginInfo all about the plugin
+     * @return the best version (or null)
+     */
+    @Nullable static public PluginVersion getBestVersion(@Nonnull final PluginVersion pluginVersion, @Nonnull final PluginInfo pluginInfo) {
+        return getBestVersion(PluginInstaller.getIdPVersion(), pluginVersion, pluginInfo);
+    }
+
+   /** Find the best update version  (plugin or IdP).
+     * @param idPVersion The IdP version to check.
+     * @param pluginVersion The Plugin version
+     * @param pluginInfo all about the plugin
+     * @return the best version (or null)
+     */
+    @Nullable static public PluginVersion getBestVersion(@Nonnull final PluginVersion idPVersion,
+            @Nonnull final PluginVersion pluginVersion, @Nonnull final PluginInfo pluginInfo) {
+
+        final List<PluginVersion> availableVersions = new ArrayList<>(pluginInfo.getAvailableVersions().keySet());
+        availableVersions.sort(null);
+        LOG.debug("Considering versions: {}", availableVersions);
+
+        for (int i = availableVersions.size()-1; i >= 0; i--) {
+            final PluginVersion version = availableVersions.get(i);
+            if (version.compareTo(pluginVersion) <= 0) {
+                LOG.debug("Version {} is less than or the same as {}. All done", version, pluginVersion);
+                return null;
+            }
+            final VersionInfo versionInfo = pluginInfo.getAvailableVersions().get(version);
+            if (versionInfo.getSupportLevel() != SupportLevel.Current) {
+                LOG.debug("Version {} has support level {}, ignoring", version, versionInfo.getSupportLevel());
+                continue;
+            }
+            if (!pluginInfo.isSupportedWithIdPVersion(version, idPVersion)) {
+                LOG.debug("Version {} is not supported with idpVersion {}", version, idPVersion);
+                continue;
+            }
+            LOG.debug("Version {} is supported with idpVersion {}", version, idPVersion);
+            if (pluginInfo.getUpdateURL(version) == null || pluginInfo.getUpdateBaseName(version) == null) {
+                LOG.debug("Version {} is does not have update information", version);
+                continue;
+            }
+            return version;
+        }
+        return null;
+    }
+
+    /** Load the property file describing all the plugin we know about from a known location.
+     * @param updateURLs where to look
+     * @param commandLine the programming calling us.
+     * @return the property files plugins.
+     */
+    @Nullable public static Properties loadPluginInfo(@Nonnull final List<URL> updateURLs,
+            @Nonnull final AbstractIdPHomeAwareCommandLine<?> commandLine) {
+        final List<URL> urls;
+        final Properties props = new Properties();
+        try {
+            if (updateURLs.isEmpty()) {
+                urls = List.of(
+                        new URL("https://shibboleth.net/downloads/identity-provider/plugins/plugins.properties"),
+                        new URL("http://plugins.shibboleth.net/plugins.properties"));
+            } else {
+                urls = updateURLs;
+            }
+        } catch (final IOException e) {
+            LOG.error("Could not load update URLs", e);
+            return null;
+        }
+        for (final URL url: urls) {
+            final Resource propertyResource;
+            try {
+                if ("file".equals(url.getProtocol())) {
+                    final String path =url.getPath();
+                    assert path != null;
+                    propertyResource = new FileSystemResource(path);
+                } else if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) {
+                    final HttpClient client = commandLine.getHttpClient();
+                    assert client != null;
+                    final HTTPResource httpResource;
+                    propertyResource = httpResource = new HTTPResource(client , url);
+                    final HttpClientSecurityContextHandler handler = new HttpClientSecurityContextHandler();
+                    handler.setHttpClientSecurityParameters(commandLine.getHttpClientSecurityParameters());
+                    handler.initialize();
+                    httpResource.setHttpClientContextHandler(handler);
+                } else {
+                    LOG.error("Only file and http[s] URLs are allowed");
+                    continue;
+                }
+                LOG.debug("Plugin Listing: Looking for update at {}", propertyResource.getDescription());
+                if (!propertyResource.exists()) {
+                    LOG.info("{} could not be located", propertyResource.getDescription());
+                    continue;
+                }
+                props.load(propertyResource.getInputStream());
+                return props;
+            } catch (final IOException | ComponentInitializationException e) {
+                LOG.error("Could not open Update URL {} :", url, e);
+                continue;
+            }
+        }
+        LOG.error("Could not locate any active update servers");
+        return null;
+    }
+
+    /** Download helper method.
+     * @param baseResource where to go for the file
+     * @param handler HttpClientSecurityContextHandler to use
+     * @param downloadDirectory where to download to
+     * @param fileName the file name
+     * @throws IOException as required
+     */
+    public static void download(@Nonnull final HTTPResource baseResource,
+            @Nonnull final HttpClientSecurityContextHandler handler,
+            @Nonnull final Path downloadDirectory,
+            @Nonnull final String fileName) throws IOException {
+        final HTTPResource httpResource = baseResource.createRelative(fileName, handler);
+        final Path filePath = downloadDirectory.resolve(fileName);
+        LOG.info("Downloading from {}", httpResource.getDescription());
+        LOG.debug("Downloading to {}", filePath);
+        try (final OutputStream fileOut = new ProgressReportingOutputStream(new FileOutputStream(filePath.toFile()))) {
+            httpResource.getInputStream().transferTo(fileOut);
+        }
+    }
+
 }
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
index f21384c32..93e960562 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
@@ -85,7 +85,9 @@ public class PluginState extends AbstractInitializableComponent {
     /** get our PluginInfo.
      * @return our PluginInfo.
      */
-    public PluginInfo getPluginInfo() {
+    @Nonnull public PluginInfo getPluginInfo() {
+        checkComponentActive();
+        assert myPluginInfo!=null;
         return myPluginInfo;
     }
     
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/TestInstallerCLI.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/TestInstallerCLI.java
index c80b36613..ff757e7ac 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/TestInstallerCLI.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/TestInstallerCLI.java
@@ -21,25 +21,68 @@ import org.testng.annotations.Test;
 
 import net.shibboleth.idp.installer.impl.IdPInstallerCLI;
 import net.shibboleth.idp.installer.impl.InstallerProperties;
+import net.shibboleth.idp.installer.impl.UpdateIdPCLI;
 /**
  *
  */
 public class TestInstallerCLI {
 
-    @Test(enabled = true)
+    @Test(enabled = false)
     public void install() {
 
         System.setProperty(InstallerProperties.KEY_STORE_PASSWORD, "p1");
         System.setProperty(InstallerProperties.SEALER_PASSWORD, "p1");
         System.setProperty(InstallerProperties.HOST_NAME, "machine.org.uk");
+        System.setProperty(InstallerProperties.TARGET_DIR,  "h:\\downloads\\idp");
 //        System.setProperty(InstallerProperties.SEALER_KEYSIZE, "256");
         IdPInstallerCLI.runMain(new String[] {
                 "-s",
                 "h:\\Perforce\\Juno\\V5\\java-identity-provider\\idp-distribution\\target\\shibboleth-identity-provider-5.0.0-SNAPSHOT",
                 "--home", "classpath:/net/shibboleth/idp/module",
-                "-t",
-                "h:\\downloads\\idp",
                 "-hc", "shibboleth.InternalHttpClient"
                });
     }
+
+    @Test(enabled = false)
+    public void updateList430() {
+        UpdateIdPCLI.runMain(new String[] {
+                "-l", 
+                "--pretendVersion","4.3.0",
+                "--updateURL", "file:C:\\Users\\rdw\\Desktop\\logs\\plugins.properties",
+                "--home", "H:\\Downloads\\idp"});
+    }
+
+    @Test(enabled = false)
+    public void updateList431() {
+        UpdateIdPCLI.runMain(new String[] {
+                "-l", 
+                "--pretendVersion","4.3.1",
+                "--updateURL", "file:C:\\Users\\rdw\\Desktop\\logs\\plugins.properties",
+                "--home", "H:\\Downloads\\idp"});
+    }
+
+    @Test(enabled = false)
+    public void check430() {
+        UpdateIdPCLI.runMain(new String[] {
+                "--pretendVersion","4.3.0",
+                "--updateURL", "file:C:\\Users\\rdw\\Desktop\\logs\\plugins.properties",
+                "--home", "H:\\Downloads\\idp"});
+    }
+
+    @Test(enabled = false)
+    public void check431() {
+        UpdateIdPCLI.runMain(new String[] {
+                "--pretendVersion","4.3.1",
+                "--updateURL", "file:C:\\Users\\rdw\\Desktop\\logs\\plugins.properties",
+                "--home", "H:\\Downloads\\idp"});
+    }
+
+    @Test(enabled = false)
+    public void download430() {
+        UpdateIdPCLI.runMain(new String[] {
+                "-d", "H:\\downloads\\idp", 
+                "--pretendVersion","4.3.0",
+                "--updateURL", "file:C:\\Users\\rdw\\Desktop\\logs\\plugins.properties",
+                "--home", "H:\\Downloads\\idp"});
+    }
 }
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginStateTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginStateTest.java
index 65f0569a7..021f01569 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginStateTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginStateTest.java
@@ -25,7 +25,6 @@ import static org.testng.Assert.fail;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.List;
 
 import javax.annotation.Nonnull;
 
@@ -112,7 +111,9 @@ public class PluginStateTest {
             @Override
             public @Nonnull java.util.List<URL> getUpdateURLs() {
                 try {
-                    return List.of(new URL("http://example.org/dir"), super.getUpdateURLs().get(0));
+                    final URL u = super.getUpdateURLs().get(0);
+                    assert u != null;
+                    return CollectionSupport.listOf(new URL("http://example.org/dir"), u);
                 } catch (final MalformedURLException e) {
                     fail(e.toString());
                     return super.getUpdateURLs();
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/RollbackTester.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/RollbackTester.java
index 41674b6fc..603df2b76 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/RollbackTester.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/RollbackTester.java
@@ -66,7 +66,10 @@ public class RollbackTester {
         final IdPModule enabled2 = new TestModule("enabled2", null, new ModuleException()); 
         final IdPModule disabled1 = new TestModule("disabled1", null, null);
         final IdPModule disabled2 = new TestModule("disablde2", new ModuleException(), null);
-        final ModuleContext ctx = new ModuleContext(mc.toString());
+        final String mcs = mc.toString();
+        final String parentString = parent.toString(); 
+        assert mcs!=null && parentString!=null;
+        final ModuleContext ctx = new ModuleContext(mcs);
 
         try {
             assertFalse(from.toFile().exists());
@@ -82,7 +85,7 @@ public class RollbackTester {
             assertFalse(disabled1.isEnabled(ctx));
             assertFalse(disabled2.isEnabled(ctx));
             
-            try (final RollbackPluginInstall rp = new RollbackPluginInstall(new ModuleContext(parent.toString()), new HashMap<>())) {
+            try (final RollbackPluginInstall rp = new RollbackPluginInstall(new ModuleContext(parentString), new HashMap<>())) {
                 rp.getFilesCopied().add(copied);
                 rp.getFilesRenamedAway().add(renamed);
                 rp.getModulesDisabled().add(disabled1);
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java
index 05979b9cf..e7923c56e 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java
@@ -30,6 +30,8 @@ import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.security.Security;
 
+import javax.annotation.Nonnull;
+
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
@@ -37,6 +39,7 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeTest;
 import net.shibboleth.shared.component.ComponentInitializationException;
 
 @SuppressWarnings("javadoc")
@@ -44,7 +47,7 @@ public class TrustStoreTest {
     
     private final static String pluginId = "net.shibboleth.plugin.test";
     
-    private Path dir;
+    @NonnullBeforeTest private Path dir;
     
     
     @BeforeClass public void setup() throws IOException {
@@ -76,12 +79,18 @@ public class TrustStoreTest {
         Files.deleteIfExists(dir.resolve("credentials").resolve(pluginId).resolve("truststore.asc"));
     }
 
+    @Nonnull private Path getDir() {
+        assert dir != null;
+        return dir;
+    }
+
     @Test public void signatureAbsentTest() throws ComponentInitializationException, IOException {
         final TrustStore ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setPluginId(pluginId);
         ts.initialize();
         try (InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+            assert sigStream!=null;
             final Signature signature = TrustStore.signatureOf(sigStream);
             assertFalse(ts.contains(signature));
         }
@@ -97,10 +106,11 @@ public class TrustStoreTest {
     @Test public void signaturePresentTest() throws ComponentInitializationException, IOException {
         populateKeyStore();
         final TrustStore ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setPluginId(pluginId);
         ts.initialize();
-        try( final InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+        try(final InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+            assert sigStream!=null;
             final Signature signature = TrustStore.signatureOf(sigStream);
             assertTrue(ts.contains(signature));
         }        
@@ -109,7 +119,7 @@ public class TrustStoreTest {
     @Test public void signingTest()  throws ComponentInitializationException, IOException {
         populateKeyStore();
         final TrustStore ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setPluginId(pluginId);
         ts.initialize();
         try( final InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc");
@@ -117,7 +127,8 @@ public class TrustStoreTest {
                final InputStream dataStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico");
                final InputStream dataStream2 = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico")) {
 
-            Signature badSig = TrustStore.signatureOf(badSigStream);
+            assert badSigStream!= null && sigStream != null;
+            final Signature badSig = TrustStore.signatureOf(badSigStream);
             assertTrue(ts.contains(badSig));
             assertFalse(ts.checkSignature(dataStream, badSig));
             assertTrue(ts.checkSignature(dataStream2, TrustStore.signatureOf(sigStream)));
@@ -128,23 +139,24 @@ public class TrustStoreTest {
         populateKeyStore();
         final Signature signature;
         try( final InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+            assert sigStream!=null;
             signature = TrustStore.signatureOf(sigStream);
         }
         TrustStore ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setPluginId(pluginId);
         ts.initialize();
         assertTrue(ts.contains(signature));
         ts.saveStore();
 
         ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setPluginId(pluginId);
         ts.initialize();
         assertTrue(ts.contains(signature));
 
         ts = new TrustStore();
-        ts.setIdpHome(dir);
+        ts.setIdpHome(getDir());
         ts.setTrustStore(dir.resolve("credentials").resolve(pluginId).resolve("truststore.asc").toString());
         ts.setPluginId(pluginId);
         ts.initialize();

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


More information about the commits mailing list