package com.evasion.sam.jaas;
import com.evasion.sam.PasswordEncoder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
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.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.sql.DataSource;
/**
* Module d'authentification JAAS.
* @author sebastien
*/
public class EvasionLoginModule implements LoginModule {
/**
* LOGGER.
*/
private static final Logger LOGGER = Logger.getLogger(EvasionLoginModule.class.getName());
private static final String PARAM_DATASOURCE_JNDI = "datasource-jndi";
private static final String PARAM_DIGEST_ALGORITHM = "digest-algorithm";
private static final String DEFAULT_DIGEST_ALGORITHM = "sha+salt";
private static final String PARAM_USER_TABLE = "user-table";
private static final String PARAM_USER_NAME_COLUMN = "user-name-column";
private static final String PARAM_PASSWORD_COLUMN = "password-column";
private static final String PARAM_ACTIVE_COLUMN = "active-column";
private static final String PARAM_BLOCK_COLUMN = "block-column";
private static final String PARAM_LAST_LOGIN_COLUMN = "last-login-column";
private static final String PARAM_ROLE_REQUEST = "role-request";
private static final String BLANK_VALUE_SQL = "\"\"";
private static final String COMA ="\"";
private StringBuilder userQuery = new StringBuilder();
private StringBuilder roleQuery = new StringBuilder();
private Subject subject;
private String username = null;
private String password = null;
private Date lastLogin = null;
private CallbackHandler callbackHandler;
private boolean success = true;
private DataSource ds = null;
private Connection con = null;
private PreparedStatement pr = null;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
LOGGER.info("Init Evasion Login Module");
String dsJndi = (String) options.get(PARAM_DATASOURCE_JNDI);
String userTable = (String) options.get(PARAM_USER_TABLE);
String userNameColumn = (String) options.get(PARAM_USER_NAME_COLUMN);
String passwordColumn = (String) options.get(PARAM_PASSWORD_COLUMN);
String activeColumn = (String) options.get(PARAM_ACTIVE_COLUMN);
String blockColumn = (String) options.get(PARAM_BLOCK_COLUMN);
String lastLoginColumn = (String) options.get(PARAM_LAST_LOGIN_COLUMN);
String roleRequest = (String) options.get(PARAM_ROLE_REQUEST);
String digestAlgorithm = (String) options.get(PARAM_DIGEST_ALGORITHM);
LOGGER.info("------- Properties ------------------");
completePropertiesAndLog(PARAM_DATASOURCE_JNDI, dsJndi);
completePropertiesAndLog(PARAM_USER_TABLE, userTable);
completePropertiesAndLog(PARAM_USER_NAME_COLUMN, userNameColumn);
completePropertiesAndLog(PARAM_PASSWORD_COLUMN, passwordColumn);
completePropertiesAndLog(PARAM_ROLE_REQUEST, roleRequest);
completePropertiesAndLogWhtihDefaultValue(PARAM_DIGEST_ALGORITHM, digestAlgorithm, DEFAULT_DIGEST_ALGORITHM);
completePropertiesAndLogWhtihDefaultValue(PARAM_ACTIVE_COLUMN, activeColumn, BLANK_VALUE_SQL);
completePropertiesAndLogWhtihDefaultValue(PARAM_BLOCK_COLUMN, blockColumn, BLANK_VALUE_SQL);
completePropertiesAndLogWhtihDefaultValue(PARAM_LAST_LOGIN_COLUMN, lastLoginColumn, BLANK_VALUE_SQL);
roleQuery.append(roleRequest);
userQuery.append("SELECT ").append(passwordColumn).append(", ").append(activeColumn).append(", ").append(blockColumn).append(", ").append(lastLoginColumn).append(" FROM ").append(userTable).append(" WHERE ").append(userNameColumn).append("=");
LOGGER.info("Login Module query : " + userQuery);
JmsClient client;
try {
client = new JmsClient();
ds = (DataSource) client.lookup(dsJndi);
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "erreur JNDI lookup ", ex);
}
}
private void completePropertiesAndLog(String propertyName, String propertyValue) {
LOGGER.info(propertyName + " : " + propertyValue);
if (propertyValue == null || propertyValue.equals("")) {
LOGGER.severe(propertyName + " can not be null");
}
}
private void completePropertiesAndLogWhtihDefaultValue(String propertyName, String propertyValue, String defaultValue) {
if (propertyValue == null || ("").equals(propertyValue)) {
propertyValue = defaultValue;
}
completePropertiesAndLog(propertyName, propertyValue);
}
@Override
public boolean login() throws LoginException {
LOGGER.info("Start Login");
traitementPWD();
// Hardcoded user verification. Only users user1 and super with password=password are allowed.
ResultSet rs = null;
try {
con = ds.getConnection();
pr = con.prepareStatement(userQuery + COMA + username + COMA);
rs = pr.executeQuery();
rs.first();
String dbPassword = rs.getString(1);
lastLogin = rs.getDate(4);
if (!dbPassword.equals(password)) {
LOGGER.log(Level.SEVERE, "Login Failed for user " + username + "!!!");
throw new LoginException("Login Failed for user " + username + "!!!");
}
LOGGER.fine("Login succes");
return true;
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQL exception on user database.", ex);
throw new LoginException("SQL exception on user database.");
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "ResultSet Exception ", ex);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQL Exception ", ex);
}
}
}
}
private boolean traitementPWD() throws LoginException {
try {
NameCallback nc = new NameCallback("UsrName");
PasswordCallback pc = new PasswordCallback("Passwd", false);
callbackHandler.handle(new Callback[]{nc, pc});
username = nc.getName();
char[] tmp = pc.getPassword();
if (tmp != null) {
password = new String(tmp);
}
if (password == null || password.isEmpty() || username == null || username.isEmpty()) {
LOGGER.severe("User or password are null");
throw new LoginException("Login Failed for user " + username + "!!!");
}
password = PasswordEncoder.encodePassword(username, password);
pc.clearPassword();
} catch (Exception ex) {
success = false;
LoginException le = new LoginException("Login Failed!!!");
LOGGER.severe("Login Failed with username: " + username + " and password: xxxxxx");
le.initCause(ex);
throw le;
}
LOGGER.fine("Login with username: " + username + " and password: xxxxxxxxx");
return true;
}
@Override
public boolean commit() throws LoginException {
LOGGER.fine("Commit");
if (username != null && success) {
EvasionPrincipal user = new EvasionPrincipal(username);
user.setLastLogin(lastLogin);
subject.getPrincipals().add(user);
if (roleQuery.length() > 0) {
ResultSet rs=null;
try {
int index = roleQuery.indexOf("?");
roleQuery.deleteCharAt(index);
roleQuery.insert(index, COMA + username + COMA);
LOGGER.fine("Role QUery: " + roleQuery.toString());
con = ds.getConnection();
pr = con.prepareStatement(roleQuery.toString());
rs = pr.executeQuery();
final List<String> rolesResult = new ArrayList<String>();
while (rs.next()) {
rolesResult.add(rs.getString(1));
}
EvasionGroup roles = new EvasionGroup("Roles");
for (String role : rolesResult) {
roles.addMember(new EvasionPrincipal(role));
}
subject.getPrincipals().add(roles);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQL exception on group request. ", ex);
throw new LoginException("SQL exception on user database. ");
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "ResultSet Exception ", ex);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQL Exception ", ex);
}
}
}
}
return true;
}
return true;
}
@Override
public boolean abort() throws LoginException {
try {
username = null;
password = null;
return true;
} finally {
if (con != null) {
try {
con.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQL Exception ", ex);
}
}
}
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(new EvasionPrincipal(username));
username = null;
password = null;
return true;
}
public class JmsClient {
private Context ctx;
public JmsClient() {
Map properties = new HashMap(2);
properties.put(Context.PROVIDER_URL, "iiop://127.0.0.1:3700");
try {
ctx = new InitialContext(new Hashtable(properties));
} catch (NamingException ex) {
LOGGER.log(Level.SEVERE, "JNDI Exception ", ex);
}
}
public Object lookup(String name) {
try {
return ctx.lookup(name);
} catch (NamingException ex) {
LOGGER.log(Level.SEVERE, "JNDI lookup Exception ", ex);
}
return null;
}
}
}