Skip to content

Commit 9a73f90

Browse files
authored
[MGPG-105] Make possible backward compatibility (#74)
Back out a bit from original MGPG-105 PR that _fails_ if best practices not followed. Introduce a "bestPractice" boolean configuration flag. By default, plugin enforces best practice (= `true`), and will refuse to operate if best practices violated. Still, user can explicitly configure value `false`, when plugin regains "old way" (unsecure) mode of secret configuration, but plugin will warn. --- https://issues.apache.org/jira/browse/MGPG-105
1 parent 6a94f3a commit 9a73f90

File tree

6 files changed

+183
-6
lines changed

6 files changed

+183
-6
lines changed

pgp-keys-map.list

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ org.junit.platform:junit-platform-commons = 0xFF6E2C001948C5F2F38B0CC385911F425E
2828
org.opentest4j:opentest4j = 0xFF6E2C001948C5F2F38B0CC385911F425EC61B51
2929
org.apache.maven.resolver = 0x522CA055B326A636D833EF6A0551FD3684FCBBB7
3030
org.apache.maven.shared:maven-invoker = 0x84789D24DF77A32433CE1F079EB80E92EB2135B1
31+
org.codehaus.plexus:plexus-cipher = 0x6A814B1F869C2BBEAB7CB7271A2A1C94BDE89688
3132
org.codehaus.plexus:plexus-classworlds = 0xB91AB7D2121DC6B0A61AA182D7742D58455ECC7C
3233
org.codehaus.plexus:plexus-component-annotations = 0xFA77DCFEF2EE6EB2DEBEDD2C012579464D01C06A
3334
org.codehaus.plexus:plexus-utils = 0xF254B35617DC255D9344BCFA873A8E86B4372146
35+
org.codehaus.plexus:plexus-sec-dispatcher = 0x2BE13D052E9AA567D657D9791FD507154FB9BA39

pom.xml

+22
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,28 @@ under the License.
137137
<version>2.9.0</version>
138138
<type>pom</type>
139139
</dependency>
140+
<dependency>
141+
<groupId>org.codehaus.plexus</groupId>
142+
<artifactId>plexus-sec-dispatcher</artifactId>
143+
<version>2.0</version>
144+
<exclusions>
145+
<exclusion>
146+
<groupId>*</groupId>
147+
<artifactId>*</artifactId>
148+
</exclusion>
149+
</exclusions>
150+
</dependency>
151+
<dependency>
152+
<groupId>org.codehaus.plexus</groupId>
153+
<artifactId>plexus-cipher</artifactId>
154+
<version>2.0</version>
155+
<exclusions>
156+
<exclusion>
157+
<groupId>*</groupId>
158+
<artifactId>*</artifactId>
159+
</exclusion>
160+
</exclusions>
161+
</dependency>
140162

141163
<dependency>
142164
<groupId>org.junit.jupiter</groupId>

src/main/java/org/apache/maven/plugins/gpg/AbstractGpgMojo.java

+128-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
import org.apache.maven.plugin.MojoFailureException;
2828
import org.apache.maven.plugins.annotations.Component;
2929
import org.apache.maven.plugins.annotations.Parameter;
30+
import org.apache.maven.project.MavenProject;
31+
import org.apache.maven.settings.Server;
32+
import org.apache.maven.settings.Settings;
33+
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
34+
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
3035

3136
/**
3237
* @author Benjamin Bentmann
@@ -248,14 +253,47 @@ public abstract class AbstractGpgMojo extends AbstractMojo {
248253
@Component
249254
protected MavenSession session;
250255

256+
// === Deprecated stuff
257+
258+
/**
259+
* Switch to lax plugin enforcement of "best practices". If set to {@code false}, plugin will retain all the
260+
* backward compatibility regarding getting secrets (but will warn). By default, plugin enforces "best practices"
261+
* and in such cases plugin fails.
262+
*
263+
* @since 3.2.0
264+
* @deprecated
265+
*/
266+
@Deprecated
267+
@Parameter(property = "gpg.bestPractices", defaultValue = "true")
268+
private boolean bestPractices;
269+
270+
/**
271+
* Current user system settings for use in Maven.
272+
*
273+
* @since 1.6
274+
* @deprecated
275+
*/
276+
@Deprecated
277+
@Parameter(defaultValue = "${settings}", readonly = true, required = true)
278+
private Settings settings;
279+
280+
/**
281+
* Maven Security Dispatcher.
282+
*
283+
* @since 1.6
284+
* @deprecated
285+
*/
286+
@Deprecated
287+
@Component
288+
private SecDispatcher secDispatcher;
289+
251290
@Override
252291
public final void execute() throws MojoExecutionException, MojoFailureException {
253292
if (skip) {
254293
// We're skipping the signing stuff
255294
return;
256295
}
257-
if ((passphrase != null && !passphrase.trim().isEmpty())
258-
|| (passphraseServerId != null && !passphraseServerId.trim().isEmpty())) {
296+
if (bestPractices && (isNotBlank(passphrase) || isNotBlank(passphraseServerId))) {
259297
// Stop propagating worst practices: passphrase MUST NOT be in any file on disk
260298
throw new MojoFailureException(
261299
"Do not store passphrase in any file (disk or SCM repository), rely on GnuPG agent or provide passphrase in "
@@ -267,7 +305,19 @@ public final void execute() throws MojoExecutionException, MojoFailureException
267305

268306
protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;
269307

270-
protected AbstractGpgSigner newSigner() throws MojoFailureException {
308+
private void logBestPracticeWarning(String source) {
309+
getLog().warn("");
310+
getLog().warn("W A R N I N G");
311+
getLog().warn("");
312+
getLog().warn("Do not store passphrase in any file (disk or SCM repository),");
313+
getLog().warn("instead rely on GnuPG agent in interactive sessions, or provide passphrase in ");
314+
getLog().warn(passphraseEnvName + " environment variable for batch mode.");
315+
getLog().warn("");
316+
getLog().warn("Sensitive content loaded from " + source);
317+
getLog().warn("");
318+
}
319+
320+
protected AbstractGpgSigner newSigner(MavenProject mavenProject) throws MojoFailureException {
271321
AbstractGpgSigner signer;
272322
if (GpgSigner.NAME.equals(this.signer)) {
273323
signer = new GpgSigner(executable);
@@ -294,10 +344,32 @@ protected AbstractGpgSigner newSigner() throws MojoFailureException {
294344
signer.setLockMode(lockMode);
295345
signer.setArgs(gpgArguments);
296346

347+
// "new way": env prevails
297348
String passphrase =
298349
(String) session.getRepositorySession().getConfigProperties().get("env." + passphraseEnvName);
299-
if (passphrase != null) {
350+
if (isNotBlank(passphrase)) {
300351
signer.setPassPhrase(passphrase);
352+
} else if (!bestPractices) {
353+
// "old way": mojo config
354+
passphrase = this.passphrase;
355+
if (isNotBlank(passphrase)) {
356+
logBestPracticeWarning("Mojo configuration");
357+
signer.setPassPhrase(passphrase);
358+
} else {
359+
// "old way": serverId + settings
360+
passphrase = loadGpgPassphrase();
361+
if (isNotBlank(passphrase)) {
362+
logBestPracticeWarning("settings.xml");
363+
signer.setPassPhrase(passphrase);
364+
} else {
365+
// "old way": project properties
366+
passphrase = getPassphrase(mavenProject);
367+
if (isNotBlank(passphrase)) {
368+
logBestPracticeWarning("Project properties");
369+
signer.setPassPhrase(passphrase);
370+
}
371+
}
372+
}
301373
}
302374

303375
// gpg signer: always failed if no passphrase and no agent and not interactive: retain this behavior
@@ -310,4 +382,56 @@ protected AbstractGpgSigner newSigner() throws MojoFailureException {
310382

311383
return signer;
312384
}
385+
386+
private boolean isNotBlank(String string) {
387+
return string != null && !string.trim().isEmpty();
388+
}
389+
390+
// Below is attic, to be thrown out
391+
392+
@Deprecated
393+
private static final String GPG_PASSPHRASE = "gpg.passphrase";
394+
395+
@Deprecated
396+
private String loadGpgPassphrase() throws MojoFailureException {
397+
if (isNotBlank(passphrase)) {
398+
Server server = settings.getServer(passphraseServerId);
399+
if (server != null) {
400+
if (isNotBlank(server.getPassphrase())) {
401+
try {
402+
return secDispatcher.decrypt(server.getPassphrase());
403+
} catch (SecDispatcherException e) {
404+
throw new MojoFailureException("Unable to decrypt gpg passphrase", e);
405+
}
406+
}
407+
}
408+
}
409+
return null;
410+
}
411+
412+
@Deprecated
413+
public String getPassphrase(MavenProject project) {
414+
String pass = null;
415+
if (project != null) {
416+
pass = project.getProperties().getProperty(GPG_PASSPHRASE);
417+
if (pass == null) {
418+
MavenProject prj2 = findReactorProject(project);
419+
pass = prj2.getProperties().getProperty(GPG_PASSPHRASE);
420+
}
421+
}
422+
if (project != null) {
423+
findReactorProject(project).getProperties().setProperty(GPG_PASSPHRASE, pass);
424+
}
425+
return pass;
426+
}
427+
428+
@Deprecated
429+
private MavenProject findReactorProject(MavenProject prj) {
430+
if (prj.getParent() != null
431+
&& prj.getParent().getBasedir() != null
432+
&& prj.getParent().getBasedir().exists()) {
433+
return findReactorProject(prj.getParent());
434+
}
435+
return prj;
436+
}
313437
}

src/main/java/org/apache/maven/plugins/gpg/GpgSignAttachedMojo.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException {
8282
// Sign collected files and attach all the signatures
8383
// ----------------------------------------------------------------------------
8484

85-
AbstractGpgSigner signer = newSigner();
85+
AbstractGpgSigner signer = newSigner(project);
8686
signer.setOutputDirectory(ascDirectory);
8787
signer.setBuildDirectory(new File(project.getBuild().getDirectory()));
8888
signer.setBaseDirectory(project.getBasedir());

src/main/java/org/apache/maven/plugins/gpg/SignAndDeployFileMojo.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException {
349349
}
350350

351351
// sign all
352-
AbstractGpgSigner signer = newSigner();
352+
AbstractGpgSigner signer = newSigner(null);
353353
signer.setOutputDirectory(ascDirectory);
354354
signer.setBaseDirectory(new File("").getAbsoluteFile());
355355

src/test/java/org/apache/maven/plugins/gpg/it/GpgSignArtifactIT.java

+29
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Collection;
2424

2525
import org.apache.maven.shared.invoker.InvocationRequest;
26+
import org.junit.jupiter.api.Test;
2627
import org.junit.jupiter.params.ParameterizedTest;
2728
import org.junit.jupiter.params.provider.MethodSource;
2829

@@ -80,4 +81,32 @@ void testPlacementOfArtifactInOutputDirectory(String pomPath, String expectedFil
8081
Arrays.sort(expectedFiles);
8182
assertEquals(Arrays.toString(expectedFiles), Arrays.toString(outputFiles));
8283
}
84+
85+
@Test
86+
void testWorstPracticesStillWork() throws Exception {
87+
// given
88+
final File pomFile = InvokerTestUtils.getTestResource("/it/sign-release-in-same-dir/pom.xml");
89+
final InvocationRequest request =
90+
InvokerTestUtils.createRequest(pomFile, mavenUserSettings, gpgHome, "gpg", false);
91+
request.addArg("-Dgpg.bestPractices=false");
92+
request.addArg("-Dgpg.passphrase=TEST");
93+
94+
final File integrationTestRootDirectory = new File(pomFile.getParent());
95+
final File expectedOutputDirectory = new File(integrationTestRootDirectory + "/target/tarballs/");
96+
97+
// when
98+
InvokerTestUtils.executeRequest(request, mavenHome, localRepository);
99+
100+
// then
101+
assertTrue(expectedOutputDirectory.isDirectory());
102+
103+
String[] outputFiles = expectedOutputDirectory.list();
104+
assertNotNull(outputFiles);
105+
106+
String[] expectedFiles =
107+
new String[] {"sign-release-in-same-dir-1.0.jar", "sign-release-in-same-dir-1.0.jar.asc"};
108+
Arrays.sort(outputFiles);
109+
Arrays.sort(expectedFiles);
110+
assertEquals(Arrays.toString(expectedFiles), Arrays.toString(outputFiles));
111+
}
83112
}

0 commit comments

Comments
 (0)