When setting up an SSH connection, use the caller supplied CredentialsProvider, if one has been given to the Transport or was defined as the default. The CredentialsProvider is re-wrapped as a JSch UserInfo, allowing the connection to use this for user interactive prompts. This give a unified API for authentication on any transport type. Change-Id: Id3b4cf5bfd27a23207cdfb188bae3b78e71e02c0 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>tags/v0.10.1
} | } | ||||
} | } | ||||
/** 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. */ |
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. | ||||
* | * |
/* | |||||
* 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; | |||||
} | |||||
} |
@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); | ||||
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; | ||||
} | } |
* @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. | ||||
* 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()) |
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) { |
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) { |