package org.jboss.security.auth.spi;
import java.security.Principal;
import java.util.Map;
import java.util.HashMap;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import org.jboss.security.Util;
import org.jboss.crypto.digest.DigestCallback;
public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule
{
private Principal identity;
private char[] credential;
private String hashAlgorithm = null;
private String hashCharset = null;
private String hashEncoding = null;
private boolean ignorePasswordCase;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options)
{
super.initialize(subject, callbackHandler, sharedState, options);
hashAlgorithm = (String) options.get("hashAlgorithm");
if( hashAlgorithm != null )
{
hashEncoding = (String) options.get("hashEncoding");
if( hashEncoding == null )
hashEncoding = Util.BASE64_ENCODING;
hashCharset = (String) options.get("hashCharset");
if( log.isTraceEnabled() )
{
log.trace("Password hashing activated: algorithm = " + hashAlgorithm
+ ", encoding = " + hashEncoding
+ ", charset = " + (hashCharset == null ? "{default}" : hashCharset)
+ ", callback = " + options.get("digestCallback")
);
}
}
String flag = (String) options.get("ignorePasswordCase");
ignorePasswordCase = Boolean.valueOf(flag).booleanValue();
}
public boolean login() throws LoginException
{
if( super.login() == true )
{
Object username = sharedState.get("javax.security.auth.login.name");
if( username instanceof Principal )
identity = (Principal) username;
else
{
String name = username.toString();
try
{
identity = createIdentity(name);
}
catch(Exception e)
{
log.debug("Failed to create principal", e);
throw new LoginException("Failed to create principal: "+ e.getMessage());
}
}
Object password = sharedState.get("javax.security.auth.login.password");
if( password instanceof char[] )
credential = (char[]) password;
else if( password != null )
{
String tmp = password.toString();
credential = tmp.toCharArray();
}
return true;
}
super.loginOk = false;
String[] info = getUsernameAndPassword();
String username = info[0];
String password = info[1];
if( username == null && password == null )
{
identity = unauthenticatedIdentity;
super.log.trace("Authenticating as unauthenticatedIdentity="+identity);
}
if( identity == null )
{
try
{
identity = createIdentity(username);
}
catch(Exception e)
{
log.debug("Failed to create principal", e);
throw new LoginException("Failed to create principal: "+ e.getMessage());
}
if( hashAlgorithm != null )
password = createPasswordHash(username, password);
String expectedPassword = getUsersPassword();
if( validatePassword(password, expectedPassword) == false )
{
super.log.debug("Bad password for username="+username);
throw new FailedLoginException("Password Incorrect/Password Required");
}
}
if( getUseFirstPass() == true )
{ sharedState.put("javax.security.auth.login.name", username);
sharedState.put("javax.security.auth.login.password", credential);
}
super.loginOk = true;
super.log.trace("User '" + identity + "' authenticated, loginOk="+loginOk);
return true;
}
protected Principal getIdentity()
{
return identity;
}
protected Principal getUnauthenticatedIdentity()
{
return unauthenticatedIdentity;
}
protected Object getCredentials()
{
return credential;
}
protected String getUsername()
{
String username = null;
if( getIdentity() != null )
username = getIdentity().getName();
return username;
}
protected String[] getUsernameAndPassword() throws LoginException
{
String[] info = {null, null};
if( callbackHandler == null )
{
throw new LoginException("Error: no CallbackHandler available " +
"to collect authentication information");
}
NameCallback nc = new NameCallback("User name: ", "guest");
PasswordCallback pc = new PasswordCallback("Password: ", false);
Callback[] callbacks = {nc, pc};
String username = null;
String password = null;
try
{
callbackHandler.handle(callbacks);
username = nc.getName();
char[] tmpPassword = pc.getPassword();
if( tmpPassword != null )
{
credential = new char[tmpPassword.length];
System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length);
pc.clearPassword();
password = new String(credential);
}
}
catch(java.io.IOException ioe)
{
throw new LoginException(ioe.toString());
}
catch(UnsupportedCallbackException uce)
{
throw new LoginException("CallbackHandler does not support: " + uce.getCallback());
}
info[0] = username;
info[1] = password;
return info;
}
protected String createPasswordHash(String username, String password)
{
DigestCallback callback = null;
String callbackClassName = (String) options.get("digestCallback");
if( callbackClassName != null )
{
try
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class callbackClass = loader.loadClass(callbackClassName);
callback = (DigestCallback) callbackClass.newInstance();
if( log.isTraceEnabled() )
log.trace("Created DigestCallback: "+callback);
}
catch (Exception e)
{
if( log.isTraceEnabled() )
log.trace("Failed to load DigestCallback", e);
SecurityException ex = new SecurityException("Failed to load DigestCallback");
ex.initCause(e);
throw ex;
}
HashMap tmp = new HashMap(options);
tmp.put("javax.security.auth.login.name", username);
tmp.put("javax.security.auth.login.password", password);
callback.init(tmp);
}
String passwordHash = Util.createPasswordHash(hashAlgorithm, hashEncoding,
hashCharset, username, password, callback);
return passwordHash;
}
protected boolean validatePassword(String inputPassword, String expectedPassword)
{
if( inputPassword == null || expectedPassword == null )
return false;
boolean valid = false;
if( ignorePasswordCase == true )
valid = inputPassword.equalsIgnoreCase(expectedPassword);
else
valid = inputPassword.equals(expectedPassword);
return valid;
}
abstract protected String getUsersPassword() throws LoginException;
}