Skip to content

Commit

Permalink
[SCM-995] Upgrade JGit to 5.13.1 and leverage Apache Mina SSHD instea…
Browse files Browse the repository at this point in the history
…d of JSch

This closes #153
  • Loading branch information
kwin authored and michael-o committed Jul 19, 2022
1 parent 07d51d3 commit acb585f
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
see http://eclipse.org/jgit/
</description>

<properties>
<jgitVersion>5.13.1.202206130422-r</jgitVersion><!-- version 6+ requires Java 11 -->
</properties>
<dependencies>
<dependency>
<groupId>javax.inject</groupId>
Expand All @@ -47,13 +50,12 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.5.4.201711221230-r</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<version>${jgitVersion}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
<version>${jgitVersion}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,47 +19,22 @@
* under the License.
*/

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.SshTransport;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;
import org.slf4j.Logger;
import org.eclipse.jgit.transport.sshd.SshdSessionFactory;

/**
* Implementation of {@link TransportConfigCallback} which adds
* a public/private key identity to ssh URLs if configured.
*/
public class JGitTransportConfigCallback implements TransportConfigCallback
{
private SshSessionFactory sshSessionFactory = null;
private final SshdSessionFactory sshSessionFactory;

public JGitTransportConfigCallback( GitScmProviderRepository repo, Logger logger )
public JGitTransportConfigCallback( SshdSessionFactory sshSessionFactory )
{
if ( repo.getFetchInfo().getProtocol().equals( "ssh" ) )
{
if ( !StringUtils.isEmptyOrNull( repo.getPrivateKey() ) && repo.getPassphrase() == null )
{
logger.debug( "using private key: " + repo.getPrivateKey() );
sshSessionFactory = new UnprotectedPrivateKeySessionFactory( repo );
}
else if ( !StringUtils.isEmptyOrNull( repo.getPrivateKey() ) && repo.getPassphrase() != null )
{
logger.debug( "using private key with passphrase: " + repo.getPrivateKey() );
sshSessionFactory = new ProtectedPrivateKeyFileSessionFactory( repo );
}
else
{
sshSessionFactory = new SimpleSessionFactory();
}
}
this.sshSessionFactory = sshSessionFactory;
}

@Override
Expand All @@ -72,60 +47,4 @@ public void configure( Transport transport )
}
}

private static class SimpleSessionFactory extends JschConfigSessionFactory
{
@Override
protected void configure( OpenSshConfig.Host host, Session session )
{
}
}

private abstract static class PrivateKeySessionFactory extends SimpleSessionFactory
{
private final GitScmProviderRepository repo;

GitScmProviderRepository getRepo()
{
return repo;
}

PrivateKeySessionFactory( GitScmProviderRepository repo )
{
this.repo = repo;
}
}

private static class UnprotectedPrivateKeySessionFactory extends PrivateKeySessionFactory
{

UnprotectedPrivateKeySessionFactory( GitScmProviderRepository repo )
{
super( repo );
}

@Override
protected JSch createDefaultJSch( FS fs ) throws JSchException
{
JSch defaultJSch = super.createDefaultJSch( fs );
defaultJSch.addIdentity( getRepo().getPrivateKey() );
return defaultJSch;
}
}

