mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
UserAuthImpl made plenty cleaner...
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.userauth;
|
package net.schmizz.sshj.userauth;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Promise;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.Service;
|
import net.schmizz.sshj.Service;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
@@ -36,71 +36,75 @@ import java.util.concurrent.TimeUnit;
|
|||||||
/** {@link UserAuth} implementation. */
|
/** {@link UserAuth} implementation. */
|
||||||
public class UserAuthImpl
|
public class UserAuthImpl
|
||||||
extends AbstractService
|
extends AbstractService
|
||||||
implements UserAuth, AuthParams {
|
implements UserAuth {
|
||||||
|
|
||||||
private final Set<String> allowed = new HashSet<String>();
|
private final Event<UserAuthException> authenticated
|
||||||
|
= new Event<UserAuthException>("authenticated", UserAuthException.chainer);
|
||||||
|
|
||||||
|
// Externally available
|
||||||
private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>();
|
private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>();
|
||||||
|
private volatile String banner = "";
|
||||||
private final Promise<Boolean, UserAuthException> result
|
|
||||||
= new Promise<Boolean, UserAuthException>("userauth result", UserAuthException.chainer);
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
private AuthMethod currentMethod;
|
|
||||||
private Service nextService;
|
|
||||||
|
|
||||||
private boolean firstAttempt = true;
|
|
||||||
|
|
||||||
private volatile String banner;
|
|
||||||
private volatile boolean partialSuccess;
|
private volatile boolean partialSuccess;
|
||||||
|
|
||||||
|
// Internal state
|
||||||
|
private Set<String> allowedMethods;
|
||||||
|
private AuthMethod currentMethod;
|
||||||
|
|
||||||
public UserAuthImpl(Transport trans) {
|
public UserAuthImpl(Transport trans) {
|
||||||
super("ssh-userauth", trans);
|
super("ssh-userauth", trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
// synchronized for mutual exclusion; ensure one authenticate() ever in progress
|
// synchronized for mutual exclusion; ensure only one authenticate() ever in progress
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void authenticate(String username, Service nextService, Iterable<AuthMethod> methods)
|
public synchronized void authenticate(final String username,
|
||||||
|
final Service nextService,
|
||||||
|
final Iterable<AuthMethod> methods)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
clearState();
|
savedEx.clear();
|
||||||
|
|
||||||
this.username = username;
|
|
||||||
this.nextService = nextService;
|
|
||||||
|
|
||||||
// Request "ssh-userauth" service (if not already active)
|
// Request "ssh-userauth" service (if not already active)
|
||||||
request();
|
super.request();
|
||||||
|
|
||||||
if (firstAttempt) { // Assume all allowed
|
if (allowedMethods == null) { // Assume all are allowed
|
||||||
|
allowedMethods = new HashSet<String>();
|
||||||
for (AuthMethod meth : methods)
|
for (AuthMethod meth : methods)
|
||||||
allowed.add(meth.getName());
|
allowedMethods.add(meth.getName());
|
||||||
firstAttempt = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
for (AuthMethod meth : methods)
|
final AuthParams authParams = makeAuthParams(username, nextService);
|
||||||
|
|
||||||
if (allowed.contains(meth.getName())) {
|
for (AuthMethod meth : methods) {
|
||||||
|
|
||||||
log.info("Trying `{}` auth...", meth.getName());
|
if (!allowedMethods.contains(meth.getName())) {
|
||||||
|
saveException(new UserAuthException(meth.getName() + " auth not allowed by server"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
boolean success = false;
|
log.info("Trying `{}` auth...", meth.getName());
|
||||||
try {
|
authenticated.clear();
|
||||||
success = tryWith(meth);
|
currentMethod = meth;
|
||||||
} catch (UserAuthException e) {
|
|
||||||
// Give other method a shot
|
|
||||||
saveException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
try {
|
||||||
log.info("`{}` auth successful", meth.getName());
|
|
||||||
return;
|
|
||||||
} else
|
|
||||||
log.info("`{}` auth failed", meth.getName());
|
|
||||||
|
|
||||||
} else
|
currentMethod.init(authParams);
|
||||||
saveException(meth.getName() + " auth not allowed by server");
|
currentMethod.request();
|
||||||
|
authenticated.await(timeout, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
} catch (UserAuthException e) {
|
||||||
|
log.info("`{}` auth failed", meth.getName());
|
||||||
|
// Give other methods a shot
|
||||||
|
saveException(e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("`{}` auth successful", meth.getName());
|
||||||
|
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
||||||
|
trans.setService(nextService); // We aren't in charge anymore, next service is
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
currentMethod = null;
|
currentMethod = null;
|
||||||
@@ -111,34 +115,13 @@ public class UserAuthImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBanner() {
|
public synchronized Deque<UserAuthException> getSavedExceptions() {
|
||||||
return banner;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNextServiceName() {
|
|
||||||
return nextService.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport() {
|
|
||||||
return trans;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the exceptions that occured during authentication process but were ignored because more method were
|
|
||||||
* available for trying.
|
|
||||||
*
|
|
||||||
* @return deque of saved exceptions
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Deque<UserAuthException> getSavedExceptions() {
|
|
||||||
return savedEx;
|
return savedEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getBanner() {
|
||||||
return username;
|
return banner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,75 +136,63 @@ public class UserAuthImpl
|
|||||||
throw new TransportException(DisconnectReason.PROTOCOL_ERROR);
|
throw new TransportException(DisconnectReason.PROTOCOL_ERROR);
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case USERAUTH_BANNER:
|
|
||||||
gotBanner(buf);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USERAUTH_SUCCESS:
|
case USERAUTH_BANNER: {
|
||||||
gotSuccess();
|
banner = buf.readString();
|
||||||
break;
|
} break;
|
||||||
|
|
||||||
case USERAUTH_FAILURE:
|
case USERAUTH_SUCCESS: {
|
||||||
gotFailure(buf);
|
authenticated.set();
|
||||||
break;
|
} break;
|
||||||
|
|
||||||
|
case USERAUTH_FAILURE: {
|
||||||
|
allowedMethods.clear();
|
||||||
|
allowedMethods.addAll(Arrays.<String>asList(buf.readString().split(",")));
|
||||||
|
partialSuccess |= buf.readBoolean();
|
||||||
|
if (allowedMethods.contains(currentMethod.getName()) && currentMethod.shouldRetry()) {
|
||||||
|
currentMethod.request();
|
||||||
|
} else {
|
||||||
|
authenticated.deliverError(new UserAuthException(currentMethod.getName() + " auth failed"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);
|
||||||
|
try {
|
||||||
|
currentMethod.handle(msg, buf);
|
||||||
|
} catch (UserAuthException e) {
|
||||||
|
authenticated.deliverError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
gotUnknown(msg, buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyError(SSHException error) {
|
public void notifyError(SSHException error) {
|
||||||
super.notifyError(error);
|
super.notifyError(error);
|
||||||
result.deliverError(error);
|
authenticated.deliverError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearState() {
|
private AuthParams makeAuthParams(final String username, final Service nextService) {
|
||||||
allowed.clear();
|
return new AuthParams() {
|
||||||
savedEx.clear();
|
|
||||||
banner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void gotBanner(SSHPacket buf) {
|
@Override
|
||||||
banner = buf.readString();
|
public String getNextServiceName() {
|
||||||
}
|
return nextService.getName();
|
||||||
|
}
|
||||||
|
|
||||||
private void gotFailure(SSHPacket buf)
|
@Override
|
||||||
throws UserAuthException, TransportException {
|
public Transport getTransport() {
|
||||||
allowed.clear();
|
return trans;
|
||||||
allowed.addAll(Arrays.<String>asList(buf.readString().split(",")));
|
}
|
||||||
partialSuccess |= buf.readBoolean();
|
|
||||||
if (allowed.contains(currentMethod.getName()) && currentMethod.shouldRetry())
|
|
||||||
currentMethod.request();
|
|
||||||
else {
|
|
||||||
saveException(currentMethod.getName() + " auth failed");
|
|
||||||
result.deliver(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void gotSuccess() {
|
@Override
|
||||||
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
public String getUsername() {
|
||||||
trans.setService(nextService); // We aren't in charge anymore, next service is
|
return username;
|
||||||
result.deliver(true);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void gotUnknown(Message msg, SSHPacket buf)
|
};
|
||||||
throws SSHException {
|
|
||||||
if (currentMethod == null || result == null) {
|
|
||||||
trans.sendUnimplemented();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("Asking {} method to handle {} packet", currentMethod.getName(), msg);
|
|
||||||
try {
|
|
||||||
currentMethod.handle(msg, buf);
|
|
||||||
} catch (UserAuthException e) {
|
|
||||||
result.deliverError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveException(String msg) {
|
|
||||||
saveException(new UserAuthException(msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveException(UserAuthException e) {
|
private void saveException(UserAuthException e) {
|
||||||
@@ -229,13 +200,4 @@ public class UserAuthImpl
|
|||||||
savedEx.push(e);
|
savedEx.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryWith(AuthMethod meth)
|
|
||||||
throws UserAuthException, TransportException {
|
|
||||||
currentMethod = meth;
|
|
||||||
result.clear();
|
|
||||||
meth.init(this);
|
|
||||||
meth.request();
|
|
||||||
return result.retrieve(timeout, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user