Merge "Support CredentialsProvider for SSH connections"
This commit is contained in:
commit
573666403d
|
@ -211,6 +211,60 @@ public void setValueNoCopy(char[] newValue) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** An item whose value is a boolean choice, presented as Yes/No. */
|
||||||
|
public static class YesNoType extends CredentialItem {
|
||||||
|
private boolean value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a prompt for a single boolean answer.
|
||||||
|
*
|
||||||
|
* @param promptText
|
||||||
|
* prompt to display to the user alongside of the input
|
||||||
|
* field. Should be sufficient text to indicate what to
|
||||||
|
* supply for this item.
|
||||||
|
*/
|
||||||
|
public YesNoType(String promptText) {
|
||||||
|
super(promptText, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the current value */
|
||||||
|
public boolean getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the new value.
|
||||||
|
*
|
||||||
|
* @param newValue
|
||||||
|
*/
|
||||||
|
public void setValue(boolean newValue) {
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An advice message presented to the user, with no response required. */
|
||||||
|
public static class InformationalMessage extends CredentialItem {
|
||||||
|
/**
|
||||||
|
* Initialize an informational message.
|
||||||
|
*
|
||||||
|
* @param messageText
|
||||||
|
* message to display to the user.
|
||||||
|
*/
|
||||||
|
public InformationalMessage(String messageText) {
|
||||||
|
super(messageText, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
// Nothing to clear.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Prompt for a username, which is not masked on input. */
|
/** Prompt for a username, which is not masked on input. */
|
||||||
public static class Username extends StringType {
|
public static class Username extends StringType {
|
||||||
/** Initialize a new username item, with a default username prompt. */
|
/** Initialize a new username item, with a default username prompt. */
|
||||||
|
|
|
@ -80,6 +80,17 @@ public static void setDefault(CredentialsProvider p) {
|
||||||
defaultProvider = p;
|
defaultProvider = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the provider is interactive with the end-user.
|
||||||
|
*
|
||||||
|
* An interactive provider may try to open a dialog box, or prompt for input
|
||||||
|
* on the terminal, and will wait for a user response. A non-interactive
|
||||||
|
* provider will either populate CredentialItems, or fail.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the provider is interactive with the end-user.
|
||||||
|
*/
|
||||||
|
public abstract boolean isInteractive();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the provider can supply the necessary {@link CredentialItem}s.
|
* Check if the provider can supply the necessary {@link CredentialItem}s.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.transport;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.Session;
|
||||||
|
import com.jcraft.jsch.UIKeyboardInteractive;
|
||||||
|
import com.jcraft.jsch.UserInfo;
|
||||||
|
|
||||||
|
/** A JSch {@link UserInfo} adapter for a {@link CredentialsProvider}. */
|
||||||
|
public class CredentialsProviderUserInfo implements UserInfo,
|
||||||
|
UIKeyboardInteractive {
|
||||||
|
private final URIish uri;
|
||||||
|
|
||||||
|
private final CredentialsProvider provider;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String passphrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a CredentialsProvider to make it suitable for use with JSch.
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* the JSch session this UserInfo will support authentication on.
|
||||||
|
* @param credentialsProvider
|
||||||
|
* the provider that will perform the authentication.
|
||||||
|
*/
|
||||||
|
public CredentialsProviderUserInfo(Session session,
|
||||||
|
CredentialsProvider credentialsProvider) {
|
||||||
|
this.uri = createURI(session);
|
||||||
|
this.provider = credentialsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URIish createURI(Session session) {
|
||||||
|
URIish uri = new URIish();
|
||||||
|
uri = uri.setScheme("ssh");
|
||||||
|
uri = uri.setUser(session.getUserName());
|
||||||
|
uri = uri.setHost(session.getHost());
|
||||||
|
uri = uri.setPort(session.getPort());
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassphrase() {
|
||||||
|
return passphrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean promptPassphrase(String msg) {
|
||||||
|
CredentialItem.StringType v = newPrompt(msg);
|
||||||
|
if (provider.get(uri, v)) {
|
||||||
|
passphrase = v.getValue();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
passphrase = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean promptPassword(String msg) {
|
||||||
|
CredentialItem.StringType v = newPrompt(msg);
|
||||||
|
if (provider.get(uri, v)) {
|
||||||
|
password = v.getValue();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
password = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CredentialItem.StringType newPrompt(String msg) {
|
||||||
|
return new CredentialItem.StringType(msg, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean promptYesNo(String msg) {
|
||||||
|
CredentialItem.YesNoType v = new CredentialItem.YesNoType(msg);
|
||||||
|
return provider.get(uri, v) && v.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showMessage(String msg) {
|
||||||
|
provider.get(uri, new CredentialItem.InformationalMessage(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] promptKeyboardInteractive(String destination, String name,
|
||||||
|
String instruction, String[] prompt, boolean[] echo) {
|
||||||
|
CredentialItem.StringType[] v = new CredentialItem.StringType[prompt.length];
|
||||||
|
for (int i = 0; i < prompt.length; i++)
|
||||||
|
v[i] = new CredentialItem.StringType(prompt[i], !echo[i]);
|
||||||
|
|
||||||
|
List<CredentialItem> items = new ArrayList<CredentialItem>();
|
||||||
|
if (instruction != null && instruction.length() > 0)
|
||||||
|
items.add(new CredentialItem.InformationalMessage(instruction));
|
||||||
|
items.addAll(Arrays.asList(v));
|
||||||
|
|
||||||
|
if (!provider.get(uri, items))
|
||||||
|
return null; // cancel
|
||||||
|
|
||||||
|
String[] result = new String[v.length];
|
||||||
|
for (int i = 0; i < v.length; i++)
|
||||||
|
result[i] = v[i].getValue();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,7 +83,8 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized Session getSession(String user, String pass,
|
public synchronized Session getSession(String user, String pass,
|
||||||
String host, int port, FS fs) throws JSchException {
|
String host, int port, CredentialsProvider credentialsProvider,
|
||||||
|
FS fs) throws JSchException {
|
||||||
if (config == null)
|
if (config == null)
|
||||||
config = OpenSshConfig.get(fs);
|
config = OpenSshConfig.get(fs);
|
||||||
|
|
||||||
|
@ -105,6 +106,11 @@ public synchronized Session getSession(String user, String pass,
|
||||||
final String pauth = hc.getPreferredAuthentications();
|
final String pauth = hc.getPreferredAuthentications();
|
||||||
if (pauth != null)
|
if (pauth != null)
|
||||||
session.setConfig("PreferredAuthentications", pauth);
|
session.setConfig("PreferredAuthentications", pauth);
|
||||||
|
if (credentialsProvider != null
|
||||||
|
&& (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
|
||||||
|
session.setUserInfo(new CredentialsProviderUserInfo(session,
|
||||||
|
credentialsProvider));
|
||||||
|
}
|
||||||
configure(hc, session);
|
configure(hc, session);
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,8 @@ public static void setInstance(final SshSessionFactory newFactory) {
|
||||||
* @param port
|
* @param port
|
||||||
* port number the server is listening for connections on. May be <=
|
* port number the server is listening for connections on. May be <=
|
||||||
* 0 to indicate the IANA registered port of 22 should be used.
|
* 0 to indicate the IANA registered port of 22 should be used.
|
||||||
|
* @param credentialsProvider
|
||||||
|
* provider to support authentication, may be null.
|
||||||
* @param fs
|
* @param fs
|
||||||
* the file system abstraction which will be necessary to
|
* the file system abstraction which will be necessary to
|
||||||
* perform certain file system operations.
|
* perform certain file system operations.
|
||||||
|
@ -119,14 +121,16 @@ public static void setInstance(final SshSessionFactory newFactory) {
|
||||||
* the session could not be created.
|
* the session could not be created.
|
||||||
*/
|
*/
|
||||||
public abstract Session getSession(String user, String pass, String host,
|
public abstract Session getSession(String user, String pass, String host,
|
||||||
int port, FS fs) throws JSchException;
|
int port, CredentialsProvider credentialsProvider, FS fs)
|
||||||
|
throws JSchException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close (or recycle) a session to a host.
|
* Close (or recycle) a session to a host.
|
||||||
*
|
*
|
||||||
* @param session
|
* @param session
|
||||||
* a session previously obtained from this factory's
|
* a session previously obtained from this factory's
|
||||||
* {@link #getSession(String,String, String, int, FS)} method.s
|
* {@link #getSession(String,String, String, int, CredentialsProvider, FS)}
|
||||||
|
* method.
|
||||||
*/
|
*/
|
||||||
public void releaseSession(final Session session) {
|
public void releaseSession(final Session session) {
|
||||||
if (session.isConnected())
|
if (session.isConnected())
|
||||||
|
|
|
@ -128,7 +128,8 @@ protected void initSession() throws TransportException {
|
||||||
final String host = uri.getHost();
|
final String host = uri.getHost();
|
||||||
final int port = uri.getPort();
|
final int port = uri.getPort();
|
||||||
try {
|
try {
|
||||||
sock = sch.getSession(user, pass, host, port, local.getFS());
|
sock = sch.getSession(user, pass, host, port,
|
||||||
|
getCredentialsProvider(), local.getFS());
|
||||||
if (!sock.isConnected())
|
if (!sock.isConnected())
|
||||||
sock.connect(tms);
|
sock.connect(tms);
|
||||||
} catch (JSchException je) {
|
} catch (JSchException je) {
|
||||||
|
|
|
@ -76,6 +76,11 @@ public UsernamePasswordCredentialsProvider(String username, char[] password) {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInteractive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(CredentialItem... items) {
|
public boolean supports(CredentialItem... items) {
|
||||||
for (CredentialItem i : items) {
|
for (CredentialItem i : items) {
|
||||||
|
|
Loading…
Reference in New Issue