package org.openslx.imagemaster.db;
import java.io.File;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import javax.net.ssl.TrustManagerFactory;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapConnectionConfig;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.log4j.Logger;
import org.apache.mina.filter.ssl.KeyStoreFactory;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.session.User;
import org.openslx.imagemaster.thrift.iface.AuthenticationError;
import org.openslx.imagemaster.thrift.iface.AuthenticationException;
import org.openslx.imagemaster.util.Sha512Crypt;
/**
* Represents a user instance that was queries (primarily) from LDAP.
* Additional information that is not provided by the LDAP server might
* be fetched from other sources, like the local database (DbUser.java).
*/
public class LdapUser extends User
{
private static final Logger log = Logger.getLogger( LdapUser.class );
protected LdapUser(int userId, String username, String password, String organization, String firstName, String lastName, String eMail, String satelliteAddress)
{
super( userId, username, password, organization, firstName, lastName, eMail,
satelliteAddress );
}
/**
* Query LDAP for user with given login
*
* @param login Login of user in the form "prefix_username"
* @return instance of LDAPUser for matching entry from LDAP or null if sth went wrong
*/
public static LdapUser forLogin( final String login, final String password ) throws AuthenticationException
{
String username, organization, firstName, lastName, eMail, satelliteAddress = "";
final String[] split = login.split( "_" );
if (split.length != 2)
throw new AuthenticationException(AuthenticationError.GENERIC_ERROR, "Login must be in form: prefix_username");
LdapConnection connection = null;
try {
LdapConnectionConfig config = new LdapConnectionConfig();
String ldapHost = Globals.getLdapHost();
log.debug( "Setting host... " + ldapHost );
config.setLdapHost( ldapHost );
boolean useSsl = Globals.getLdapSsl();
log.debug( "Setting use ssl... " + useSsl);
config.setUseSsl( useSsl );
int ldapPort = Globals.getLdapPort();
log.debug( "Setting port... " + ldapPort );
config.setLdapPort( ldapPort );
// load keystore ...
KeyStoreFactory ksf = new KeyStoreFactory();
ksf.setDataFile( new File(Globals.getLdapKeystorePath()) );
ksf.setPassword( Globals.getLdapKeystorePassword());
ksf.setType( "jks" );
// ... and set TrustManager
TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
tmf.init( ksf.newInstance() );
config.setTrustManagers( tmf.getTrustManagers() );
connection = new LdapNetworkConnection( config );
log.debug( "Trying to bind..." );
String bind = Globals.getLdapBindQuery().replace( "%", login );
connection.bind( bind, password );
//connection.bind();
log.debug( "Bind successful" );
// make search query
EntryCursor cursor = connection.search( Globals.getLdapSearchBaseDn(),
Globals.getLdapSearchFilter().replace( "%", login ), SearchScope.SUBTREE );
// only use the first result
cursor.next();
Entry entry = cursor.get();
username = entry.get( "cn" ).toString().split( " " )[1].split( "@" )[0];
organization = entry.get( "cn" ).toString().split( "@" )[1];
firstName = entry.get( "givenName" ).getString();
lastName = entry.get( "sn" ).getString();
eMail = entry.get( "mail" ).getString();
// get the satellite address from db
DbSatellite dbSatellite = DbSatellite.fromSuffix( organization );
if ( dbSatellite != null ) {
satelliteAddress = dbSatellite.getAddress();
} else {
// Organization is not known. This should not happen because the login would have failed then.
throw new AuthenticationException( AuthenticationError.GENERIC_ERROR, "Your Organization is not known by the server. Please contact your administrator." );
}
// everything went fine
return new LdapUser( 0, username, Sha512Crypt.Sha512_crypt( password, null, 0 ), organization, firstName, lastName, eMail, satelliteAddress );
} catch ( LdapException e) {
if ( e.getMessage().contains( "Cannot connect on the server" ) ) {
DbSatellite dbSatellite = DbSatellite.fromPrefix(split[0]);
if (dbSatellite == null) throw new AuthenticationException(AuthenticationError.INVALID_CREDENTIALS, "Credentials invalid.");
String lo = split[1] + "@" + dbSatellite.getOrganizationId();
log.info( "LDAP server could not be reached. Trying to connect locally with: " + lo );
return LdapUser.localLogin(lo, password);
}
e.printStackTrace();
throw new AuthenticationException( AuthenticationError.GENERIC_ERROR, "Something went wrong." );
} catch ( CursorException e ) {
e.printStackTrace();
throw new AuthenticationException( AuthenticationError.INVALID_CREDENTIALS, "Could not find user entry." );
} catch ( IOException e ) {
// could not load keyfile
e.printStackTrace();
} catch ( NoSuchAlgorithmException e ) {
// could not load algorithm
e.printStackTrace();
} catch ( KeyStoreException | NoSuchProviderException | CertificateException e ) {
// some problem with the key
e.printStackTrace();
} finally {
// close connection
try {
connection.unBind();
connection.close();
} catch ( IOException | LdapException e ) {
// was not connected so don't do anything...
}
}
return null;
}
/**
* Login user locally if external Ldap server is not available
* @param login Must be in form "username@organization"
* @param password The user's password
*/
private static LdapUser localLogin( String login, String password )
{
DbUser user = DbUser.forLogin( login );
if (user == null) return null; // no user found
// check users password
if (!Sha512Crypt.verifyPassword( password, user.password )) return null;
// return ldapuser if valid
return new LdapUser( user.userId, user.login, Sha512Crypt.Sha512_crypt( password, null, 0 ), user.organizationId, user.firstName,
user.lastName, user.eMail, user.satelliteAddress );
}
}