[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