Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #8 from osbornjd/jay/MarkIIIKeyAuth
Browse files Browse the repository at this point in the history
Key Authorization
  • Loading branch information
osbornjd committed Nov 20, 2019
2 parents 354a0ec + d99d5e4 commit 2dc52d7
Show file tree
Hide file tree
Showing 18 changed files with 509 additions and 89 deletions.
@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2019- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Initial API and implementation and/or initial documentation -
* Jay Jay Billings, Joe Osborn
*******************************************************************************/
package org.eclipse.ice.commands;

/**
* This class is intended to offer the possibility to establish a connection via
* a basic authorization process, wherein one just obtains the username,
* hostname, and password from the user. It is intended to be a concrete
* implementation of ConnectionAuthorizationHandler in its most basic form. It
* is recommended to use alternative authorization handlers to establish a
* connection, as this is the only one where the password is stored in memory
* for some time period. See comments in
* {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#password} and
* {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#setPassword(char[])}
*
* @author Joe Osborn
*
*/
public class BasicConnectionAuthorizationHandler extends ConnectionAuthorizationHandler {

/**
* Default constructor
*/
public BasicConnectionAuthorizationHandler() {
}

/**
* See {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#getPassword()}
* and the above comments on use of this class
*/
@Override
protected char[] getPassword() {
return password;
}

/**
* No options required for console authorization
* See {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#setOption(String)}
*/
@Override
public void setOption(String option) {
return;
}



}
Expand Up @@ -63,6 +63,13 @@ public abstract class Command {
*/
protected int exitValue = -1;

/**
* Have a connection manager for commands that defaults to the static object
* from the factory method. Users can override this if they want to through a
* specific constructor which sets the manager.
*/
protected ConnectionManager manager = ConnectionManagerFactory.getConnectionManager();

/**
* Default constructor
*/
Expand Down
Expand Up @@ -34,7 +34,7 @@ public class Connection {
* An AtomicReference to the ConnectionConfiguration from which connection
* information can be gathered
*/
private AtomicReference<ConnectionConfiguration> configuration;
private AtomicReference<ConnectionConfiguration> configuration = new AtomicReference<ConnectionConfiguration>(null);;

/**
* The secure channel provided by com.jcraft.jsch
Expand Down Expand Up @@ -70,7 +70,6 @@ public class Connection {
* Default constructor
*/
public Connection() {
configuration = new AtomicReference<ConnectionConfiguration>(new ConnectionConfiguration());
}

/**
Expand All @@ -92,6 +91,9 @@ public ConnectionConfiguration getConfiguration() {
return configuration.get();
}

public void setConfiguration(ConnectionConfiguration configuration) {
this.configuration.set(configuration);
}
/**
* Set the JShell session {@link org.eclipse.ice.commands.Connection#jShell}
*
Expand Down
Expand Up @@ -42,6 +42,20 @@ public abstract class ConnectionAuthorizationHandler {
*/
String hostname = null;

/**
* A char array which can hold a password should the user desire. It is not
* recommended to use this since this stores the password in memory for some
* time. Rather, it is recommended to use either the
* {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#getPassword()}
* function, which retrieves the password from the user in some way, or use
* the private key functionality for establishing a connection as in
* {@link org.eclipse.ice.commands.ConnectionManagerTest#testOpenConnectionKeyPath}.
* Note that in all implementations of getPassword, the password is not actually
* stored in this char[] and is rather obtained where it is needed to establish
* a connection and then immediately destroyed.
*/
char[] password = null;

/**
* This function gets a password for the command authentication. The password is
* returned in a char array since Strings are immutable, so it is generally ill
Expand All @@ -51,6 +65,17 @@ public abstract class ConnectionAuthorizationHandler {
*/
protected abstract char[] getPassword();

/**
* This function is intended to be a "jack of all trades" function where an option
* can be passed that may be specific to a particular subclass. For example, for
* text file authorization, a path to the text file can be passed as specified
* by the subclass. The purpose of this function is for ease of setting things
* in the factory method.
*
* @param option
*/
public abstract void setOption(String option);

/**
* Getter for authorization hostname
* {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#hostname}
Expand Down Expand Up @@ -91,4 +116,23 @@ public void setUsername(String username) {
this.username = username;
}

/**
* Setter for {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#password}
* Please see the associated comment to this member variable before using this function.
* It is highly recommended that after a connection is established, this function is
* called again to remove the password information from memory. For example:
* ```java
* ConnectionAuthorizationHandler handler = new BasicConnectionAuthorizationHandler();
* handler.setPassword("password".toCharArray());
* // Now establish some connection with the ConnectionManager
* (insert code to establish connection)
* // Now erase the password from memory
* handler.setPassword("".toCharArray());
* ```
* @param password
*/
public void setPassword(char[] password) {
this.password = password;
}

}
Expand Up @@ -48,7 +48,12 @@ public ConnectionAuthorizationHandlerFactory() {
// A text file containing all credentials. Authorization will automatically grab
// credentials
handlerList.put("text", new TxtFileConnectionAuthorizationHandler());

// Use a path to a key pair that was already generated to establish the
// connection
handlerList.put("keypath", new KeyPathConnectionAuthorizationHandler());
// Use a basic authorization by just explicitly setting the username,
// hostname, and password
handlerList.put("basic", new BasicConnectionAuthorizationHandler());
}

/**
Expand All @@ -70,24 +75,19 @@ public ConnectionAuthorizationHandler getConnectionAuthorizationHandler(String t
* @param type - type of authorization handler desired
* @return - authorization handler
*/
public ConnectionAuthorizationHandler getConnectionAuthorizationHandler(String type, String path) {
public ConnectionAuthorizationHandler getConnectionAuthorizationHandler(String type, String option) {
ConnectionAuthorizationHandler auth = null;

// Iterate over the default authorization types
for (Map.Entry<String, ConnectionAuthorizationHandler> entry : handlerList.entrySet()) {
// Check if the type matches, and set the auth variable
if (type.equals(entry.getKey())) {
auth = entry.getValue();
// Set the option for the authorization, if necessary
auth.setOption(option);
}
}

// Have a separate set for a text file authorization since it needs to take the
// path as a constructor argument
// TODO - think about if there is a better way to do this - don't want to have
// to add an additional if statement for each non-default constructor
if (type.equals("text"))
auth = new TxtFileConnectionAuthorizationHandler(path);

if (auth == null)
logger.error("Unknown authorization type! Will return null.");

Expand Down
Expand Up @@ -23,8 +23,8 @@
import com.jcraft.jsch.JSchException;

/**
* This factory class manages remote connections, and as such interfaces with
* all classes that are associated with remote connections.
* This class manages remote connections, and as such interfaces with all
* classes that are associated with remote connections.
*
* @author Joe Osborn
*
Expand Down Expand Up @@ -73,7 +73,6 @@ public ConnectionManager() {
* @return Connection - returns connection if successful, null otherwise
*/
public Connection openConnection(ConnectionConfiguration config) throws JSchException {

// The new connection to be opened
Connection newConnection = new Connection(config);

Expand All @@ -88,11 +87,6 @@ public Connection openConnection(ConnectionConfiguration config) throws JSchExce
// Get the information necessary to open the connection
if (newConnection.getConfiguration() != null) {
ConnectionAuthorizationHandler auth = newConnection.getConfiguration().getAuthorization();

// Get the password first. If authorization is a text file, then
// username and hostname will be set. Otherwise user must set them
char[] pwd = auth.getPassword();

String username = auth.getUsername();
String hostname = auth.getHostname();

Expand All @@ -104,31 +98,25 @@ public Connection openConnection(ConnectionConfiguration config) throws JSchExce
throw new JSchException();
}

// Pass it to the session
newConnection.getSession().setPassword(String.valueOf(pwd));

// Erase contents of pwd and fill with null
Arrays.fill(pwd, Character.MIN_VALUE);
// Authorize the JSch session with a ConnectionAuthorizationHandler
authorizeSession(newConnection);

// JSch default requests ssh-rsa host checking, but some keys
// request ecdsa-sha2-nistp256. So loop through the available
// host keys that were grabbed from known_hosts and check what
// type the user-given hostname needs
HostKeyRepository hkr = jsch.getHostKeyRepository();

String type = null;
for (HostKey hk : hkr.getHostKey()) {
// If this hostkey contains the hostname that was supplied by
// the user
if (hk.getHost().contains(hostname)) {
String type = hk.getType();
type = hk.getType();
// Set the session configuration key type to that hosts type
newConnection.getSession().setConfig("server_host_key", type);
}
}

// Set the authentication requirements
newConnection.getSession().setConfig("PreferredAuthentications", "publickey,password");

// If the user wants to disable StrictHostKeyChecking, add it to the
// session configuration
if (!requireStrictHostKeyChecking)
Expand All @@ -138,9 +126,8 @@ public Connection openConnection(ConnectionConfiguration config) throws JSchExce
try {
newConnection.getSession().connect();
} catch (JSchException e) {
// Add something here that if this is a first time connect, and thus
// the fingerprint doesn't exist, to try to manually override?
logger.error("Couldn't connect to session with given username and/or password. Exiting.", e);
logger.error("Couldn't connect to session with given username and/or password/key. Exiting.", e);

throw new JSchException();
}

Expand All @@ -158,6 +145,42 @@ public Connection openConnection(ConnectionConfiguration config) throws JSchExce
return null;
}

/**
* This function deals with the new connection authentication. It takes a
* connection that is being opened, and decides what kind of authentication to
* provide JSch depending on whether or not a password exists in the
* ConnectionAuthorizationHandler.
*
* @param connection
* @throws JSchException
*/
private void authorizeSession(Connection connection) throws JSchException {
// Get the authorization information
ConnectionAuthorizationHandler auth = connection.getConfiguration().getAuthorization();

// If a password was set, then try to authenticate with the password
if (auth.getPassword() != null) {
logger.info("Trying to authenticate with a password");
// Get the password first. If authorization is a text file, then
// username and hostname will be set. Otherwise user must set them
char[] pwd = auth.getPassword();

// Pass it to the session
connection.getSession().setPassword(String.valueOf(pwd));

// Erase contents of pwd and fill with null
Arrays.fill(pwd, Character.MIN_VALUE);
// Set the authentication requirements
connection.getSession().setConfig("PreferredAuthentications", "publickey,password");
} else {
logger.info("Trying to authenticate with a key");
// Otherwise try a key authentication
String keyPath = ((KeyPathConnectionAuthorizationHandler) auth).getKeyPath();
connection.getJShellSession().addIdentity(keyPath);
}
return;
}

/**
* This function finds the particular connection requested by name in the list
* of all connections and returns it.
Expand Down Expand Up @@ -250,6 +273,17 @@ public void closeAllConnections() {

}

/**
* This function allows adding a user defined connection to the connection
* manager rather than opening a default connection through the function
* {@link ConnectionManager#openConnection(ConnectionConfiguration)}.
*
* @param connection
*/
public void addConnection(Connection connection) {
connectionList.put(connection.getConfiguration().getName(), connection);
}

/**
* This function lists all the connections (and their statuses, i.e. if open or
* not) to the logger, if so desired. Useful for checking the connections and
Expand Down
Expand Up @@ -24,7 +24,7 @@
*/
public class ConnectionManagerFactory {

static ConnectionManager manager = new ConnectionManager();
static private ConnectionManager manager = new ConnectionManager();

/**
* Default constructor
Expand Down
Expand Up @@ -66,4 +66,13 @@ protected char[] getPassword() {
return password.toCharArray();
}

/**
* No options required for console authorization
* See {@link org.eclipse.ice.commands.ConnectionAuthorizationHandler#setOption(String)}
*/
@Override
public void setOption(String option) {
return;
}

}
Expand Up @@ -64,6 +64,16 @@ public abstract class FileHandler implements IFileHandler {
*/
protected HandleType HANDLE_TYPE = null;


/**
* Have a connection manager for commands that defaults to the static object from
* the factory method. Users can override this if they want to through a specific
* constructor which sets the manager.
*/
protected ConnectionManager manager = ConnectionManagerFactory.getConnectionManager();



/**
* Default constructor
*/
Expand Down

0 comments on commit 2dc52d7

Please sign in to comment.