Skip to content

Commit

Permalink
[AS7-2658] Better error message to indicate authentication failure.
Browse files Browse the repository at this point in the history
  • Loading branch information
darranl committed Nov 22, 2011
1 parent d38d74d commit 4a2d981
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 35 deletions.
99 changes: 76 additions & 23 deletions cli/src/main/java/org/jboss/as/cli/CommandLineMain.java
Expand Up @@ -33,6 +33,7 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.AccessController;
import java.security.PrivilegedAction;
Expand All @@ -49,6 +50,7 @@
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.RealmChoiceCallback;
import javax.security.sasl.SaslException;

import org.jboss.as.cli.batch.Batch;
import org.jboss.as.cli.batch.BatchManager;
Expand Down Expand Up @@ -101,12 +103,14 @@
import org.jboss.as.cli.operation.impl.DefaultCallbackHandler;
import org.jboss.as.cli.operation.impl.DefaultOperationCandidatesProvider;
import org.jboss.as.cli.operation.impl.DefaultOperationRequestAddress;
import org.jboss.as.cli.operation.impl.DefaultOperationRequestBuilder;
import org.jboss.as.cli.operation.impl.DefaultOperationRequestParser;
import org.jboss.as.cli.operation.impl.DefaultPrefixFormatter;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.protocol.StreamUtils;
import org.jboss.dmr.ModelNode;
import org.jboss.sasl.JBossSaslProvider;
import org.jboss.sasl.callback.DigestHashCallback;

/**
*
Expand Down Expand Up @@ -779,39 +783,73 @@ public void connectController(String host, int port) {
port = defaultControllerPort;
}

try {
CallbackHandler cbh = new AuthenticationCallbackHandler();
ModelControllerClient newClient = ModelControllerClient.Factory.create(host, port, cbh);
if(this.client != null) {
disconnectController();
try {
ModelControllerClient newClient = null;

CallbackHandler cbh = new AuthenticationCallbackHandler(username, password);
ModelControllerClient tempClient = ModelControllerClient.Factory.create(host, port, cbh);
switch (initialConnection(tempClient)) {
case SUCCESS:
newClient = tempClient;
break;
case CONNECTION_FAILURE:
printLine("The controller is not available at " + host + ":" + port);
break;
case AUTHENTICATION_FAILURE:
printLine("Unable to authenticate against controller at " + host + ":" + port);
break;
}

client = newClient;
this.controllerHost = host;
this.controllerPort = port;
if (newClient != null) {
if (this.client != null) {
disconnectController();
}

List<String> nodeTypes = Util.getNodeTypes(newClient, new DefaultOperationRequestAddress());
if (!nodeTypes.isEmpty()) {
client = newClient;
this.controllerHost = host;
this.controllerPort = port;

List<String> nodeTypes = Util.getNodeTypes(newClient, new DefaultOperationRequestAddress());
domainMode = nodeTypes.contains("server-group");
// printLine("Connected to "
// + (domainMode ? "domain controller at " : "standalone controller at ")
// + host + ":" + port);
} else {
printLine("The controller is not available at " + host + ":" + port);
disconnectController();
}
} catch (UnknownHostException e) {
printLine("Failed to resolve host '" + host + "': " + e.getLocalizedMessage());
}
}

/**
* Used to make a call to the server to verify that it is possible to connect.
*/
private ConnectStatus initialConnection(final ModelControllerClient client) {
try {
DefaultOperationRequestBuilder builder = new DefaultOperationRequestBuilder();
builder.setOperationName("read-attribute");
builder.addProperty("name", "name");

client.execute(builder.buildRequest());
// We don't actually care what the response is we just want to be sure the ModelControllerClient
// does not throw an Exception.
return ConnectStatus.SUCCESS;
} catch (Exception e) {
boolean authenticationFailure = false;

Throwable current = e;
while (current != null && authenticationFailure == false) {
if (current instanceof SaslException) {
authenticationFailure = true;
}
current = current.getCause();
}

StreamUtils.safeClose(client);
return authenticationFailure ? ConnectStatus.AUTHENTICATION_FAILURE : ConnectStatus.CONNECTION_FAILURE;
}
}

