/*
 * $Id: LdapUserHelper.java,v 1.12 2007/07/20 14:18:07 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * 
 * $Log: LdapUserHelper.java,v $
 * Revision 1.12  2007/07/20 14:18:07  marbobo
 * + Max. Anzahl von Benutzern die im System gleichzeitig angemeldet sein dürfen ist jetzt konfiguriebar
 *
 * Revision 1.11  2004/12/09 15:22:58  jonyp
 * change from VKCLanguage.getDefault to VKCLangaugeSerializer.getDefaultLanguage
 *
 * Revision 1.10  2004/12/09 14:09:58  jonyp
 * renamed getLanguageByMappingValue to guessLanguageByMappingValue
 *
 * Revision 1.9  2004/12/07 20:29:29  jonyp
 * deleted unneed import
 *
 * Revision 1.8  2004/12/06 16:40:48  jonyp
 * - fixed problem with npe on saveUser, because setClearTextPassword was used instead of setPassword
 * - randomly genered password for ldap user now: username + textual representation of current timestamp
 *
 * Revision 1.7  2004/12/01 16:47:19  jonyp
 * setGroups with empty HashSet instead of null
 *
 * Revision 1.6  2004/11/24 17:38:53  jonyp
 * added "convertToLdapUser" // preliminary !
 *
 * Revision 1.5  2004/11/09 16:08:02  marbobo
 * - Unnötige Anweisungen entfernt
 *
 * Revision 1.4  2004/11/03 17:33:35  jonyp
 * some corrections concering language support
 *
 * Revision 1.3  2004/10/26 15:04:29  jonyp
 * added support of mapping_language ==> take given language if found
 *
 * Revision 1.2  2004/10/22 15:26:18  jonyp
 * *** empty log message ***
 *
 * Revision 1.1  2004/10/15 15:15:02  jonyp
 * preliminary: do not use
 *
 * 
 */

package vkc.ldap.db.helper;

import java.sql.Timestamp;
import java.util.HashSet;

import java.security.MessageDigest;

import javax.naming.ldap.LdapContext;
import javax.naming.directory.Attributes;
import javax.naming.NamingException;

import vkc.db.entities.VKCLanguage;
import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.db.serializer.VKCUserSerializer;
import vkc.db.serializer.VKCLanguageSerializer;
import vkc.exception.DBException;
import vkc.exception.MaxAllowedUsersExceededException;
import vkc.exception.UnknownUserException;
import vkc.util.VKCCoder;
import vkc.view.WebShare;

import vkc.ldap.VKCLdapUser;
import vkc.ldap.db.entities.VKCLdapServer;
import vkc.ldap.db.serializer.VKCLdapServerSerializer;
import vkc.ldap.LdapProxy;

