Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java')
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java257
1 files changed, 257 insertions, 0 deletions
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
index 420a1d16eb..0d6f3027f2 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
@@ -18,21 +18,30 @@ import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
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;
import org.apache.sshd.common.PropertyResolverUtils;
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;
@@ -68,6 +77,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
* @throws Exception
@@ -77,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.
*
@@ -419,4 +558,122 @@ public class JGitClientSession extends ClientSessionImpl {
return b.toString();
}
+ @Override
+ public <T> T getAttribute(AttributeKey<T> key) {
+ T value = super.getAttribute(key);
+ if (value == null) {
+ IoSession ioSession = getIoSession();
+ if (ioSession != null) {
+ Object obj = ioSession.getAttribute(AttributeRepository.class);
+ if (obj instanceof AttributeRepository) {
+ AttributeRepository sessionAttributes = (AttributeRepository) obj;
+ value = sessionAttributes.resolveAttribute(key);
+ }
+ }
+ }
+ return value;
+ }
+
+ @Override
+ public PropertyResolver getParentPropertyResolver() {
+ IoSession ioSession = getIoSession();
+ if (ioSession != null) {
+ Object obj = ioSession.getAttribute(AttributeRepository.class);
+ if (obj instanceof PropertyResolver) {
+ return (PropertyResolver) obj;
+ }
+ }
+ return super.getParentPropertyResolver();
+ }
+
+ /**
+ * An {@link AttributeRepository} that chains together two other attribute
+ * sources in a hierarchy.
+ */
+ public static class ChainingAttributes implements AttributeRepository {
+
+ private final AttributeRepository delegate;
+
+ private final AttributeRepository parent;
+
+ /**
+ * Create a new {@link ChainingAttributes} attribute source.
+ *
+ * @param self
+ * to search for attributes first
+ * @param parent
+ * to search for attributes if not found in {@code self}
+ */
+ public ChainingAttributes(AttributeRepository self,
+ AttributeRepository parent) {
+ this.delegate = self;
+ this.parent = parent;
+ }
+
+ @Override
+ public int getAttributesCount() {
+ return delegate.getAttributesCount();
+ }
+
+ @Override
+ public <T> T getAttribute(AttributeKey<T> key) {
+ return delegate.getAttribute(Objects.requireNonNull(key));
+ }
+
+ @Override
+ public Collection<AttributeKey<?>> attributeKeys() {
+ return delegate.attributeKeys();
+ }
+
+ @Override
+ public <T> T resolveAttribute(AttributeKey<T> key) {
+ T value = getAttribute(Objects.requireNonNull(key));
+ if (value == null) {
+ return parent.getAttribute(key);
+ }
+ return value;
+ }
+ }
+
+ /**
+ * A {@link ChainingAttributes} repository that doubles as a
+ * {@link PropertyResolver}. The property map can be set via the attribute
+ * key {@link SessionAttributes#PROPERTIES}.
+ */
+ public static class SessionAttributes extends ChainingAttributes
+ implements PropertyResolver {
+
+ /** Key for storing a map of properties in the attributes. */
+ public static final AttributeKey<Map<String, Object>> PROPERTIES = new AttributeKey<>();
+
+ private final PropertyResolver parentProperties;
+
+ /**
+ * Creates a new {@link SessionAttributes} attribute and property
+ * source.
+ *
+ * @param self
+ * to search for attributes first
+ * @param parent
+ * to search for attributes if not found in {@code self}
+ * @param parentProperties
+ * to search for properties if not found in {@code self}
+ */
+ public SessionAttributes(AttributeRepository self,
+ AttributeRepository parent, PropertyResolver parentProperties) {
+ super(self, parent);
+ this.parentProperties = parentProperties;
+ }
+
+ @Override
+ public PropertyResolver getParentPropertyResolver() {
+ return parentProperties;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ Map<String, Object> props = getAttribute(PROPERTIES);
+ return props == null ? Collections.emptyMap() : props;
+ }
+ }
}

Back to the top