You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2013/10/19 14:46:22 UTC

svn commit: r1533747 - in /maven/release/trunk/maven-release-manager/src: main/java/org/apache/maven/shared/release/exec/ test/java/org/apache/maven/shared/release/exec/

Author: rfscholte
Date: Sat Oct 19 12:46:22 2013
New Revision: 1533747

URL: http://svn.apache.org/r1533747
Log:
[MRELEASE-766] release:prepare stores settings.xml in a public directory 
encrypt all passwords and passphrases of the settings.xml

Modified:
    maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/AbstractMavenExecutor.java
    maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/ForkedMavenExecutor.java
    maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java
    maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/ForkedMavenExecutorTest.java
    maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/InvokerMavenExecutorTest.java

Modified: maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/AbstractMavenExecutor.java
URL: http://svn.apache.org/viewvc/maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/AbstractMavenExecutor.java?rev=1533747&r1=1533746&r2=1533747&view=diff
==============================================================================
--- maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/AbstractMavenExecutor.java (original)
+++ maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/AbstractMavenExecutor.java Sat Oct 19 12:46:22 2013
@@ -19,16 +19,28 @@ package org.apache.maven.shared.release.
  * under the License.
  */
 
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.SettingsUtils;
+import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
 import org.apache.maven.shared.release.ReleaseResult;
 import org.apache.maven.shared.release.env.DefaultReleaseEnvironment;
 import org.apache.maven.shared.release.env.ReleaseEnvironment;
 import org.codehaus.plexus.logging.LogEnabled;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.StringUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
+import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
+import org.sonatype.plexus.components.cipher.PlexusCipher;
+import org.sonatype.plexus.components.cipher.PlexusCipherException;
+import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
+import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
+import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
+import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
 
 public abstract class AbstractMavenExecutor
     implements MavenExecutor, LogEnabled
@@ -36,6 +48,19 @@ public abstract class AbstractMavenExecu
 
     private Logger logger;
 
+    /**
+     * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
+     * used instead.
+     * 
+     * @plexus.requirement role="org.sonatype.plexus.components.sec.dispatcher.SecDispatcher" role-hint="mng-4384"
+     */
+    private DefaultSecDispatcher secDispatcher;
+
+    /**
+     * @plexus.requirement
+     */
+    private PlexusCipher cipher;
+    
     protected AbstractMavenExecutor()
     {
     }
@@ -100,4 +125,121 @@ public abstract class AbstractMavenExecu
     {
         this.logger = logger;
     }
