View Javadoc
1   package org.apache.maven.scm.provider.git.command.checkout;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.io.Writer;
25  import java.nio.file.FileSystem;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.attribute.PosixFilePermissions;
29  import java.security.GeneralSecurityException;
30  import java.security.KeyPair;
31  import java.security.PublicKey;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
36  import org.apache.maven.scm.provider.git.GitScmTestUtils;
37  import org.apache.maven.scm.repository.ScmRepository;
38  import org.apache.maven.scm.tck.command.checkout.CheckOutCommandTckTest;
39  import org.apache.sshd.common.config.keys.KeyUtils;
40  import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyEncryptionContext;
41  import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;
42  import org.apache.sshd.git.GitLocationResolver;
43  import org.apache.sshd.git.pack.GitPackCommandFactory;
44  import org.apache.sshd.server.SshServer;
45  import org.apache.sshd.server.auth.pubkey.KeySetPublickeyAuthenticator;
46  import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
47  import org.apache.sshd.server.session.ServerSession;
48  import org.apache.sshd.util.test.CommonTestSupportUtils;
49  import org.apache.sshd.util.test.CoreTestSupportUtils;
50  import org.bouncycastle.openssl.PKCS8Generator;
51  import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
52  import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
53  import org.bouncycastle.util.io.pem.PemObject;
54  import org.junit.Assume;
55  import org.junit.Rule;
56  import org.junit.Test;
57  import org.junit.rules.TemporaryFolder;
58  
59  /**
60   *
61   */
62  public abstract class GitSshCheckOutCommandTckTest
63      extends CheckOutCommandTckTest
64  {
65      protected final SshServer sshServer;
66      protected final KeyPair keyPair;
67      protected final List<PublicKey> acceptedPublicKeys;
68  
69      @Rule
70      public TemporaryFolder tmpDirectory = new TemporaryFolder();
71  
72      protected GitSshCheckOutCommandTckTest() throws GeneralSecurityException
73      {
74           sshServer = CoreTestSupportUtils.setupTestServer( getClass() );
75           keyPair = CommonTestSupportUtils.generateKeyPair( KeyUtils.RSA_ALGORITHM, 2048 );
76           acceptedPublicKeys = new ArrayList<>();
77           acceptedPublicKeys.add( keyPair.getPublic() );
78           PublickeyAuthenticator authenticator = new KeySetPublickeyAuthenticator( "onlykey",
79                    acceptedPublicKeys );
80           sshServer.setPublickeyAuthenticator( authenticator );
81      }
82  
83      void writePrivateKeyAsPkcs8( Path file, String passphrase )
84              throws IOException, GeneralSecurityException
85      {
86          // encryption only optional
87          if ( passphrase != null )
88          {
89              // encryption with format outlined in https://dnaeon.github.io/openssh-private-key-binary-format/
90              OpenSSHKeyPairResourceWriter writer = new OpenSSHKeyPairResourceWriter();
91              OpenSSHKeyEncryptionContext context = new OpenSSHKeyEncryptionContext();
92              context.setCipherType( "192" );
93              context.setPassword( passphrase );
94              try ( OutputStream output = Files.newOutputStream( file ) )
95              {
96                  writer.writePrivateKey( keyPair, "comment", context, output );
97              }
98          }
99          else
100         {
101             // wrap unencrypted private key as regular PKCS8 private key
102             PKCS8Generator pkcs8Generator = new JcaPKCS8Generator( keyPair.getPrivate(), null );
103             PemObject pemObject = pkcs8Generator.generate();
104 
105             try ( Writer writer = Files.newBufferedWriter( file );
106                             JcaPEMWriter pw = new JcaPEMWriter( writer ) )
107             {
108                 pw.writeObject( pemObject );
109             }
110         }
111 
112         if ( file.getFileSystem().supportedFileAttributeViews().contains( "posix" ) )
113         {
114             // must only be readable/writeable by me
115             Files.setPosixFilePermissions( file, PosixFilePermissions.fromString( "rwx------" ) );
116         }
117     }
118 
119     protected abstract String getScmProvider();
120 
121     /** {@inheritDoc} */
122     public String getScmUrl()
123         throws Exception
124     {
125         return "scm:" + getScmProvider() + ":ssh://localhost:" + sshServer.getPort() + "/repository";
126     }
127 
128     public void configureCredentials( ScmRepository repository, String passphrase )
129         throws Exception
130     {
131         ScmProviderRepositoryWithHost providerRepository =
132              ScmProviderRepositoryWithHost.class.cast( repository.getProviderRepository() );
133         // store as file
134         Path privateKeyFile = tmpDirectory.newFile().toPath();
135         writePrivateKeyAsPkcs8( privateKeyFile, passphrase );
136         providerRepository.setPrivateKey( privateKeyFile.toString() );
137         providerRepository.setPassphrase( passphrase ); // may be null
138     }
139 
140     /** {@inheritDoc} */
141     public void initRepo()
142         throws Exception
143     {
144         GitScmTestUtils.initRepo( "src/test/resources/repository/", getRepositoryRoot(), getWorkingDirectory() );
145 
146         GitLocationResolver gitLocationResolver = new GitLocationResolver()
147         {
148             @Override
149             public Path resolveRootDirectory( String command, String[] args, ServerSession session, FileSystem fs )
150                 throws IOException
151             {
152                 return getRepositoryRoot().getParentFile().toPath();
153             }
154         };
155         sshServer.setCommandFactory( new GitPackCommandFactory( gitLocationResolver ) );
156         sshServer.start();
157 
158         // as checkout also already happens in setup() make sure to configure credentials here as well
159         configureCredentials( getScmRepository(), null );
160     }
161 
162     @Override
163     public void removeRepo() throws Exception
164     {
165         sshServer.stop();
166         super.removeRepo();
167     }
168 
169     @Override
170     @Test
171     public void testCheckOutCommandTest()
172         throws Exception
173     {
174         configureCredentials( getScmRepository(), null );
175         super.testCheckOutCommandTest();
176     }
177 
178     @Test
179     public void testCheckOutCommandWithPassphraseTest() throws Exception
180     {
181         // TODO: currently no easy way to pass passphrase in gitexe
182         Assume.assumeTrue( "Ignore test with passphrase for provider " + getScmProvider(),
183                            "jgit".equals( getScmProvider() ) );
184         configureCredentials( getScmRepository(), "mySecret" );
185         super.testCheckOutCommandTest();
186     }
187 }