Skip to content

Commit

Permalink
[SCM-832] maven-scm-provider-jgit should support SSH public key auth (#…
Browse files Browse the repository at this point in the history
…50)

* Support public key auth for SSH #SCM-832

Adds a TransportConfigCallback to all remote commands, which adds a public/private key based identity for repositories with ssh URLs if configured.

* Updated documentation for #SCM-832

* [SCM-832] Updated documentation

* [SCM-832] Added debug logging

maven-scm-provider-jgit now outputs the private key used when run as mvn -X
  • Loading branch information
mkutter authored and olamy committed Dec 14, 2018
1 parent b37707d commit 9a3daee
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
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 com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.apache.maven.scm.log.ScmLogger;
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.transport.*;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;

/**
* 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;

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

@Override
public void configure(Transport transport) {
if (transport instanceof SshTransport) {
SshTransport sshTransport = (SshTransport) transport;
sshTransport.setSshSessionFactory(sshSessionFactory);
}
}

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

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

public GitScmProviderRepository getRepo() {
return repo;
}

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

static private class UnprotectedPrivateKeySessionFactory extends PrivateKeySessionFactory {

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

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

static private class ProtectedPrivateKeyFileSessionFactory extends PrivateKeySessionFactory {

public 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 @@ -28,6 +28,7 @@
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
Expand Down Expand Up @@ -83,7 +84,7 @@
*/
public class JGitUtils
{

private JGitUtils()
{
// no op
Expand Down Expand Up @@ -178,15 +179,19 @@ public static CredentialsProvider getCredentials( GitScmProviderRepository repos
return new UsernamePasswordCredentialsProvider( repository.getUser().trim(),
repository.getPassword().trim() );
}


return null;
}

public static Iterable<PushResult> push( ScmLogger logger, Git git, GitScmProviderRepository repo, RefSpec refSpec )
throws GitAPIException, InvalidRemoteException, TransportException
{
CredentialsProvider credentials = JGitUtils.prepareSession( logger, git, repo );
Iterable<PushResult> pushResultList =
git.push().setCredentialsProvider( credentials ).setRefSpecs( refSpec ).call();
PushCommand command = git.push().setRefSpecs(refSpec).setCredentialsProvider(credentials)
.setTransportConfigCallback(new JGitTransportConfigCallback(repo, logger));

Iterable<PushResult> pushResultList = command.call();
for ( PushResult pushResult : pushResultList )
{
Collection<RemoteRefUpdate> ru = pushResult.getRemoteUpdates();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult;
import org.apache.maven.scm.provider.ScmProviderRepository;
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.branch.JGitBranchCommand;
import org.apache.maven.scm.provider.git.jgit.command.remoteinfo.JGitRemoteInfoCommand;
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.revwalk.RevCommit;
Expand Down Expand Up @@ -109,7 +109,13 @@ protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo,
CredentialsProvider credentials = JGitUtils.getCredentials( (GitScmProviderRepository) repo );
getLogger().info( "cloning [" + branch + "] to " + fileSet.getBasedir() );
CloneCommand command = Git.cloneRepository().setURI( repository.getFetchUrl() );

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

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

command.setProgressMonitor( monitor );
git = command.call();
}
Expand All @@ -129,6 +135,8 @@ protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo,
{
// git repo exists, so we must git-pull the changes
CredentialsProvider credentials = JGitUtils.prepareSession( getLogger(), git, repository );
TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback(
(GitScmProviderRepository) repo, getLogger());

if ( version != null && StringUtils.isNotEmpty( version.getName() ) && ( version instanceof ScmTag ) )
{
Expand All @@ -138,12 +146,17 @@ protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo,
// In fact, a tag in git may be in multiple branches. This occurs if
// you create a branch after the tag has been created
getLogger().debug( "fetch..." );
git.fetch().setCredentialsProvider( credentials ).setProgressMonitor( monitor ).call();
FetchCommand command = git.fetch().setCredentialsProvider(credentials).setProgressMonitor(monitor);
command.setTransportConfigCallback(transportConfigCallback);
command.call();

}
else
{
getLogger().debug( "pull..." );
git.pull().setCredentialsProvider( credentials ).setProgressMonitor( monitor ).call();
PullCommand command = git.pull().setCredentialsProvider(credentials).setProgressMonitor(monitor);
command.setTransportConfigCallback(transportConfigCallback);
command.call();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.maven.scm.command.list.ListScmResult;
import org.apache.maven.scm.provider.ScmProviderRepository;
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.repository.GitScmProviderRepository;
import org.eclipse.jgit.api.Git;
Expand Down Expand Up @@ -61,7 +62,10 @@ protected ListScmResult executeListCommand( ScmProviderRepository repo, ScmFileS
JGitUtils.prepareSession( getLogger(), git, (GitScmProviderRepository) repo );

List<ScmFile> list = new ArrayList<ScmFile>();
Collection<Ref> lsResult = git.lsRemote().setCredentialsProvider( credentials ).call();
Collection<Ref> lsResult = git.lsRemote().setCredentialsProvider( credentials )
.setTransportConfigCallback(
new JGitTransportConfigCallback((GitScmProviderRepository) repo, getLogger()))
.call();
for ( Ref ref : lsResult )
{
getLogger().debug( ref.getObjectId().getName() + " " + ref.getTarget().getName() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult;
import org.apache.maven.scm.provider.ScmProviderRepository;
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.repository.GitScmProviderRepository;
import org.eclipse.jgit.api.Git;
Expand Down Expand Up @@ -61,7 +62,8 @@ public RemoteInfoScmResult executeRemoteInfoCommand( ScmProviderRepository repos
CredentialsProvider credentials = JGitUtils.getCredentials( repo );

LsRemoteCommand lsCommand =
git.lsRemote().setRemote( repo.getPushUrl() ).setCredentialsProvider( credentials );
git.lsRemote().setRemote( repo.getPushUrl() ).setCredentialsProvider( credentials )
.setTransportConfigCallback(new JGitTransportConfigCallback(repo, getLogger()));

Map<String, String> tag = new HashMap<String, String>();
Collection<Ref> allTags = lsCommand.setHeads( false ).setTags( true ).call();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ under the License.
maven-scm-provider-jgit
===

This scm provider implementation allows the usage of git with the release and scm plugin without having to install a nativ git client.
This implementation uses username and password instead of a public/private keys to authenticate the requests to a remote repository like GitHub.
This scm provider implementation allows the usage of git with the release and scm plugin without having to install a native git client.
This implementation can use both username and password, or public/private keys to authenticate the requests to a remote
repository like GitHub. At the moment, public/private keys are only supported for SSH access.

Configuration
---
Expand Down Expand Up @@ -65,7 +66,17 @@ Usage with the `maven-scm-plugin`
</dependency>
</dependencies>
</plugin>


Public/private key configuration in settings.xml - for use with ssh URLs like ssh://git@github.com/apache/maven-scm

<servers>
<server>
<id>git@github.com</id>
<privateKey>path/to/private/key</privateKey>
<passphrase>private key passphrase</passphrase>
</server>
<server>

Examples
____

Expand Down

0 comments on commit 9a3daee

Please sign in to comment.