[sshd agent] Introduce ConnectorDescriptor
Once a factory supports different SSH agents on the same platform, which is planned for Windows once we use Apache MINA sshd 2.8.0, client code may need to have a way to specify which SSH agent shall be used when the SSH config doesn't define anything. Add a mechanism by which a ConnectorFactory can tell what Connectors it may provide. Client code can use this to set the identityAgent parameter of ConnectorFactory.create() to the wanted default if it would be null otherwise. A ConnectorDescriptor is a pair of strings: an internal name, and a display name. The latter is included because client code might want to communicate agent names to the user, be it in error messages or in some chooser dialog where a user could define which of several alternative SSH agents should be used as default. The internal name is intended to be used in the IdentityAgent directive in ~/.ssh/config. Also make the ConnectorFactory discovered via the ServiceLoader accessible and overrideable. Provide static get/setDefault() methods, similar to the SshSessionFactory itself. Change-Id: Ie3d077395d32dfddc72bc8627e92b23636938182 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
b84738c369
commit
e7838b9c08
|
@ -13,4 +13,5 @@ msgSendFailed=Sending {0} bytes to SSH agent failed; {0} bytes not written
|
|||
msgSendFailed2=Sending {0} bytes to SSH agent failed: {1} - {2}
|
||||
msgSharedMemoryFailed=Could not set up shared memory for communicating with Pageant
|
||||
msgShortRead=Short read from SSH agent, expected {0} bytes, got {1} bytes; last read() returned {2}
|
||||
|
||||
pageant=Pageant
|
||||
unixDefaultAgent=ssh-agent
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.jgit.transport.sshd.agent.Connector;
|
||||
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
|
||||
|
@ -41,4 +43,26 @@ public boolean isSupported() {
|
|||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This factory returns on Windows a
|
||||
* {@link org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor
|
||||
* ConnectorDescriptor} for the internal name "pageant"; on Unix one for
|
||||
* "SSH_AUTH_SOCK".
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Collection<ConnectorDescriptor> getSupportedConnectors() {
|
||||
return Collections.singleton(getDefaultConnector());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectorDescriptor getDefaultConnector() {
|
||||
if (SystemReader.getInstance().isWindows()) {
|
||||
return PageantConnector.DESCRIPTOR;
|
||||
}
|
||||
return UnixDomainSocketConnector.DESCRIPTOR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,29 @@
|
|||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
|
||||
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor;
|
||||
|
||||
/**
|
||||
* A connector using Pageant's shared memory IPC mechanism.
|
||||
*/
|
||||
public class PageantConnector extends AbstractConnector {
|
||||
|
||||
/**
|
||||
* {@link ConnectorDescriptor} for the {@link PageantConnector}.
|
||||
*/
|
||||
public static final ConnectorDescriptor DESCRIPTOR = new ConnectorDescriptor() {
|
||||
|
||||
@Override
|
||||
public String getIdentityAgent() {
|
||||
return "pageant"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Texts.get().pageant;
|
||||
}
|
||||
};
|
||||
|
||||
private final PageantLibrary lib;
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,5 +42,7 @@ public static Texts get() {
|
|||
/***/ public String msgSendFailed2;
|
||||
/***/ public String msgSharedMemoryFailed;
|
||||
/***/ public String msgShortRead;
|
||||
/***/ public String pageant;
|
||||
/***/ public String unixDefaultAgent;
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
import org.apache.sshd.common.SshException;
|
||||
import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
|
||||
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor;
|
||||
import org.eclipse.jgit.util.StringUtils;
|
||||
import org.eclipse.jgit.util.SystemReader;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -38,6 +39,22 @@
|
|||
*/
|
||||
public class UnixDomainSocketConnector extends AbstractConnector {
|
||||
|
||||
/**
|
||||
* {@link ConnectorDescriptor} for the {@link UnixDomainSocketConnector}.
|
||||
*/
|
||||
public static final ConnectorDescriptor DESCRIPTOR = new ConnectorDescriptor() {
|
||||
|
||||
@Override
|
||||
public String getIdentityAgent() {
|
||||
return ENV_SSH_AUTH_SOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Texts.get().unixDefaultAgent;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Logger LOG = LoggerFactory
|
||||
.getLogger(UnixDomainSocketConnector.class);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
public final class ConnectorFactoryProvider {
|
||||
|
||||
private static final ConnectorFactory FACTORY = loadDefaultFactory();
|
||||
private static volatile ConnectorFactory INSTANCE = loadDefaultFactory();
|
||||
|
||||
private static ConnectorFactory loadDefaultFactory() {
|
||||
ServiceLoader<ConnectorFactory> loader = ServiceLoader
|
||||
|
@ -35,17 +35,27 @@ private static ConnectorFactory loadDefaultFactory() {
|
|||
|
||||
}
|
||||
|
||||
private ConnectorFactoryProvider() {
|
||||
// No instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default {@link ConnectorFactory} obtained via the
|
||||
* {@link ServiceLoader}.
|
||||
* Retrieves the currently set default {@link ConnectorFactory}.
|
||||
*
|
||||
* @return the {@link ConnectorFactory}, or {@code null} if none.
|
||||
*/
|
||||
public static ConnectorFactory getDefaultFactory() {
|
||||
return FACTORY;
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default {@link ConnectorFactory}.
|
||||
*
|
||||
* @param factory
|
||||
* {@link ConnectorFactory} to use, or {@code null} to use the
|
||||
* factory discovered via the {@link ServiceLoader}.
|
||||
*/
|
||||
public static void setDefaultFactory(ConnectorFactory factory) {
|
||||
INSTANCE = factory == null ? loadDefaultFactory() : factory;
|
||||
}
|
||||
|
||||
private ConnectorFactoryProvider() {
|
||||
// No instantiation
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
|
||||
import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
|
||||
import org.eclipse.jgit.internal.transport.sshd.SshdText;
|
||||
import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
|
||||
import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.SshConfigStore;
|
||||
|
@ -456,12 +455,15 @@ protected ServerKeyDatabase createServerKeyDatabase(@NonNull File homeDir,
|
|||
/**
|
||||
* Gets a {@link ConnectorFactory}. If this returns {@code null}, SSH agents
|
||||
* are not supported.
|
||||
* <p>
|
||||
* The default implementation uses {@link ConnectorFactory#getDefault()}
|
||||
* </p>
|
||||
*
|
||||
* @return the factory, or {@code null} if no SSH agent support is desired
|
||||
* @since 6.0
|
||||
*/
|
||||
protected ConnectorFactory getConnectorFactory() {
|
||||
return ConnectorFactoryProvider.getDefaultFactory();
|
||||
return ConnectorFactory.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jgit.annotations.NonNull;
|
||||
import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
|
||||
|
||||
/**
|
||||
* A factory for creating {@link Connector}s. This is a service provider
|
||||
|
@ -24,14 +26,45 @@
|
|||
*/
|
||||
public interface ConnectorFactory {
|
||||
|
||||
/**
|
||||
* Retrieves the currently set default {@link ConnectorFactory}. This is the
|
||||
* factory that is used unless overridden by the
|
||||
* {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
|
||||
*
|
||||
* @return the current default factory; may be {@code null} if none is set
|
||||
* and the {@link java.util.ServiceLoader} cannot find any suitable
|
||||
* implementation
|
||||
*/
|
||||
static ConnectorFactory getDefault() {
|
||||
return ConnectorFactoryProvider.getDefaultFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default {@link ConnectorFactory}. This is the factory that is used
|
||||
* unless overridden by the
|
||||
* {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
|
||||
* <p>
|
||||
* If no default factory is set programmatically, an implementation is
|
||||
* discovered via the {@link java.util.ServiceLoader}.
|
||||
* </p>
|
||||
*
|
||||
* @param factory
|
||||
* {@link ConnectorFactory} to set, or {@code null} to revert to
|
||||
* the default behavior of using the
|
||||
* {@link java.util.ServiceLoader}.
|
||||
*/
|
||||
static void setDefault(ConnectorFactory factory) {
|
||||
ConnectorFactoryProvider.setDefaultFactory(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Connector}.
|
||||
*
|
||||
* @param identityAgent
|
||||
* identifies the wanted agent connection; if {@code null}, the
|
||||
* factory is free to provide a {@link Connector} to a default
|
||||
* agent. The value will typically come from the IdentityAgent
|
||||
* setting in ~/.ssh/config.
|
||||
* agent. The value will typically come from the
|
||||
* {@code IdentityAgent} setting in {@code ~/.ssh/config}.
|
||||
* @param homeDir
|
||||
* the current local user's home directory as configured in the
|
||||
* {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}
|
||||
|
@ -58,4 +91,83 @@ Connector create(String identityAgent, File homeDir)
|
|||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* {@link ConnectorDescriptor}s describe available {@link Connector}s a
|
||||
* {@link ConnectorFactory} may provide.
|
||||
* <p>
|
||||
* A {@link ConnectorFactory} may support connecting to different SSH
|
||||
* agents. Agents are identified by name; a user can choose a specific agent
|
||||
* for instance via the {@code IdentityAgent} setting in
|
||||
* {@code ~/.ssh/config}.
|
||||
* </p>
|
||||
* <p>
|
||||
* OpenSSH knows two built-in names: "none" for not using any agent, and
|
||||
* "SSH_AUTH_SOCK" for using an agent that communicates over a Unix domain
|
||||
* socket given by the value of environment variable {@code SSH_AUTH_SOCK}.
|
||||
* Other agents can be specified in OpenSSH by specifying the socket file
|
||||
* directly. (The "standard" OpenBSD OpenSSH knows only this communication
|
||||
* mechanism.) "SSH_AUTH_SOCK" is also the default in OpenBSD OpenSSH if
|
||||
* nothing is configured.
|
||||
* </p>
|
||||
* <p>
|
||||
* A particular {@link ConnectorFactory} may support more communication
|
||||
* mechanisms or different agents. For instance, a factory on Windows might
|
||||
* support Pageant, Win32-OpenSSH, or even git bash ssh-agent, and might
|
||||
* accept internal names like "pageant", "openssh", "SSH_AUTH_SOCK" in
|
||||
* {@link ConnectorFactory#create(String, File)} to choose among them.
|
||||
* </p>
|
||||
* The {@link ConnectorDescriptor} interface and the
|
||||
* {@link ConnectorFactory#getSupportedConnectors()} and
|
||||
* {@link ConnectorFactory#getDefaultConnector()} methods provide a way for
|
||||
* code using a {@link ConnectorFactory} to learn what the factory supports
|
||||
* and thus implement some way by which a user can influence the default
|
||||
* behavior if {@code IdentityAgent} is not set or
|
||||
* {@link ConnectorFactory#create(String, File)} is called with
|
||||
* {@code identityAgent == null}.
|
||||
*/
|
||||
interface ConnectorDescriptor {
|
||||
|
||||
/**
|
||||
* Retrieves the internal name of a supported {@link Connector}. The
|
||||
* internal name is the one a user can specify for instance in the
|
||||
* {@code IdentityAgent} setting in {@code ~/.ssh/config} to select the
|
||||
* connector.
|
||||
*
|
||||
* @return the internal name; not empty
|
||||
*/
|
||||
@NonNull
|
||||
String getIdentityAgent();
|
||||
|
||||
/**
|
||||
* Retrieves a display name for a {@link Connector}, suitable for
|
||||
* showing in a UI.
|
||||
*
|
||||
* @return the display name; properly localized and not empty
|
||||
*/
|
||||
@NonNull
|
||||
String getDisplayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells which kinds of SSH agents this {@link ConnectorFactory} supports.
|
||||
* <p>
|
||||
* An implementation of this method should document the possible values it
|
||||
* returns.
|
||||
* </p>
|
||||
*
|
||||
* @return an immutable collection of {@link ConnectorDescriptor}s,
|
||||
* including {@link #getDefaultConnector()} and not including a
|
||||
* descriptor for internal name "none"
|
||||
*/
|
||||
@NonNull
|
||||
Collection<ConnectorDescriptor> getSupportedConnectors();
|
||||
|
||||
/**
|
||||
* Tells what kind of {@link Connector} this {@link ConnectorFactory}
|
||||
* creates if {@link ConnectorFactory#create(String, File)} is called with
|
||||
* {@code identityAgent == null}.
|
||||
*
|
||||
* @return a {@link ConnectorDescriptor} for the default connector
|
||||
*/
|
||||
ConnectorDescriptor getDefaultConnector();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
*/
|
||||
public abstract class SshSessionFactory {
|
||||
|
||||
private static SshSessionFactory INSTANCE = loadSshSessionFactory();
|
||||
private static volatile SshSessionFactory INSTANCE = loadSshSessionFactory();
|
||||
|
||||
private static SshSessionFactory loadSshSessionFactory() {
|
||||
ServiceLoader<SshSessionFactory> loader = ServiceLoader.load(SshSessionFactory.class);
|
||||
|
|
Loading…
Reference in New Issue