public class LdapUserHelper {
	/**
	 * Authentifizierung
	 * 
	 * @param username
	 * @param password
	 * @param ldapServerId
	 * @return VKCLdapUser
	 * @throws UnknownUserException
	 * @throws NamingException
	 * @throws DBException
	 * @throws MaxAllowedUsersExceededException 
	 */
	public static VKCLdapUser authenticate( String username, String password, Integer ldapServerId )
	throws UnknownUserException, 
		   NamingException,
		   DBException, 
           MaxAllowedUsersExceededException {
        int maxAllowedUsers = VKCSysParameterSerializer.retrieveIntValue("LOGIN", "MaxAllowedUsers", 0);
        int activeUsersCount = WebShare.getActiveUsers().length;
        
        if (maxAllowedUsers > 0 && activeUsersCount >= maxAllowedUsers) {
            //Maximale Anzahl an erlaubten Benutzer wurde überschritten
            throw new MaxAllowedUsersExceededException("The maximum number of allowed users in VKC exceeded");
        }

	    VKCLdapUser ldapUser = new VKCLdapUser(); 
	    
	    // get infos of the ldap server
	    VKCLdapServer ldapServer = VKCLdapServerSerializer.retrieveLdapServer( ldapServerId );    	

	    // build userDN
	    String userDN = ldapServer.getMapping_loginattr() + "=" + username + "," + ldapServer.getUsersBaseRDN() + "," + ldapServer.getBaseDN();
	    
	    // external user URL
	    String userURL = ldapServer.getLdapServerURL() + "/" + userDN;
	    
	    // get ldapContext 
	    LdapContext ldapCtx = LdapProxy.getDirContext( ldapServer.getLdapServerURL(), userDN, password, ldapServer.getAuthMethod() );
	    
	    if ( ldapCtx == null ) {
	        // no user known
	        throw new UnknownUserException( "External user " + userURL + " could not be resolved!" );
	    }

	    // get the basic attributes, based on schema "person, organizationalPerson, inetOrgPerson"
	    // minimal we need: fullname (e.g. "cn"), unique user id (e.g. "uid"), mail adress (e.g. "mail"), language (e.g. 
	    String[] attrIDs = { ldapServer.getMapping_fullname(), ldapServer.getMapping_userid(), ldapServer.getMapping_email(), ldapServer.getMapping_language() };
	    Attributes attrs = ldapCtx.getAttributes( userDN, attrIDs );
       
	    if ( attrs == null ) {
	        // no attributes found
	        throw new UnknownUserException( "External user " + userURL + " could not be resolved!" );
	    }    
	    
	    if ( attrs.get( ldapServer.getMapping_userid() ) == null || attrs.get( ldapServer.getMapping_fullname() ) == null ) {
	        throw new UnknownUserException( "External user " + userURL + " could not be resolved!" );
	    }    	    
	    
	    // mandatory mappings
	    String ldap_commonName = (String) ( attrs.get( ldapServer.getMapping_fullname() ) ).get();
	    String ldap_userId =(String) ( attrs.get( ldapServer.getMapping_userid() ) ).get();
	    
	    // voluntary fields
	    String ldap_email = null;
	    
	    if ( attrs.get( ldapServer.getMapping_email() ) != null ) { 
	        ldap_email =(String) ( attrs.get( ldapServer.getMapping_email() ) ).get();
	    }
	    	    
	    String ldap_language = null;
	    
	    if ( attrs.get( ldapServer.getMapping_language() ) != null ) {
	        ldap_language = (String) ( attrs.get( ldapServer.getMapping_language() ) ).get();
	    }
	    
	    try {
            // check if this user has already an entry in table "users"
	        Integer userId = VKCUserSerializer.retrieveUserIdFromExternalUserId( ldapServer.getLdapServerId(), (String) ( attrs.get( "uid" ) ).get() ); 

			// set password to username + textual representation of current timestamp
			byte[] md5Bytes = ( username + (new Timestamp(System.currentTimeMillis())).toString() ).getBytes( "ISO-8859-1" );
			MessageDigest digest = MessageDigest.getInstance( "MD5" );
			digest.reset();
			digest.update( md5Bytes );
			byte[] fingerPrint = digest.digest();
			fingerPrint = VKCCoder.base64Encode( fingerPrint );
			String md5Password = new String( fingerPrint, "ISO-8859-1" );

	        if ( userId == null ) {
	            // does not exist, insert a new one...
	            ldapUser.setUserId( null );            
	            ldapUser.setUser( ldap_userId );
	            ldapUser.setFirstname( null );
	            ldapUser.setLastname( ldap_commonName );
	            ldapUser.setPassword( md5Password );
	            ldapUser.setAccountValid( true );
                
	            if ( ldap_email != null ) {
    	            ldapUser.setEmail( ldap_email );
                    ldapUser.setMessagingEnabled( true );
                    ldapUser.setShowEmail( true );
                } else {
    	            ldapUser.setEmail( null );                    
                    ldapUser.setMessagingEnabled( false );
                    ldapUser.setShowEmail( false );
                }

	            ldapUser.setCreatorId( VKCUser.SYSTEM_USER_ID );
                ldapUser.setCreated( new Timestamp( System.currentTimeMillis() ) );	            
	            
                if ( ldap_language != null ) {
                    // returns a supported language
                    VKCLanguage supportedLanguage = VKCLanguageSerializer.guessLanguageByMappingValue( ldap_language ); 
                    
                    if ( supportedLanguage == null ) {
                        // take default language
                        ldapUser.setLanguage( VKCLanguageSerializer.getDefaultLanguage() );
                    } else {
                        ldapUser.setLanguage( supportedLanguage );
                    }
                } else {
                    // no supported language found, take default one... 
                    ldapUser.setLanguage( VKCLanguageSerializer.getDefaultLanguage() );
                }
                
                // initally give user an empty hashSet of groups
                // set parameter to null causes lots of NPE
                // because mainly all function refering to getGroups()
                // do not check for null. instead require an empty hashset of groups
	            ldapUser.setGroups( new HashSet() );
	            ldapUser.setExternalServerId( ldapServer.getLdapServerId() );
                ldapUser.setExternalServerType( "ldap" );
                ldapUser.setExternalUserId( ldap_userId );
	        } else {
	            // does exist, update
	            VKCUser user = VKCUserSerializer.retrieveUser( userId );
	            
	            // copy necessary attributes from VKCUser to VKCLdapUser
	            ldapUser.setUserId( user.getUserId() );
	            ldapUser.setUser( user.getUser() );
	            ldapUser.setFirstname( null );
	            ldapUser.setLastname( ldap_commonName );
	            ldapUser.setPassword( md5Password );
	            ldapUser.setAccountValid( true );

	            if ( ldap_email != null ) {
    	            ldapUser.setEmail( ldap_email );                    
                    ldapUser.setMessagingEnabled( true );
                    ldapUser.setShowEmail( true );
                } else {
    	            ldapUser.setEmail( null );                    
                    ldapUser.setMessagingEnabled( false );
                    ldapUser.setShowEmail( false );
                }

	            ldapUser.setCreatorId( user.getCreatorId() );
                ldapUser.setCreated( user.getCreated() );	            
	            ldapUser.setLanguage( user.getLanguage() );
	            ldapUser.setGroups( user.getGroups() );
	            ldapUser.setExternalServerId( user.getExternalServerId() );
                ldapUser.setExternalServerType( user.getExternalServerType() );
                ldapUser.setExternalUserId( user.getExternalUserId() );
	            
	            ldapUser.setLastChangerId( VKCUser.SYSTEM_USER_ID );
                ldapUser.setLastChanged( new Timestamp( System.currentTimeMillis() ) );	            
            }
            
            VKCUserSerializer.saveUser( ldapUser );
        } catch ( Exception ex ) {
            throw new DBException( ex ); 
        }
	    
	    ldapUser.setLdapContext( ldapCtx );	    
	    
	    return ldapUser;
	}
	
	public VKCLdapUser convertToLdapUser(VKCUser user ) {
	    
	    // preliminary
	    return null;
	    
	}
		
	
}