@Override
public void disconnectController() {
if(this.client != null) {
StreamUtils.safeClose(client);
// if(loggingEnabled) {
// printLine("Closed connection to " + this.controllerHost + ':' + this.controllerPort);
// }
client = null;
this.controllerHost = null;
this.controllerPort = -1;
Expand Down Expand Up @@ -986,6 +1024,10 @@ protected void setOutputTarget(String filePath) {
this.outputTarget = new BufferedWriter(writer);
}

private enum ConnectStatus {
SUCCESS, AUTHENTICATION_FAILURE, CONNECTION_FAILURE
}

private class AuthenticationCallbackHandler implements CallbackHandler {

// After the CLI has connected the physical connection may be re-established numerous times.
Expand All @@ -997,12 +1039,18 @@ private class AuthenticationCallbackHandler implements CallbackHandler {

private String username;
private char[] password;
private String digest;

private AuthenticationCallbackHandler() {
private AuthenticationCallbackHandler(String username, char[] password) {
// A local cache is used for scenarios where no values are specified on the command line
// and the user wishes to use the connect command to establish a new connection.
username = CommandContextImpl.this.username;
password = CommandContextImpl.this.password;
this.username = username;
this.password = password;
}

private AuthenticationCallbackHandler(String username, String digest) {
this.username = username;
this.digest = digest;
}

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
Expand All @@ -1027,7 +1075,8 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
username = readLine("Username: ", false, true);
}
ncb.setName(username);
} else if (current instanceof PasswordCallback) {
} else if (current instanceof PasswordCallback && digest == null) {
// If a digest had been set support for PasswordCallback is disabled.
PasswordCallback pcb = (PasswordCallback) current;
if (password == null) {
showRealm();
Expand All @@ -1037,6 +1086,10 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
}
}
pcb.setPassword(password);
} else if (current instanceof DigestHashCallback && digest != null) {
// We don't support an interactive use of this callback so it must have been set in advance.
DigestHashCallback dhc = (DigestHashCallback) current;
dhc.setHexHash(digest);
} else {
printLine("Unexpected Callback " + current.getClass().getName());
throw new UnsupportedCallbackException(current);
Expand Down
Expand Up @@ -26,6 +26,7 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;

import javax.security.auth.callback.CallbackHandler;

Expand Down Expand Up @@ -107,7 +108,7 @@ class Factory {
* @return A model controller client
*/
public static ModelControllerClient create(final InetAddress address, final int port){
return new RemotingModelControllerClient(address.getHostName(), port, null);
return new RemotingModelControllerClient(address.getHostName(), port, null, null);
}

/**
Expand All @@ -119,7 +120,20 @@ public static ModelControllerClient create(final InetAddress address, final int
* @return A model controller client
*/
public static ModelControllerClient create(final InetAddress address, final int port, final CallbackHandler handler){
return new RemotingModelControllerClient(address.getHostName(), port, handler);
return new RemotingModelControllerClient(address.getHostName(), port, handler, null);
}

/**
* Create a client instance for a remote address and port.
*
* @param address the address of the remote host
* @param port the port
* @param handler CallbackHandler to obtain authentication information for the call.
* @param saslOptions Additional options to be passed to the SASL mechanism.
* @return A model controller client
*/
public static ModelControllerClient create(final InetAddress address, final int port, final CallbackHandler handler, final Map<String, String> saslOptions){
return new RemotingModelControllerClient(address.getHostName(), port, handler, saslOptions);
}

/**
Expand All @@ -131,7 +145,7 @@ public static ModelControllerClient create(final InetAddress address, final int
* @throws UnknownHostException if the host cannot be found
*/
public static ModelControllerClient create(final String hostName, final int port) throws UnknownHostException {
return new RemotingModelControllerClient(hostName, port, null);
return new RemotingModelControllerClient(hostName, port, null, null);
}

/**
Expand All @@ -144,7 +158,21 @@ public static ModelControllerClient create(final String hostName, final int port
* @throws UnknownHostException if the host cannot be found
*/
public static ModelControllerClient create(final String hostName, final int port, final CallbackHandler handler) throws UnknownHostException {
return new RemotingModelControllerClient(hostName, port, handler);
return new RemotingModelControllerClient(hostName, port, handler, null);
}

/**
* Create a client instance for a remote address and port and CallbackHandler.
*
* @param hostName the remote host
* @param port the port
* @param handler CallbackHandler to obtain authentication information for the call.
* @param saslOptions Additional options to be passed to the SASL mechanism.
* @return A model controller client
* @throws UnknownHostException if the host cannot be found
*/
public static ModelControllerClient create(final String hostName, final int port, final CallbackHandler handler, final Map<String, String> saslOptions) throws UnknownHostException {
return new RemotingModelControllerClient(hostName, port, handler, saslOptions);
}
}

Expand Down
Expand Up @@ -26,6 +26,7 @@

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;

import javax.security.auth.callback.CallbackHandler;

Expand All @@ -47,15 +48,17 @@ public class RemotingModelControllerClient extends AbstractModelControllerClient
private final String hostName;
private final int port;
private final CallbackHandler callbackHandler;
private final Map<String, String> saslOptions;
private volatile Endpoint endpoint;
private ManagementClientChannelStrategy strategy;
private boolean closed;


public RemotingModelControllerClient(String hostName, int port, final CallbackHandler callbackHandler) {
public RemotingModelControllerClient(String hostName, int port, final CallbackHandler callbackHandler, final Map<String, String> saslOptions) {
this.hostName = hostName;
this.port = port;
this.callbackHandler = callbackHandler;
this.saslOptions = saslOptions;
}

@Override
Expand All @@ -77,8 +80,9 @@ protected synchronized ManagementClientChannelStrategy getClientChannelStrategy(
}
if (strategy == null) {
endpoint = Remoting.createEndpoint("management-client", OptionMap.EMPTY);

endpoint.addConnectionProvider("remote", new RemoteConnectionProviderFactory(), OptionMap.create(Options.SSL_ENABLED, Boolean.FALSE));
strategy = ManagementClientChannelStrategy.create(hostName, port, endpoint, this, callbackHandler);
strategy = ManagementClientChannelStrategy.create(hostName, port, endpoint, this, callbackHandler, saslOptions);
}
return strategy;
}
Expand Down
Expand Up @@ -28,7 +28,10 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
Expand All @@ -49,6 +52,9 @@
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Property;
import org.xnio.Sequence;
import org.xnio.OptionMap.Builder;

/**
* This class is not thread safe and should only be used by one thread
Expand Down Expand Up @@ -96,15 +102,29 @@ public static <T extends ProtocolChannel> ProtocolChannelClient<T> create(final


public Connection connect(CallbackHandler handler) throws IOException {
return connect(handler, null);
}

public Connection connect(CallbackHandler handler, Map<String, String> saslOptions) throws IOException {
if (connection != null) {
throw MESSAGES.alreadyConnected();
}

// TODO - do we need better way to decide this?
OptionMap map = OptionMap.create(SASL_POLICY_NOANONYMOUS, Boolean.FALSE);
Builder builder = OptionMap.builder();
builder.set(SASL_POLICY_NOANONYMOUS, Boolean.FALSE);
if (saslOptions != null) {
List<Property> tempProperties = new ArrayList<Property>(saslOptions.size());
for (String currentKey : saslOptions.keySet()) {
tempProperties.add(Property.of(currentKey, saslOptions.get(currentKey)));
}

builder.set(Options.SASL_PROPERTIES, Sequence.of(tempProperties));
}

CallbackHandler actualHandler = handler != null ? handler : new AnonymousCallbackHandler();
WrapperCallbackHandler wrapperHandler = new WrapperCallbackHandler(actualHandler);
IoFuture<Connection> future = endpoint.connect(uri, map, wrapperHandler);
IoFuture<Connection> future = endpoint.connect(uri, builder.getMap(), wrapperHandler);
try {
this.connection = future.get();
} catch (CancellationException e) {
Expand Down
Expand Up @@ -29,6 +29,7 @@
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Security;
import java.util.Map;

import javax.security.auth.callback.CallbackHandler;

Expand All @@ -54,8 +55,9 @@ public static synchronized ManagementClientChannelStrategy create(final Manageme

public static ManagementClientChannelStrategy create(String hostName, int port, final Endpoint endpoint,
final ManagementOperationHandler handler,
final CallbackHandler cbHandler) throws URISyntaxException, IOException {
return new Establishing(hostName, port, endpoint, handler, cbHandler);
final CallbackHandler cbHandler,
final Map<String, String> saslOptions) throws URISyntaxException, IOException {
return new Establishing(hostName, port, endpoint, handler, cbHandler, saslOptions);
}

private static class Existing extends ManagementClientChannelStrategy {
Expand Down Expand Up @@ -85,14 +87,17 @@ private static class Establishing extends ManagementClientChannelStrategy {
private volatile ProtocolChannelClient<ManagementChannel> client;
private volatile ManagementChannel channel;
private final CallbackHandler callbackHandler;
private final Map<String,String> saslOptions;

public Establishing(final String hostName, final int port, final Endpoint endpoint,
final ManagementOperationHandler handler, final CallbackHandler callbackHandler) {
final ManagementOperationHandler handler, final CallbackHandler callbackHandler,
final Map<String, String> saslOptions) {
this.hostName = hostName;
this.port = port;
this.endpoint = endpoint;
this.handler = handler;
this.callbackHandler = callbackHandler;
this.saslOptions = saslOptions;
}

@Override
Expand Down Expand Up @@ -120,7 +125,7 @@ public Object run() {
boolean ok = false;
try {
try {
client.connect(callbackHandler);
client.connect(callbackHandler, saslOptions);
} catch (ConnectException e) {
throw e;
}
Expand Down

0 comments on commit 4a2d981

Please sign in to comment.