[java-mvn-enforcer] 02/03: Use an external maven jar file as the source of enforcer data

Rod Widdowson rdw at steadingsoftware.com
Mon Nov 15 13:45:29 UTC 2021


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

rdw pushed a commit to branch main
in repository java-mvn-enforcer.

View the commit online:
http://git.shibboleth.net/view/?p=java-mvn-enforcer.git;a=commit;h=549152dcad00a1b00e1047f38b9b47545dfd426c

commit 549152dcad00a1b00e1047f38b9b47545dfd426c
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sat Nov 13 15:34:43 2021 +0000

    Use an external maven jar file as the source of enforcer data
    
    resolve it, check its signature, addit to a classpath and
    lookup the keyrings (and asc files).
---
 pom.xml                                            |   2 +-
 .../mvn/enforcer/impl/BaseSigChecker.java          |  22 ++--
 .../shibboleth/mvn/enforcer/impl/GPGKeyRing.java   |  70 ++++++++----
 .../shibboleth/mvn/enforcer/impl/JarEnforcer.java  | 123 ++++++++++++++++-----
 .../mvn/enforcer/impl/ProjectPomContext.java       |  18 +--
 5 files changed, 170 insertions(+), 65 deletions(-)

diff --git a/pom.xml b/pom.xml
index dd7e5cd..bf97bc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,7 +15,7 @@
     <groupId>net.shibboleth.maven.enforcer.rules</groupId>
     <artifactId>maven-dist-enforcer</artifactId>
     <name>Shibboleth Distribution Enforcer Rule</name>
