/**
* @author Terry Fleury (tfleury@ncsa.uiuc.edu)
* @version 1.0 2006-09-06
*
* This class provides several utility methods to assist with "Single
* Sign-On" authentication of users utilizing a MyProxy server. It also has
* a convenience method to create a session password upon successful
* authentication of a user. Most of these methods are simply wrappers for
* MyProxy methods.
*
* To compile, you will need the following jars in your CLASSPATH:
* cog-jglobus.jar - From GT4 Java WS Core Binary installer:
* http://www.globus.org/toolkit/downloads
* commons-lang.jar - From Apache Jakarta Commons Lang project:
* http://jakarta.apache.org/commons/lang
* log4j.jar - Available lots of places, but you can use the one from
* the GT4 Java WS Core Binary package
*
* To run, you will need the following ADDITIONAL jars in your CLASSPATH:
* cryptix32.jar - From GT4 Java WS Core Binary
* cryptix-asn1.jar - From GT4 Java WS Core Binary
* puretls.jar - From GT4 Java WS Core Binary
*/
package edu.uiuc.ncsa.myproxy;
// In standard Java
import org.ietf.jgss.GSSCredential;
import java.rmi.dgc.VMID;
// In cog-jglobus.jar
import org.globus.myproxy.MyProxy;
import org.globus.myproxy.DestroyParams;
import org.globus.myproxy.GetParams;
import org.globus.myproxy.InitParams;
import org.globus.myproxy.InfoParams;
import org.globus.myproxy.CredentialInfo;
// In commons-lang.jar
import org.apache.commons.lang.RandomStringUtils;
// In log4j.jar
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
/**
* The SSOUtils (Single Sign-On Utilities) class consists of several static
* methods to assist with the creation and authentication of session
* passwords using a MyProxy server. To use these methods, you will need to
* have a MyProxy server set up with MyProxyCA to create user credentials
* on-the-fly, and also configured with "check_multiple_credentials" so as
* to be able to store multiple (randomly named) credentials for a given
* username. The two main methods you will use are
* createSessionPassword
and authenticateUser
.
* The first method takes in a username/password and returns a new random
* session password for that username. It also has the "side effect" of
* authenticating the username (if the returned session password is not
* empty). The second method takes in a username/password and returns
* true/false for if that username authenticates with the MyProxy server.
* Note that in each method the "password" can be either the username's long
* lived password OR a previously created session password.
*
* Example usage:
*
*
* import edu.uiuc.ncsa.myproxy.SSOUtils; * * String username; * String password; * String sessionpassword; * * sessionpassword = SSOUtils.createSessionPassword( * "myproxy-test.ncsa.uiuc.edu",7512,username,password); * * if (sessionpassword.length() > 0) { * // We successfully authenticated the given username/password * // AND created a new random session password for that username. * // We can use the new session password in subsequent authentication * // attempts of the username. * } * * if (SSOUtils.authenticateUser("myproxy-test.ncsa.uiuc.edu",7512, * username,sessionpassword)) { * // We successfuly authenticated the given username/session password. * // Note that we could also authenticate the original password * // with this method. * } ** * Another useful utility method is
enableSSL
which allows the
* use of "https" in your Java URLs.
*/
public class SSOUtils {
/**
* Default lifetime of a session password, in seconds (= 4 hours).
*/
public static final int SSOLIFETIME = 14400; // 4 hours
static Logger logger = Logger.getLogger(SSOUtils.class);
/**
* A short "main" test application to demonstrate how to use the utility
* methods of the class. This main method can be used to test out the
* functionality of the utility methods of the SSOUtils class. It takes
* in a username, password, and optional MyProxy host. It creates a
* session password based on that username/password (using some other
* authentication mechanism) at the MyProxy host. It then tests
* authentication with username/session password. The MyProxy host is
* hard-coded but can be overridden by providing an alternate MyProxy
* host as the third command line argument.
* @param args The command line arguments. Must include USERNAME as the
* first argument and PASSWORD as the second argument. May
* also include a MYPROXYHOST (FQDN) as the third argument,
* otherwise a default MyProxy server will be used.
*/
public static void main(String[] args) {
// Set up the logger for outputting 'debug' messages
logger.setLevel((Level)Level.DEBUG);
// Check number of command line arguments
if ((args.length < 2) || (args.length > 3)) {
System.out.println("Usage: java edu.uiuc.ncsa.myproxy.SSOUtils " +
"java.net.URL
instances, then you need to call this
* method first. This is simply a convenience method.
* @return The reference position in which the SSL provider was added
* (>= 0), or -1 if the SSL provider was not added because it
* was already installed.
*/
public static int enableSSL() {
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");
return java.security.Security.addProvider(
new com.sun.net.ssl.internal.ssl.Provider());
}
/**
* Create a session password by storing a randomly named credential with
* a random password on a MyProxy server. This method hides the gory
* details of creating a session password for a particular
* username
/password
combination. First, it
* attempts to get a credential from the specified MyProxy server using
* the specified username
and password
. Note
* that this password can be the username's long lived password or a
* previously created session password. Then, it delegates the
* fetched credential back to the same MyProxy server using a random
* credential name and random password. If the credential is
* successfully put to the MyProxy server, the random (session) password
* is returned. Otherwise, the empty string ("") is returned. You
* should use this method if you only want a session password and have
* no need of the user credential associated with that session password.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param username The username for storing the credential.
* @param password The password corresponding to the username. This
* can be either the user's long lived password or a
* previously created session password associated with
* that username.
* @return The random session password which was used to successfully
* put a credential for the username to the MyProxy server, or
* the empty string ("") if there was some sort of error.
*/
public static String createSessionPassword(String host, int port,
String username, String password) {
String randpass = "";
String randname = "";
GSSCredential credential = null;
credential = getCredFromMyProxy(host,port,username,password);
if (credential != null) {
randpass = RandomStringUtils.randomAlphanumeric(16);
randname = putRandomNamedCredToMyProxy(host,port,credential,
username,randpass,SSOLIFETIME);
if (randname.length() == 0) {
randpass = "";
}
}
return randpass;
}
/**
* Authenticate a username/password with a MyProxy server. This is a
* convenience function which returns a boolean success/fail for a
* username
/password
combination rather than
* the credential for that username
. Note that this method
* can take either the user's long lived password or a session password.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param username The username for storing the credential.
* @param password The password corresponding to the username. This
* can be either the user's long lived password or a
* previously created session password associated with
* that username.
* @return True if the username/password successfully authenticates with
* the MyProxy server, false otherwise.
*/
public static boolean authenticateUser(String host, int port,
String username, String password) {
boolean retval;
retval = (getCredFromMyProxy(host,port,username,password) != null);
return retval;
}
/**
* Destroy a previously created session password. This method is a bit
* of a hack. It enables you to delete a credential from a MyProxy
* server by providing only the username and session password.
* Typically, the destroy() method requires the name of the credential
* to be deleted. But session passwords are created with a random
* credential name that is not typically available to the developer. So
* this method takes the long way. It first gets all stored credentials
* for a username. We then have a list of credential names. It then
* tries to get() each named credential utilizing the passed-in username
* and (session) password. If the get() is successful, then we found
* the credential associated with the session password, so we can do a
* standard MyProxy destroy() call.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param username The username for destroying the credential.
* @param password The password corresponding to the username. This
* is a previously created session password which was
* stored with a random credential name (which you don't
* know).
* @return True if the credential associated with the passed-in session
* password is successfully deleted from the MyProxy server,
* thus rendering the session password useless. False if there
* was any sort of error.
*/
public static boolean destroySessionPassword(String host, int port,
String username, String password) {
boolean retval = false; /* Assume failure */
if (port == 0) {
port = MyProxy.DEFAULT_PORT;
}
logger.debug("destroySessionPassword() called, connecting to '" +
host + ":" + port + " with username = '"+
username + "' and session password = '"+
password + "' ...");
/* Make sure there is a credential associated with the password */
GSSCredential credential =
getCredFromMyProxy(host,port,username,password);
if (credential != null) {
CredentialInfo[] info = null;
MyProxy myProxyServer = new MyProxy(host,port);
/* Get a list of all stored credential for the username */
InfoParams infoParams = new InfoParams();
infoParams.setLifetime(0);
infoParams.setUserName(username);
infoParams.setPassphrase(password);
try {
logger.debug("In destroySessionPassword(), trying to get info "+
"from " + host + ":" + port + " for username = '"+
username + "' using session password = '" +
password + "' ...");
info = myProxyServer.info(credential,infoParams);
logger.debug("'INFO' Succeeded!");
} catch (Exception e) {
info = null;
logger.error("When trying to destroy credential on '" +
host + ":" + port + "', info() failed because "+
e.getMessage());
}
/* Scan through the list of stored creds for ones with a *
* credname (i.e. NOT the default unnamed cred). Try to do *
* a get() with the username, password, and credname. If *
* successful, then destroy that credential */
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getName().length() > 0) {
/* Make sure that there is a non-empty credname */
GSSCredential tempcred;
GetParams getParams = new GetParams(username,password);
getParams.setCredentialName(info[i].getName());
try {
tempcred = myProxyServer.get(credential,getParams);
} catch (Exception e) {
tempcred = null;
}
if (tempcred != null) {
/* Found it! Destroy it using the credname. */
DestroyParams destroyParams =
new DestroyParams(username,password);
destroyParams.setCredentialName(info[i].getName());
try {
logger.debug("Trying to destroy cred named '" +
info[i].getName() + "' from " +
host + ":" + port + " for username = '"+
username + "' and session password = '"+
password + "' ...");
myProxyServer.destroy(credential,destroyParams);
logger.debug("'DESTROY' Succeeded!");
retval = true;
} catch (Exception e) {
logger.error("Failed to destroy session "+
"password '" + password + "' because "+
e.getMessage());
}
}
}
}
}
}
return retval;
}
/**
* Fetch a credential from a MyProxy server. This is really just
* a wrapper for MyProxy's old static get
method which is
* now deprecated. If any of host
, username
,
* or password
is not specified (i.e. null or empty
* string), then an error is logged and the return credential is null.
* If the port
is not specified (i.e. 0), then the default
* MyProxy port (7512) will be used. If the lifetime
is
* not specified (i.e. 0), then a default lifetime
* (SSOLIFETIME
) will be used.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param username The username for the credential.
* @param password The password corresponding to the username.
* @param lifetime The requested lifetime of the fetched credential, in
* seconds. If set to 0, then a default value will be
* used.
* @return A GSS credential for the proxy credential from the MyProxy
* server, or "null" if there was an error.
* @see #getCredFromMyProxy(String,int,String,String)
*/
public static GSSCredential getCredFromMyProxy(String host, int port,
String username, String password, int lifetime) {
GSSCredential retcred = null;
if (port == 0) {
port = MyProxy.DEFAULT_PORT;
}
if (lifetime == 0) {
lifetime = SSOLIFETIME;
}
if ((host.length() == 0) || (username.length() == 0) ||
(password.length() == 0)) {
logger.error("In getCredFromMyProxy, host, username, and/or " +
"password was not specified." );
} else {
try {
MyProxy myProxyServer = new MyProxy(host,port);
logger.debug("Trying to get credential from "
+ host + ":" + port + " for username = '"+
username + "' using password = '" +
password + "' and lifetime = '" +
lifetime + "' ...");
retcred = myProxyServer.get(username,password,lifetime);
logger.debug("'GET' Succeeded!");
} catch (Exception e) {
logger.error("Could not get proxy from '" +
host + ":" + port + "' because "+
e.getMessage());
}
}
return retcred;
}
/**
* Fetch a credential from a MyProxy server. This is a convenience
* method which calls the above getCredFromMyProxy with a default
* lifetime (SSOLIFETIME
).
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param username The username for the credential.
* @param password The password corresponding to the username.
* @return A GSS credential for the proxy credential from the MyProxy
* server, or "null" if there was an error.
* @see #getCredFromMyProxy(String,int,String,String,int)
*/
public static GSSCredential getCredFromMyProxy(String host, int port,
String username, String password) {
return getCredFromMyProxy(host,port,username,password,SSOLIFETIME);
}
/**
* Delegate a credential to a MyProxy server under a given credential
* name. This method puts a GSSCredential to a specified MyProxy
* server:port
under the given username
with a
* specified password
, lifetime
(in seconds),
* and credential name
. If any of host
,
* username
, or password
is not specified
* (i.e. null or empty string), then an error is logged and failure is
* retured. If the port
is not specified (i.e. 0), then
* the default MyProxy port (7512) will be used. If the
* lifetime
is not specified (i.e. 0), then a default
* lifetime (SSOLIFETIME
) will be used. If the credential
* name
is empty (""), then the credential is stored as the
* user's "default" credential.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param credential A GSSCredential to be delegated to a MyProxy server.
* @param username The username for storing the credential.
* @param password The password corresponding to the username.
* @param lifetime The requested lifetime of the stored credential, in
* seconds. If set to 0, then a default value will be
* used.
* @param credname The name under which to store the credential. If the
* empty string (""), then the credential stored is the
* "default" credential for the username. Note: Use only
* alphanumeric characters for this field.
* @return True if successfully put credential to MyProxy server, false
* if there was some sort of error.
*/
public static boolean putNamedCredToMyProxy(
String host, int port, GSSCredential credential,
String username, String password, int lifetime,
String credname) {
boolean retbool = false; // Assume failure
if (port == 0) {
port = MyProxy.DEFAULT_PORT;
}
if (lifetime == 0) {
lifetime = SSOLIFETIME;
}
if ((host.length() == 0) || (username.length() == 0) ||
(password.length() == 0)) {
logger.error("In getCredFromMyProxy, host, username, or " +
"password was not given." );
} else {
try {
MyProxy myProxyServer = new MyProxy(host,port);
InitParams params = new InitParams();
params.setUserName(username);
params.setPassphrase(password);
params.setLifetime(lifetime);
if (credname.length() > 0) {
params.setCredentialName(credname);
}
logger.debug("Trying to put credential '" + credential +
"' to " + host + ":" + port + " with username = '"+
username + "', credname = '" +
credname + "', and lifetime = '" +
lifetime + "' ...");
myProxyServer.put(credential,params);
logger.debug("'PUT' Succeeded!");
retbool = true;
} catch (Exception e) {
logger.error("Failed! Could not put credential to '" +
host + ":" + port + "' because "+
e.getMessage());
}
}
return retbool;
}
/**
* Delegate a credential to a MyProxy server. This is a convenience
* method which calls putNamedCredToMyProxy
with a blank
* credential name. This stores the credential under the "default"
* credential for the given username
.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param credential A GSSCredential to be delegated to a MyProxy server.
* @param username The username for storing the credential.
* @param password The password corresponding to the username.
* @param lifetime The requested lifetime of the stored credential, in
* seconds. If set to 0, then a default value will be
* used.
* @return True if successfully put credential to MyProxy server, false
* if there was some sort of error.
* @see #putNamedCredToMyProxy(String,int,GSSCredential,String,String,int,String)
*/
public static boolean putCredToMyProxy(
String host, int port, GSSCredential credential,
String username, String password, int lifetime) {
return putNamedCredToMyProxy(host,port,credential,
username,password,lifetime,"");
}
/**
* Delegate a credential to a MyProxy server under a random
* credential name. This is a convenience method which calls
* putNamedCredToMyProxy
with a random credential name. If
* the credential is successfully put to the MyProxy server, then the
* random credential name utilized is returned. Otherwise, the empty
* string ("") is returned.
* @param host The FQDN of the MyProxy host.
* @param port The MyProxy port. If 0, then the default MyProxy port
* (7512) will be used.
* @param credential A GSSCredential to be delegated to a MyProxy server.
* @param username The username for storing the credential.
* @param password The password corresponding to the username.
* @param lifetime The requested lifetime of the stored credential, in
* seconds. If set to 0, then a default value will be
* used.
* @return The random credential name utilized when successfully putting
* the credential to the MyProxy server, or the empty string
* ("") if failed to put the credential.
* @see #putNamedCredToMyProxy(String,int,GSSCredential,String,String,int,String)
*/
public static String putRandomNamedCredToMyProxy(
String host, int port, GSSCredential credential,
String username, String password, int lifetime) {
String randname = new java.rmi.dgc.VMID().toString() +
RandomStringUtils.randomAlphanumeric(4);
if (!putNamedCredToMyProxy(host,port,credential,username,
password,lifetime,randname)) {
randname = "";
}
return randname;
}
}