|
|
@@ -29,8 +29,10 @@ import java.util.Set; |
|
|
|
|
|
|
|
import org.apache.sshd.client.ClientFactoryManager; |
|
|
|
import org.apache.sshd.client.config.hosts.HostConfigEntry; |
|
|
|
import org.apache.sshd.client.future.AuthFuture; |
|
|
|
import org.apache.sshd.client.keyverifier.ServerKeyVerifier; |
|
|
|
import org.apache.sshd.client.session.ClientSessionImpl; |
|
|
|
import org.apache.sshd.client.session.ClientUserAuthService; |
|
|
|
import org.apache.sshd.common.AttributeRepository; |
|
|
|
import org.apache.sshd.common.FactoryManager; |
|
|
|
import org.apache.sshd.common.PropertyResolver; |
|
|
@@ -39,6 +41,7 @@ import org.apache.sshd.common.SshException; |
|
|
|
import org.apache.sshd.common.config.keys.KeyUtils; |
|
|
|
import org.apache.sshd.common.io.IoSession; |
|
|
|
import org.apache.sshd.common.io.IoWriteFuture; |
|
|
|
import org.apache.sshd.common.kex.KexState; |
|
|
|
import org.apache.sshd.common.util.Readable; |
|
|
|
import org.apache.sshd.common.util.buffer.Buffer; |
|
|
|
import org.eclipse.jgit.errors.InvalidPatternException; |
|
|
@@ -73,6 +76,17 @@ public class JGitClientSession extends ClientSessionImpl { |
|
|
|
|
|
|
|
private volatile StatefulProxyConnector proxyHandler; |
|
|
|
|
|
|
|
/** |
|
|
|
* Work-around for bug 565394 / SSHD-1050; remove when using sshd 2.6.0. |
|
|
|
*/ |
|
|
|
private volatile AuthFuture authFuture; |
|
|
|
|
|
|
|
/** Records exceptions before there is an authFuture. */ |
|
|
|
private List<Throwable> earlyErrors = new ArrayList<>(); |
|
|
|
|
|
|
|
/** Guards setting an earlyError and the authFuture together. */ |
|
|
|
private final Object errorLock = new Object(); |
|
|
|
|
|
|
|
/** |
|
|
|
* @param manager |
|
|
|
* @param session |
|
|
@@ -83,6 +97,125 @@ public class JGitClientSession extends ClientSessionImpl { |
|
|
|
super(manager, session); |
|
|
|
} |
|
|
|
|
|
|
|
// BEGIN Work-around for bug 565394 / SSHD-1050 |
|
|
|
// Remove when using sshd 2.6.0. |
|
|
|
|
|
|
|
@Override |
|
|
|
public AuthFuture auth() throws IOException { |
|
|
|
if (getUsername() == null) { |
|
|
|
throw new IllegalStateException( |
|
|
|
SshdText.get().sessionWithoutUsername); |
|
|
|
} |
|
|
|
ClientUserAuthService authService = getUserAuthService(); |
|
|
|
String serviceName = nextServiceName(); |
|
|
|
List<Throwable> errors = null; |
|
|
|
AuthFuture future; |
|
|
|
// Guard both getting early errors and setting authFuture |
|
|
|
synchronized (errorLock) { |
|
|
|
future = authService.auth(serviceName); |
|
|
|
if (future == null) { |
|
|
|
// Internal error; no translation. |
|
|
|
throw new IllegalStateException( |
|
|
|
"No auth future generated by service '" //$NON-NLS-1$ |
|
|
|
+ serviceName + '\''); |
|
|
|
} |
|
|
|
errors = earlyErrors; |
|
|
|
earlyErrors = null; |
|
|
|
authFuture = future; |
|
|
|
} |
|
|
|
if (errors != null && !errors.isEmpty()) { |
|
|
|
Iterator<Throwable> iter = errors.iterator(); |
|
|
|
Throwable first = iter.next(); |
|
|
|
iter.forEachRemaining(t -> { |
|
|
|
if (t != first && t != null) { |
|
|
|
first.addSuppressed(t); |
|
|
|
} |
|
|
|
}); |
|
|
|
// Mark the future as having had an exception; just to be on the |
|
|
|
// safe side. Actually, there shouldn't be anyone waiting on this |
|
|
|
// future yet. |
|
|
|
future.setException(first); |
|
|
|
if (log.isDebugEnabled()) { |
|
|
|
log.debug("auth({}) early exception type={}: {}", //$NON-NLS-1$ |
|
|
|
this, first.getClass().getSimpleName(), |
|
|
|
first.getMessage()); |
|
|
|
} |
|
|
|
if (first instanceof SshException) { |
|
|
|
throw new SshException( |
|
|
|
((SshException) first).getDisconnectCode(), |
|
|
|
first.getMessage(), first); |
|
|
|
} |
|
|
|
throw new IOException(first.getMessage(), first); |
|
|
|
} |
|
|
|
return future; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void signalAuthFailure(AuthFuture future, Throwable t) { |
|
|
|
signalAuthFailure(t); |
|
|
|
} |
|
|
|
|
|
|
|
private void signalAuthFailure(Throwable t) { |
|
|
|
AuthFuture future = authFuture; |
|
|
|
if (future == null) { |
|
|
|
synchronized (errorLock) { |
|
|
|
if (earlyErrors != null) { |
|
|
|
earlyErrors.add(t); |
|
|
|
} |
|
|
|
future = authFuture; |
|
|
|
} |
|
|
|
} |
|
|
|
if (future != null) { |
|
|
|
future.setException(t); |
|
|
|
} |
|
|
|
if (log.isDebugEnabled()) { |
|
|
|
boolean signalled = future != null && t == future.getException(); |
|
|
|
log.debug("signalAuthFailure({}) type={}, signalled={}: {}", this, //$NON-NLS-1$ |
|
|
|
t.getClass().getSimpleName(), Boolean.valueOf(signalled), |
|
|
|
t.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void exceptionCaught(Throwable t) { |
|
|
|
signalAuthFailure(t); |
|
|
|
super.exceptionCaught(t); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void preClose() { |
|
|
|
signalAuthFailure( |
|
|
|
new SshException(SshdText.get().authenticationOnClosedSession)); |
|
|
|
super.preClose(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void handleDisconnect(int code, String msg, String lang, |
|
|
|
Buffer buffer) throws Exception { |
|
|
|
signalAuthFailure(new SshException(code, msg)); |
|
|
|
super.handleDisconnect(code, msg, lang, buffer); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected <C extends Collection<ClientSessionEvent>> C updateCurrentSessionState( |
|
|
|
C newState) { |
|
|
|
if (closeFuture.isClosed()) { |
|
|
|
newState.add(ClientSessionEvent.CLOSED); |
|
|
|
} |
|
|
|
if (isAuthenticated()) { // authFuture.isSuccess() |
|
|
|
newState.add(ClientSessionEvent.AUTHED); |
|
|
|
} |
|
|
|
if (KexState.DONE.equals(getKexState())) { |
|
|
|
AuthFuture future = authFuture; |
|
|
|
if (future == null || future.isFailure()) { |
|
|
|
newState.add(ClientSessionEvent.WAIT_AUTH); |
|
|
|
} |
|
|
|
} |
|
|
|
return newState; |
|
|
|
} |
|
|
|
|
|
|
|
// END Work-around for bug 565394 / SSHD-1050 |
|
|
|
|
|
|
|
/** |
|
|
|
* Retrieves the {@link HostConfigEntry} this session was created for. |
|
|
|
* |