private static class ProtectedPrivateKeyFileSessionFactory extends PrivateKeySessionFactory
{

ProtectedPrivateKeyFileSessionFactory( GitScmProviderRepository repo )
{
super( repo );
}

@Override
protected JSch createDefaultJSch( FS fs ) throws JSchException
{
JSch defaultJSch = super.createDefaultJSch( fs );
defaultJSch.addIdentity( getRepo().getPrivateKey(), getRepo().getPassphrase() );
return defaultJSch;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ public static Iterable<PushResult> push( Git git, GitScmProviderRepository repo,
{
CredentialsProvider credentials = prepareSession( git, repo );
PushCommand command = git.push().setRefSpecs( refSpec ).setCredentialsProvider( credentials )
.setTransportConfigCallback( new JGitTransportConfigCallback( repo, LOGGER ) );
.setTransportConfigCallback(
new JGitTransportConfigCallback( new ScmProviderAwareSshdSessionFactory( repo, LOGGER ) )
);

Iterable<PushResult> pushResultList = command.call();
for ( PushResult pushResult : pushResultList )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.apache.maven.scm.provider.git.jgit.command;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;

import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.sshd.IdentityPasswordProvider;
import org.eclipse.jgit.transport.sshd.KeyPasswordProvider;
import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.eclipse.jgit.util.StringUtils;
import org.slf4j.Logger;

/**
* {@link SshdSessionFactory} considering the settings from {@link GitScmProviderRepository}.
*
*/
public class ScmProviderAwareSshdSessionFactory extends SshdSessionFactory
{
private final GitScmProviderRepository repo;
private final Logger logger;

public ScmProviderAwareSshdSessionFactory( GitScmProviderRepository repo, Logger logger )
{
this.repo = repo;
this.logger = logger;
}

@Override
protected List<Path> getDefaultIdentities( File sshDir )
{
if ( !StringUtils.isEmptyOrNull( repo.getPrivateKey() ) )
{
logger.debug( "Using private key at {}", repo.getPrivateKey() );
return Collections.singletonList( Paths.get( repo.getPrivateKey() ) );
}
else
{
return super.getDefaultIdentities( sshDir );
}
}

@Override
protected KeyPasswordProvider createKeyPasswordProvider( CredentialsProvider provider )
{
if ( repo.getPassphrase() != null )
{
return new IdentityPasswordProvider( provider )
{
@Override
public char[] getPassphrase( URIish uri, int attempt ) throws IOException
{
if ( attempt > 0 )
{
throw new IOException( "Passphrase was not correct in first attempt, "
+ "canceling further attempts!" );
}
logger.debug( "Using stored passphrase" );
return repo.getPassphrase().toCharArray();
}
};
}
else
{
return super.createKeyPasswordProvider( provider );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
* under the License.
*/


import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;

import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFile;
import org.apache.maven.scm.ScmFileSet;
Expand All @@ -32,6 +39,7 @@
import org.apache.maven.scm.provider.git.command.GitCommand;
import org.apache.maven.scm.provider.git.jgit.command.JGitTransportConfigCallback;
import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
import org.apache.maven.scm.provider.git.jgit.command.ScmProviderAwareSshdSessionFactory;
import org.apache.maven.scm.provider.git.jgit.command.branch.JGitBranchCommand;
import org.apache.maven.scm.provider.git.jgit.command.remoteinfo.JGitRemoteInfoCommand;
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
Expand All @@ -48,11 +56,7 @@
import org.eclipse.jgit.storage.file.WindowCacheConfig;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.treewalk.TreeWalk;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;

/**
* @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
Expand All @@ -63,6 +67,19 @@ public class JGitCheckOutCommand
extends AbstractCheckOutCommand
implements GitCommand
{
private BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier;

public JGitCheckOutCommand()
{
sshSessionFactorySupplier = ScmProviderAwareSshdSessionFactory::new;
}

public void setSshSessionFactorySupplier(
BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier )
{
this.sshSessionFactorySupplier = sshSessionFactorySupplier;
}

/**
* For git, the given repository is a remote one. We have to clone it first if the working directory does not
* contain a git repo yet, otherwise we have to git-pull it.
Expand Down Expand Up @@ -94,6 +111,9 @@ protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo,
branch = Constants.MASTER;
}

TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback(
sshSessionFactorySupplier.apply( (GitScmProviderRepository) repo, logger ) );

logger.debug( "try checkout of branch: " + branch );

if ( !fileSet.getBasedir().exists() || !( new File( fileSet.getBasedir(), ".git" ).exists() ) )
Expand All @@ -116,31 +136,27 @@ protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo,

command.setCredentialsProvider( credentials ).setBranch( branch ).setDirectory( fileSet.getBasedir() );

TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback(
(GitScmProviderRepository) repo, logger );
command.setTransportConfigCallback( transportConfigCallback );

command.setProgressMonitor( monitor );
git = command.call();
}

JGitRemoteInfoCommand remoteInfoCommand = new JGitRemoteInfoCommand();

remoteInfoCommand.setSshSessionFactorySupplier( sshSessionFactorySupplier );
RemoteInfoScmResult result = remoteInfoCommand.executeRemoteInfoCommand( repository, fileSet, null );

if ( git == null )
{
// deliberately not using JGitUtils.openRepo(), the caller told us exactly where to checkout
git = Git.open( fileSet.getBasedir() );
}

if ( fileSet.getBasedir().exists() && new File( fileSet.getBasedir(), ".git" ).exists()
&& result.getBranches().size() > 0 )
{
// git repo exists, so we must git-pull the changes
CredentialsProvider credentials = JGitUtils.prepareSession( git, repository );
TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback(
(GitScmProviderRepository) repo, logger );

if ( version != null && StringUtils.isNotEmpty( version.getName() ) && ( version instanceof ScmTag ) )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@
import org.apache.maven.scm.provider.git.command.GitCommand;
import org.apache.maven.scm.provider.git.jgit.command.JGitTransportConfigCallback;
import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
import org.apache.maven.scm.provider.git.jgit.command.ScmProviderAwareSshdSessionFactory;
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;

/**
* @author Dominik Bartholdi (imod)
Expand All @@ -48,6 +51,19 @@ public class JGitListCommand
implements GitCommand
{

private BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier;

public JGitListCommand()
{
sshSessionFactorySupplier = ScmProviderAwareSshdSessionFactory::new;
}

public void setSshSessionFactorySupplier(
BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier )
{
this.sshSessionFactorySupplier = sshSessionFactorySupplier;
}

@Override
protected ListScmResult executeListCommand( ScmProviderRepository repo, ScmFileSet fileSet, boolean recursive,
ScmVersion scmVersion )
Expand All @@ -64,7 +80,8 @@ protected ListScmResult executeListCommand( ScmProviderRepository repo, ScmFileS
List<ScmFile> list = new ArrayList<>();
Collection<Ref> lsResult = git.lsRemote().setCredentialsProvider( credentials )
.setTransportConfigCallback(
new JGitTransportConfigCallback( (GitScmProviderRepository) repo, logger ) )
new JGitTransportConfigCallback(
sshSessionFactorySupplier.apply( (GitScmProviderRepository) repo, logger ) ) )
.call();
for ( Ref ref : lsResult )
{
Expand Down
Loading

0 comments on commit acb585f

Please sign in to comment.