+
+    
+    protected Settings encryptSettings( Settings settings )
+    {
+        Settings encryptedSettings = SettingsUtils.copySettings( settings );
+        
+        for ( Server server : encryptedSettings.getServers() )
+        {
+            String password = server.getPassword(); 
+            if( password != null && !isEncryptedString( password ) )
+            {
+                try
+                {
+                    server.setPassword( encryptAndDecorate( password ) );
+                }
+                catch ( IllegalStateException e )
+                {
+                    // ignore
+                }
+                catch ( SecDispatcherException e )
+                {
+                    // ignore
+                }
+                catch ( PlexusCipherException e )
+                {
+                    // ignore
+                }
+            }
+
+            String passphrase = server.getPassphrase(); 
+            if( passphrase != null && !isEncryptedString( passphrase ) )
+            {
+                try
+                {
+                    server.setPassphrase( encryptAndDecorate( passphrase ) );
+                }
+                catch ( IllegalStateException e )
+                {
+                    // ignore
+                }
+                catch ( SecDispatcherException e )
+                {
+                    // ignore
+                }
+                catch ( PlexusCipherException e )
+                {
+                    // ignore
+                }
+            }
+        }
+        
+        for ( Proxy proxy : encryptedSettings.getProxies() )
+        {
+            String password = proxy.getPassword();
+            if( password != null && !isEncryptedString( password ) )
+            {
+                try
+                {
+                    proxy.setPassword( encryptAndDecorate( password ) );
+                }
+                catch ( IllegalStateException e )
+                {
+                    // ignore
+                }
+                catch ( SecDispatcherException e )
+                {
+                    // ignore
+                }
+                catch ( PlexusCipherException e )
+                {
+                    // ignore
+                }
+            }
+        }
+        
+        return encryptedSettings;
+    }
+    
+    // From org.apache.maven.cli.MavenCli.encryption(CliRequest)
+    private final String encryptAndDecorate( String passwd ) throws IllegalStateException, SecDispatcherException, PlexusCipherException
+    {
+        String configurationFile = secDispatcher.getConfigurationFile();
+
+        if ( configurationFile.startsWith( "~" ) )
+        {
+            configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
+        }
+
+        String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
+
+        String master = null;
+
+        SettingsSecurity sec = SecUtil.read( file, true );
+        if ( sec != null )
+        {
+            master = sec.getMaster();
+        }
+
+        if ( master == null )
+        {
+            throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
+        }
+
+        DefaultPlexusCipher cipher = new DefaultPlexusCipher();
+        String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
+        return cipher.encryptAndDecorate( passwd, masterPasswd );
+    }
+    
+    private boolean isEncryptedString( String str )
+    {
+        return cipher.isEncryptedString( str );
+    }
+
+    protected SettingsXpp3Writer getSettingsWriter()
+    {
+        return new SettingsXpp3Writer();
+    }
 }