-    <version>2.1.1-SNAPSHOT</version>
+    <version>3.0.0-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <properties>
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
index f306b70..d8a02b5 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
@@ -159,19 +159,19 @@ public class BaseSigChecker {
         }
         if (path == null || !Files.exists(path)) {
             log.info("Could not find key for {}, trying local store.", artifact);
-            try (final InputStream stream =
-                    new BufferedInputStream(new FileInputStream(
-                            projectContext.getEnforcerDir()
-                                          .resolve("localSignatures")
-                                          .resolve( artifact.toString() + ".jar.asc")
-                                          .toFile()))) {
+            final String name = ProjectPomContext.CLASSPATH_ROOT +  "localSignatures/" + artifact.toString() + ".jar.asc";
+            try (final InputStream stream = getProjectContext().getEnforcerLoader().getResourceAsStream(name)) {
+                if (stream == null) {
+                    log.error("Key for {} no found in classpath store", artifact);
+                    return null;
+                }
                 final Signature sig =  GPGKeyRing.signatureOf(stream);
-                report.format("%-30s: %-14s Signature not available.  Loaded from local store\n",
+                report.format("%-30s: %-14s Signature not available.  Loaded from classpath store\n",
                         artifact.getArtifactId(), artifact.getVersion());
-                log.info("Key for {} found in local store.", artifact);
+                log.info("Key for {} found in classpath store.", artifact);
                 return sig;
-            } catch (final IOException e) {
-                log.error("Could not load key from local store:", e);
+            } catch (final Throwable e) {
+                log.error("Could not load key from classpath store:", e);
                 return null;
             }
         }
@@ -197,7 +197,7 @@ public class BaseSigChecker {
         }
 
         try  {
-            final GPGKeyRing store = new GPGKeyRing(projectContext.getEnforcerDir().resolve("keyRings"), group);
+            final GPGKeyRing store = new GPGKeyRing(projectContext.getEnforcerLoader(), group);
             keyRings.put(group,  Optional.of(store));
             return store;
         } catch (final Exception e) {
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/GPGKeyRing.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/GPGKeyRing.java
index 90e199f..a47419e 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/GPGKeyRing.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/GPGKeyRing.java
@@ -18,12 +18,14 @@
 package net.shibboleth.mvn.enforcer.impl;
 
 import java.io.BufferedInputStream;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.security.Security;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -33,6 +35,8 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openpgp.PGPException;
 import org.bouncycastle.openpgp.PGPObjectFactory;
 import org.bouncycastle.openpgp.PGPPublicKey;
@@ -62,33 +66,61 @@ import org.slf4j.Logger;
     @Nonnull private final PGPPublicKeyRingCollection keyRings;
     
     /** Constructor.
-     * Locate and load the keyring for the provided group,  First look for the keyring
+     * Locate and load the keyring for the provided file.
+     * @param file where the keyring is held
+     * @throws Exception under various error conditions.
+     */
+    public GPGKeyRing(final File file) throws Exception {
+        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+        log.debug("Loading keyring for {}", file);
+
+        try (final InputStream keyRingStream = new BufferedInputStream(new FileInputStream(file))) {
+            keyRings = new PGPPublicKeyRingCollection(keyRingStream, new JcaKeyFingerprintCalculator());
+        }
+    }
+    
+    /** Constructor.
+     * Locate and load the keyring for the provided group via the classpath,  First look for the keyring
      * and then for an asc file.
-     * @param dir where the keyrings are held
+     * @param loader to the jar file with the keyrings
      * @param group the group to look for
      * @throws Exception under various error conditions.
      */
-    public GPGKeyRing(final Path dir, final String group) throws Exception {
-        final Path armoredPath = dir.resolve(group);
-        final Path keyringPath = dir.resolve(group +".gpg");
-        if (Files.exists(keyringPath)) {
-            log.debug("Loading keyring for {}", group);
-            try (final InputStream keyRingStream =
-                    new BufferedInputStream(new FileInputStream(keyringPath.toFile()))) {
-                keyRings = new PGPPublicKeyRingCollection(keyRingStream, new JcaKeyFingerprintCalculator());
-            }
-        } else if (Files.exists(armoredPath)) {
-            log.debug("Loading asci keys for {}", group);
-            try (final InputStream armoredStream =
-                    new BufferedInputStream(new FileInputStream(armoredPath.toFile()))) {
-                keyRings = loadRingFromAsc(armoredStream);
+    public GPGKeyRing(final ClassLoader loader, final String group) throws Exception {
+        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+        final String armoredPath = ProjectPomContext.CLASSPATH_ROOT + "keyRings/" + group;
+        final String keyringPath = armoredPath +".gpg";
+        try (final InputStream gpgFromLoader = loader.getResourceAsStream(keyringPath);
+            final InputStream gpgFromStandard = getClass().getResourceAsStream(keyringPath);
+            final InputStream ascFromLoader = loader.getResourceAsStream(armoredPath);
+            final InputStream ascFromStandard = getClass().getResourceAsStream(armoredPath);) {
+            if (gpgFromLoader != null) {
+                if (gpgFromStandard != null) {
+                    log.error("Found keyring for {} on standard path.", group);
+                    throw new EnforcerRuleException("Found keyring on standard classpath");
+                }
+                log.debug("Loading keyring for {} from classloader.", group);
+
+                keyRings = new PGPPublicKeyRingCollection(gpgFromLoader, new JcaKeyFingerprintCalculator());
+            } else if (ascFromLoader != null) {
+                if (ascFromStandard != null) {
+                    log.error("Found asc for {} on standard path.", group);
+                    throw new EnforcerRuleException("Found keyring on standard classpath");
+                }
+                log.debug("Loading asci keys for {} from classloader.", group);
+                keyRings = loadRingFromAsc(ascFromLoader);
+            } else {
+                log.warn("No keyring or asc file found for {}", group);
+                throw new FileNotFoundException("Could not locate keyring");
             }
-        } else {
-            log.warn("No keyring or asc file found for {}", group);
-            throw new FileNotFoundException("Could not locate keyring");
         }
     }
 
+
     /** Return a store loaded from the supplied stream.
      *
      * @param in the stream
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/JarEnforcer.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/JarEnforcer.java
index 0e2ab75..7e15c6a 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/JarEnforcer.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/JarEnforcer.java
@@ -16,17 +16,24 @@
  */
 package net.shibboleth.mvn.enforcer.impl;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.factory.ArtifactFactory;
 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
@@ -39,6 +46,7 @@ import org.apache.maven.execution.MavenSession;
 import org.apache.maven.project.MavenProject;
 import org.slf4j.Logger;
 
+import net.shibboleth.mvn.enforcer.impl.GPGKeyRing.Signature;
 import net.shibboleth.mvn.enforcer.impl.ParsedPom.PomArtifact;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 import net.shibboleth.utilities.java.support.xml.BasicParserPool;
@@ -50,34 +58,37 @@ import net.shibboleth.utilities.java.support.xml.XMLConstants;
 @SuppressWarnings("deprecation")
 public class JarEnforcer implements EnforcerRule, MavenLoader{
 
+    /* 
+     * Parameters added to the pom.
+     */
+    
     /** Relative path of the parent pom.*/
     private String parentPomDir=".";
-
     /** Space separated list of locations to look for jars in. */
     private String jarDirs="";
-
-    /** Where to get external data. */
-    private String enforcerData = "";
-
+    /** Where to get external data. Group */
+    private String dataGroupId = "";
+    /** Where to get external data. ArtifactName*/
+    private String dataArtifactId = "";
+    /** Where to get external data. Version*/
+    private String dataVersion = "";
+    /** The key rings for the data .*/
+    private String dataKeyRing;
     /** Where to get the mapping of artifact to group. */
     private String artifactMap = "";
-
     /** Will we check that all jars we distribute jars have valid signatures? */
     private boolean checkSignatures = true;
-
     /** Will we check that the jars we distribute are versions we expected? */
     private boolean checkDependencies = true;
-
     /** Do we want to find out which jar files were included on behalf of which
      * pom-defined artifacts (quite slow).
      *
      *  Requires that {@link #checkDependencies} be true.
      */
     private boolean listJarSources;
-
     /** Will we check that all jars in ~/.m2/... jars have valid signatures? */
     private boolean checkM2;
-
+    
     /** Our artifact factory.  This is deprecated but there seems no easy way to create one.
      * (No replacement is suggested and the best code out there creates a pom file and parses it.
      * Really?
@@ -114,6 +125,10 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
             throw new EnforcerRuleException("No <jarsDirs/> provided");
         }
         try {
+            org.apache.maven.repository.RepositorySystem rp = helper.getComponent(org.apache.maven.repository.RepositorySystem.class);
+            if (rp != null) {
+                log.error("Non Null repo system!");
+            }
             artifactFactory = helper.getComponent(ArtifactFactory.class);
             artifactResolver = helper.getComponent(ArtifactResolver.class);
             session = (MavenSession) helper.evaluate( "${session}" );
@@ -137,7 +152,7 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
             pool.initialize();
 
             try (final ProjectPomContext pomContext = new ProjectPomContext(this,
-                EnforcerLogger.getLogger(ProjectPomContext.class), pool, Path.of(enforcerData), tmp, map)) {
+                EnforcerLogger.getLogger(ProjectPomContext.class), pool, getGPGDataClassLoader(), tmp, map)) {
 
                 pomContext.initialize(pom);
 
@@ -165,6 +180,46 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
         }
     }
 
+    /** Grab a ClassLoader for the data file.  Eventually this will do a GPG test.
+     * @return A class loader built from the downloaded artifact
+     * @throws Exception upon an error
+     */
+    private ClassLoader getGPGDataClassLoader() throws Exception {
+
+        // Only do this if we are sig checking
+        if (!checkSignatures) {
+            return null;
+        }
+
+        final File jar = downloadArtifact(dataGroupId, dataArtifactId, dataVersion, "jar");
+        if (jar == null || !jar.exists()) {
+            log.error("Could not locate data artifact {}:{}:{}", dataGroupId, dataArtifactId, dataVersion);
+            throw new FileNotFoundException("Could not locate data artifact");
+        }
+        final File asc = downloadArtifact(dataGroupId, dataArtifactId, dataVersion, "jar.asc");
+        if (jar == null || !jar.exists()) {
+            log.error("Could not locate data artifact signature for {}:{}:{}", dataGroupId, dataArtifactId, dataVersion);
+            throw new FileNotFoundException("Could not locate data artifact signature");
+        }
+        final File keyRingFile = new File (dataKeyRing);
+        if (!keyRingFile.exists()) {
+            log.error("KeyRing {} not found", keyRingFile);
+            throw new FileNotFoundException(dataKeyRing);
+        }
+        final GPGKeyRing keyRing = new GPGKeyRing(keyRingFile);
+        try (final InputStream ascStream = new BufferedInputStream(new FileInputStream(asc));
+             final InputStream jarStream = new BufferedInputStream(new FileInputStream(jar))) {
+
+            final Signature sig = new Signature(ascStream);
+            if (!keyRing.checkSignature(jarStream, sig)) {
+                log.error("Signature check on data artifact {}:{}:{}:{} failed", dataGroupId, dataArtifactId, dataVersion);
+                throw new EnforcerRuleException("Signature check on data artifact failed");
+            }
+        }
+        final URL url[] = {jar.toURI().toURL()};
+        return new URLClassLoader(url);
+    }
+
     /** Do the m2 signature checks
      * @param pomContext Context for the work
      * @param target Target Directory of this project
@@ -174,17 +229,18 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
     private boolean performM2Check(final ProjectPomContext pomContext, final Path target) throws Exception {
         boolean m2Result = true;
         if (checkM2) {
-            // cannot use our artifacts because sometimes we get the in source one
-            final PomArtifact artifact = pomContext.getParentPom().new
-                  PomArtifact("org.opensaml", "opensaml-parent", "4.1.0");
-            final Path resolvedPom = downloadArtifact(artifact, "pom");
-            // Resolved pm path is <pathTpM2Repo>/group1/group2/..../artifact/version/pomfilename
+            final String group = "org.opensaml";
+            final String id = "opensaml-parent";
+            final String version = "4.1.0";
+
+            final Path resolvedPom = downloadArtifact(group, id, version, "pom").toPath();
+            // Resolved pom path is <pathTpM2Repo>/group1/group2/..../artifact/version/pomfilename
             log.debug("Resolved Pom = {}", resolvedPom);
             Path root = resolvedPom.getParent().getParent().getParent(); // strip version, artifact
-            int index = artifact.getGroupId().indexOf('.');
+            int index = group.indexOf('.');
             while (index > 0) {
                 root = root.getParent();
-                index = artifact.getGroupId().indexOf('.', index+1);
+                index = group.indexOf('.', index+1);
             }
             root = root.getParent();
             log.info("Inferred M2 Root at ", root);
@@ -266,13 +322,19 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
         return false;
     }
 
-    /** {@inheritDoc} */
-    public Path downloadArtifact(final PomArtifact artifact, final String type) throws Exception {
-        final Artifact mavenArtifact = artifactFactory.createArtifact(artifact.getGroupId(),
-                artifact.getArtifactId(), artifact.getVersion(), "", type);
+    /** Helper function to download an artitfact.
+     * @param groupId Group Id
+     * @param artifactId Artifact Id
+     * @param version Version
+     * @param type type
+     * @return a file or null i
+     * @throws Exception
+     */
+    @Nullable public File downloadArtifact(final String groupId, final String artifactId, final String version, final String type) throws Exception {
+        final Artifact mavenArtifact = artifactFactory.createArtifact(groupId,artifactId, version, "", type);
 
         if (mavenArtifact == null) {
-            log.error("Could not create {} : {} ", artifact, type);
+            log.error("Could not create {}:{}:{}:{}", groupId, artifactId, version, type);
         } else {
             final ArtifactResolutionRequest request = new ArtifactResolutionRequest()
                 .setArtifact( mavenArtifact )
@@ -281,11 +343,20 @@ public class JarEnforcer implements EnforcerRule, MavenLoader{
                 .setRemoteRepositories( project.getRemoteArtifactRepositories());
             final ArtifactResolutionResult result = artifactResolver.resolve(request);
             if (result.isSuccess()) {
-                log.debug("Resolved OK : {} : ", artifact, type);
-                return mavenArtifact.getFile().toPath();
+                log.debug("Resolved OK {}:{}:{}:{}", groupId, artifactId, version, type);
+                return mavenArtifact.getFile();
             }
-            log.info("Could not resolve {} : {} ", artifact, type);
+            log.info("Could not resolve " + groupId +":" + artifactId + ":" + version + ":" + type);
         }
         return null;
     }
+
+    /** {@inheritDoc} */
+    public Path downloadArtifact(final PomArtifact artifact, final String type) throws Exception {
+        final File file = downloadArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), type);
+        if (file == null) {
+            return null;
+        }
+        return file.toPath();
+    }
 }
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
index 9fba5eb..90714e3 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
@@ -47,6 +47,9 @@ import net.shibboleth.utilities.java.support.xml.ParserPool;
  */
 public final class ProjectPomContext implements AutoCloseable {
 
+    /** Where the keyrings live when on the classpath. */
+    public final static String CLASSPATH_ROOT = "net/shibboleth/mvn/enforcer/";
+
     /** A list of things which get added to real versions. */
     private static final List<String> EXTENSION_GARNISH = 
         List.of("-SNAPSHOT", "-GA", "-jre", "-empty-to-avoid-conflict-with-guava");
@@ -61,7 +64,7 @@ public final class ProjectPomContext implements AutoCloseable {
     private final Path workingDir;
 
     /** Source of extra data (like signatures. */
-    private final Path enforcerDir;
+    private final ClassLoader enforcerLoader;
 
     /** The parsed idp-parent pom. */
     private ParsedPom parentPom;
@@ -81,26 +84,25 @@ public final class ProjectPomContext implements AutoCloseable {
     /** If non-null the path to a  {@link Properties} file which maps artifacts to groups */
     private Path artifactMap;
 
-
     /** Constructor.
      * @param loader how to get artifacts
      * @param logger where to log or null if we are using out own.
      * @param pool a parser pool
-     * @param srcDir source for extra info (like keyrings)
+     * @param classLoader source for extra info (like keyrings)
      * @param tmpDir where to put stuff.
      * @param mapFile artifact to group mapping {@link Properties} file
      */
     public ProjectPomContext(final MavenLoader loader,
             @Nonnull final Logger logger,
             @Nonnull final ParserPool pool,
-            @Nonnull final Path srcDir,
+            @Nonnull final ClassLoader classLoader,
             @Nonnull final Path tmpDir,
             @Nonnull final Path mapFile) {
         mavenLoader = Constraint.isNotNull(loader, "Loader must not be null");
         log = Constraint.isNotNull(logger, "Logger must not be null");
         workingDir = Constraint.isNotNull(tmpDir, "Working dir must not be null");
         Constraint.isTrue(Files.exists(tmpDir), "Working dir must exist");
-        enforcerDir = srcDir;
+        enforcerLoader = Constraint.isNotNull(classLoader, "Class Loader must not be null");;
         Constraint.isTrue(Files.exists(tmpDir), "Enforcer dir must exist");
         parserPool = Constraint.isNotNull(pool, "Parse Pool must not be null");
         artifactMap = mapFile;
@@ -120,11 +122,11 @@ public final class ProjectPomContext implements AutoCloseable {
         return workingDir;
     }
     
-    /** Rteunr where to get data from.
+    /** Return where to get data from.
      * @return Returns the parent path of all externmal data.
      */
-    @Nonnull public Path getEnforcerDir() {
-        return enforcerDir;
+    @Nonnull public ClassLoader getEnforcerLoader() {
+        return enforcerLoader;
     }
 
     /** lookup the group for the provided artifact Id.

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


More information about the commits mailing list