Modified: maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/ForkedMavenExecutor.java
URL: http://svn.apache.org/viewvc/maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/ForkedMavenExecutor.java?rev=1533747&r1=1533746&r2=1533747&view=diff
==============================================================================
--- maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/ForkedMavenExecutor.java (original)
+++ maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/ForkedMavenExecutor.java Sat Oct 19 12:46:22 2013
@@ -77,12 +77,12 @@ public class ForkedMavenExecutor
             try
             {
                 settingsFile = File.createTempFile( "release-settings", ".xml" );
-                SettingsXpp3Writer writer = new SettingsXpp3Writer();
+                SettingsXpp3Writer writer = getSettingsWriter();
                 FileWriter fileWriter = null;
                 try
                 {
                     fileWriter = new FileWriter( settingsFile );
-                    writer.write( fileWriter, releaseEnvironment.getSettings() );
+                    writer.write( fileWriter, encryptSettings( releaseEnvironment.getSettings() ) );
                 }
                 finally
                 {

Modified: maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java
URL: http://svn.apache.org/viewvc/maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java?rev=1533747&r1=1533746&r2=1533747&view=diff
==============================================================================
--- maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java (original)
+++ maven/release/trunk/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java Sat Oct 19 12:46:22 2013
@@ -176,7 +176,7 @@ public class InvokerMavenExecutor
 
     // TODO: Configuring an invocation request from a command line could as well be part of the Invoker API
     protected void setupRequest( InvocationRequest req,
-                                 LoggerBridge bridge,
+                                 InvokerLogger bridge,
                                String additionalArguments )
         throws MavenExecutorException
     {
@@ -327,8 +327,8 @@ public class InvokerMavenExecutor
                               ReleaseResult result )
         throws MavenExecutorException
     {
-        Handler handler = new Handler( getLogger() );
-        LoggerBridge bridge = new LoggerBridge( getLogger() );
+        InvocationOutputHandler handler = getOutputHandler();
+        InvokerLogger bridge = getInvokerLogger();
 
         Invoker invoker =
             new DefaultInvoker().setMavenHome( releaseEnvironment.getMavenHome() ).setLogger( bridge ).setOutputHandler(
@@ -350,12 +350,12 @@ public class InvokerMavenExecutor
             try
             {
                 settingsFile = File.createTempFile( "release-settings", ".xml" );
-                SettingsXpp3Writer writer = new SettingsXpp3Writer();
+                SettingsXpp3Writer writer = getSettingsWriter();
                 FileWriter fileWriter = null;
                 try
                 {
                     fileWriter = new FileWriter( settingsFile );
-                    writer.write( fileWriter, releaseEnvironment.getSettings() );
+                    writer.write( fileWriter, encryptSettings( releaseEnvironment.getSettings() ) );
                 }
                 finally
                 {
@@ -410,6 +410,16 @@ public class InvokerMavenExecutor
         }
     }
 
+    protected InvokerLogger getInvokerLogger()
+    {
+        return new LoggerBridge( getLogger() );
+    }
+
+    protected InvocationOutputHandler getOutputHandler()
+    {
+        return new Handler( getLogger() );
+    }
+
     private static final class Handler
         implements InvocationOutputHandler
     {

Modified: maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/ForkedMavenExecutorTest.java
URL: http://svn.apache.org/viewvc/maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/ForkedMavenExecutorTest.java?rev=1533747&r1=1533746&r2=1533747&view=diff
==============================================================================
--- maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/ForkedMavenExecutorTest.java (original)
+++ maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/ForkedMavenExecutorTest.java Sat Oct 19 12:46:22 2013
@@ -24,6 +24,7 @@ import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -32,12 +33,21 @@ import static org.mockito.Mockito.when;
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.Writer;
 
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
 import org.apache.maven.shared.release.ReleaseResult;
+import org.apache.maven.shared.release.env.DefaultReleaseEnvironment;
+import org.apache.maven.shared.release.env.ReleaseEnvironment;
 import org.codehaus.plexus.PlexusTestCase;
 import org.codehaus.plexus.util.cli.Arg;
 import org.codehaus.plexus.util.cli.CommandLineException;
 import org.codehaus.plexus.util.cli.Commandline;
+import org.mockito.ArgumentCaptor;
+import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
 
 /**
  * Test the forked Maven executor.
@@ -48,6 +58,8 @@ public class ForkedMavenExecutorTest
     extends PlexusTestCase
 {
     private ForkedMavenExecutor executor;
+    
+    private SecDispatcher secDispatcher;
 
     protected void setUp()
         throws Exception
@@ -55,6 +67,8 @@ public class ForkedMavenExecutorTest
         super.setUp();
 
         executor = (ForkedMavenExecutor) lookup( MavenExecutor.ROLE, "forked-path" );
+        
+        secDispatcher = (SecDispatcher) lookup( SecDispatcher.ROLE, "mng-4384" );
     }
 
     public void testExecution()
@@ -289,4 +303,67 @@ public class ForkedMavenExecutorTest
         
         verifyNoMoreInteractions( commandLineMock, argMock, commandLineFactoryMock );
     }
+    
+    public void testEncryptSettings()
+        throws Exception
+    {
+        // prepare
+        File workingDirectory = getTestFile( "target/working-directory" );
+        Process mockProcess = mock( Process.class );
+        when( mockProcess.getInputStream() ).thenReturn( mock( InputStream.class ) );
+        when( mockProcess.getErrorStream() ).thenReturn( mock( InputStream.class ) );
+        when( mockProcess.getOutputStream() ).thenReturn( mock( OutputStream.class ) );
+        when( mockProcess.waitFor() ).thenReturn( 0 );
+
+        Commandline commandLineMock = mock( Commandline.class );
+        when( commandLineMock.execute() ).thenReturn( mockProcess );
+
+        Arg valueArgument = mock( Arg.class );
+        when( commandLineMock.createArg() ).thenReturn( valueArgument );
+
+        CommandLineFactory commandLineFactoryMock = mock( CommandLineFactory.class );
+        when( commandLineFactoryMock.createCommandLine( isA( String.class ) /* "mvn" */) ).thenReturn( commandLineMock );
+
+        executor.setCommandLineFactory( commandLineFactoryMock );
+
+        Settings settings = new Settings();
+        Server server = new Server();
+        server.setPassphrase( "server_passphrase" );
+        server.setPassword( "server_password" );
+        settings.addServer( server );
+        Proxy proxy = new Proxy();
+        proxy.setPassword( "proxy_password" );
+        settings.addProxy( proxy );
+
+        ReleaseEnvironment releaseEnvironment = new DefaultReleaseEnvironment();
+        releaseEnvironment.setSettings( settings );
+
+        AbstractMavenExecutor executorSpy = spy( executor );
+        SettingsXpp3Writer settingsWriter = mock( SettingsXpp3Writer.class );
+
+        ArgumentCaptor<Settings> encryptedSettings = ArgumentCaptor.forClass( Settings.class );
+
+        when( executorSpy.getSettingsWriter() ).thenReturn( settingsWriter );
+
+        executorSpy.executeGoals( workingDirectory, "validate", releaseEnvironment, false, null, new ReleaseResult() );
+
+        verify( settingsWriter ).write( isA( Writer.class ), encryptedSettings.capture() );
+
+        assertNotSame( settings, encryptedSettings.getValue() );
+
+        Server encryptedServer = encryptedSettings.getValue().getServers().get( 0 );
+        assertEquals( "server_passphrase", secDispatcher.decrypt( encryptedServer.getPassphrase() ) );
+        assertEquals( "server_password", secDispatcher.decrypt( encryptedServer.getPassword() ) );
+
+        Proxy encryptedProxy = encryptedSettings.getValue().getProxies().get( 0 );
+        assertEquals( "proxy_password", secDispatcher.decrypt( encryptedProxy.getPassword() ) );
+
+        File settingsSecurity = new File( System.getProperty( "user.home" ), ".m2/settings-security.xml" );
+        if ( settingsSecurity.exists() )
+        {
+            assertFalse( "server_passphrase".equals( encryptedServer.getPassphrase() ) );
+            assertFalse( "server_password".equals( encryptedServer.getPassword() ) );
+            assertFalse( "proxy_password".equals( encryptedProxy.getPassword() ) );
+        }
+    }
 }
\ No newline at end of file

Modified: maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/InvokerMavenExecutorTest.java
URL: http://svn.apache.org/viewvc/maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/InvokerMavenExecutorTest.java?rev=1533747&r1=1533746&r2=1533747&view=diff
==============================================================================
--- maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/InvokerMavenExecutorTest.java (original)
+++ maven/release/trunk/maven-release-manager/src/test/java/org/apache/maven/shared/release/exec/InvokerMavenExecutorTest.java Sat Oct 19 12:46:22 2013
@@ -19,46 +19,76 @@ package org.apache.maven.shared.release.
  * under the License.
  */
 
-import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.isA;
 import static org.mockito.Mockito.mock;
-
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.Writer;
+
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
+import org.apache.maven.shared.invoker.InvocationOutputHandler;
 import org.apache.maven.shared.invoker.InvocationRequest;
+import org.apache.maven.shared.release.ReleaseResult;
+import org.apache.maven.shared.release.env.DefaultReleaseEnvironment;
+import org.apache.maven.shared.release.env.ReleaseEnvironment;
+import org.codehaus.plexus.PlexusTestCase;
 import org.codehaus.plexus.logging.Logger;
 import org.junit.Test;
-
+import org.mockito.ArgumentCaptor;
+import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
 
 public class InvokerMavenExecutorTest
+    extends PlexusTestCase
 {
 
+    private InvokerMavenExecutor executor;
+
+    private SecDispatcher secDispatcher;
+
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        executor = (InvokerMavenExecutor) lookup( MavenExecutor.ROLE, "invoker" );
+
+        secDispatcher = (SecDispatcher) lookup( SecDispatcher.ROLE, "mng-4384" );
+    }
+
     @Test
-    public void testThreads() throws Exception
+    public void testThreads()
+        throws Exception
     {
-        InvokerMavenExecutor executor = new InvokerMavenExecutor();
         Logger logger = mock( Logger.class );
         executor.enableLogging( logger );
-        
+
         InvocationRequest req = new DefaultInvocationRequest();
         executor.setupRequest( req, null, "-T 3" );
         assertEquals( "3", req.getThreads() );
-        
+
         req = new DefaultInvocationRequest();
         executor.setupRequest( req, null, "-T4" );
         assertEquals( "4", req.getThreads() );
-        
+
         req = new DefaultInvocationRequest();
         executor.setupRequest( req, null, "\"-T5\"" );
         assertEquals( "5", req.getThreads() );
-        
     }
 
     @Test
-    public void testGlobalSettings() throws Exception
+    public void testGlobalSettings()
+        throws Exception
     {
-        InvokerMavenExecutor executor = new InvokerMavenExecutor();
         Logger logger = mock( Logger.class );
         executor.enableLogging( logger );
-        
+
         InvocationRequest req = new DefaultInvocationRequest();
         executor.setupRequest( req, null, "-gs custom-settings.xml" );
         assertEquals( "custom-settings.xml", req.getGlobalSettingsFile().getPath() );
@@ -67,4 +97,61 @@ public class InvokerMavenExecutorTest
         executor.setupRequest( req, null, "--global-settings other-settings.xml" );
         assertEquals( "other-settings.xml", req.getGlobalSettingsFile().getPath() );
     }
+
+    public void testEncryptSettings()
+        throws Exception
+    {
+        // prepare
+        File workingDirectory = getTestFile( "target/working-directory" );
+        workingDirectory.mkdirs();
+        
+        
+        Settings settings = new Settings();
+        Server server = new Server();
+        server.setPassphrase( "server_passphrase" );
+        server.setPassword( "server_password" );
+        settings.addServer( server );
+        Proxy proxy = new Proxy();
+        proxy.setPassword( "proxy_password" );
+        settings.addProxy( proxy );
+
+        ReleaseEnvironment releaseEnvironment = new DefaultReleaseEnvironment();
+        releaseEnvironment.setSettings( settings );
+
+        InvokerMavenExecutor executorSpy = spy( executor );
+        SettingsXpp3Writer settingsWriter = mock( SettingsXpp3Writer.class );
+
+        ArgumentCaptor<Settings> encryptedSettings = ArgumentCaptor.forClass( Settings.class );
+
+        when( executorSpy.getSettingsWriter() ).thenReturn( settingsWriter );
+        when( executorSpy.getOutputHandler() ).thenReturn( null );
+        when( executorSpy.getInvokerLogger() ).thenReturn( null );
+
+        try
+        {
+            executorSpy.executeGoals( workingDirectory, "validate", releaseEnvironment, false, null, new ReleaseResult() );
+        }
+        catch ( MavenExecutorException e )
+        {
+        }
+
+        verify( settingsWriter ).write( isA( Writer.class ), encryptedSettings.capture() );
+
+        assertNotSame( settings, encryptedSettings.getValue() );
+
+        Server encryptedServer = encryptedSettings.getValue().getServers().get( 0 );
+        assertEquals( "server_passphrase", secDispatcher.decrypt( encryptedServer.getPassphrase() ) );
+        assertEquals( "server_password", secDispatcher.decrypt( encryptedServer.getPassword() ) );
+
+        Proxy encryptedProxy = encryptedSettings.getValue().getProxies().get( 0 );
+        assertEquals( "proxy_password", secDispatcher.decrypt( encryptedProxy.getPassword() ) );
+
+        File settingsSecurity = new File( System.getProperty( "user.home" ), ".m2/settings-security.xml" );
+        if ( settingsSecurity.exists() )
+        {
+            assertFalse( "server_passphrase".equals( encryptedServer.getPassphrase() ) );
+            assertFalse( "server_password".equals( encryptedServer.getPassword() ) );
+            assertFalse( "proxy_password".equals( encryptedProxy.getPassword() ) );
+        }
+    }
 }