/*
 * Copyright
 *   2007 axyus - www.axyus.com
 *   2007 F.Bollaro - Franck.Bollaro@mail.ap-hm.fr
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.auth.ldapAphm;

import fr.gouv.finances.cp.xemelios.auth.AuthenticationCancelationException;
import fr.gouv.finances.cp.xemelios.common.Scramble;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Properties;

import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import fr.gouv.finances.cp.xemelios.auth.AbstractAuthenticationManager;
import fr.gouv.finances.cp.xemelios.auth.AuthChecker;
import fr.gouv.finances.cp.xemelios.auth.AuthProvider;
import fr.gouv.finances.cp.xemelios.auth.AuthenticationConfigurationException;
import fr.gouv.finances.cp.xemelios.auth.XemeliosUser;
import fr.gouv.finances.cp.xemelios.auth.ldapAphm.LdapProvider;
import fr.gouv.finances.cp.xemelios.data.DataAccessException;
import fr.gouv.finances.cp.xemelios.data.DataConfigurationException;
import fr.gouv.finances.cp.xemelios.data.DataLayerManager;
import fr.gouv.finances.cp.xemelios.data.impl.MySqlDataLayer;
import fr.gouv.finances.cp.xemelios.ui.MainWindow;

public class LdapManager extends AbstractAuthenticationManager implements AuthChecker {
    
    private String baseUrl=null;
    private String adminLogin=null;
    private String adminPassword=null;
    private String searchPath=null;
    private String searchField=null;
    
    private AuthProvider provider = null;
    
    
    public boolean authenticate(XemeliosUser user) throws DataAccessException, AuthenticationConfigurationException {
        
        LdapUser lu = null;
        int auth = 0;
        
        if(user instanceof LdapUser) lu = (LdapUser)user;
        else throw new AuthenticationConfigurationException("can only authenticate LdapUser");
        
        Connection con = null;
        MySqlDataLayer dataLayer = null;
        PreparedStatement ps = null;
        
        String login = user.getId().toUpperCase();
        
        // vérification du user en base !!
        try {
            if(! (DataLayerManager.getImplementation() instanceof MySqlDataLayer))
                throw new AuthenticationConfigurationException(getAuthenticationManagerName()+" can run only with mysql data layer");
            
            dataLayer = (MySqlDataLayer)DataLayerManager.getImplementation();
            con = dataLayer.getConnection();
            String sql = "SELECT ACCESS FROM APHM_USERS WHERE LOGIN='"+login.toLowerCase()+"'";
            ResultSet rs = con.createStatement().executeQuery(sql);
            if (rs.next()){
                auth = rs.getInt(1);
            } else return false;
        } catch (DataConfigurationException e1) {
            throw new AuthenticationConfigurationException(e1.getMessage());
        } catch (SQLException e1) {
            throw new AuthenticationConfigurationException(e1.getMessage());
        }
        
        String password = lu.getPassword();
        
        Hashtable env = new Hashtable();
        env.put(DirContext.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
        env.put(DirContext.PROVIDER_URL , baseUrl );
        env.put(DirContext.SECURITY_AUTHENTICATION,  "simple");
        env.put(DirContext.SECURITY_PRINCIPAL, adminLogin  );
        env.put(DirContext.SECURITY_CREDENTIALS, adminPassword);
        DirContext ctx = null;
        String userDN = null;
        
        try {
            //bind admin
            ctx = new InitialDirContext(env);
            //recherche login demandé dans l'arbre
            SearchControls ctls = new SearchControls();
            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            NamingEnumeration e= ctx.search(searchPath , "(" + searchField + "=" + login + ")", ctls);
            if (e.hasMore()) {
                SearchResult o =(SearchResult) e.next();
                userDN = "" + o.getAttributes().get("distinguishedName").getAll().next();
            }
        } catch (Exception e) {
            System.out.println("PROBLEME DE PARAMETRAGE LDAP !!!! "+e.getMessage());
        } finally {
            try {
                if (ctx != null)ctx.close();
            } catch (Exception e) {}
        }
        
        if(userDN == null) return false;    //user pas trouvé !!
        lu.setAuth(auth);
        //  bind user
        env = new Hashtable();
        env.put(DirContext.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory") ;
        env.put(DirContext.PROVIDER_URL, baseUrl);
        env.put(DirContext.SECURITY_AUTHENTICATION,"simple");
        env.put(DirContext.SECURITY_PRINCIPAL, userDN);
        env.put(DirContext.SECURITY_CREDENTIALS, password);
        try {
            ctx = new InitialDirContext(env);
            return true;
        } catch (AuthenticationException e) {
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (ctx != null) ctx.close();
            } catch (Exception e) {    }
        }
        
    }
    
    public LdapManager(MainWindow mw) throws AuthenticationConfigurationException, AuthenticationCancelationException {
        super();
        setMainWindow(mw);
        try {
            if(! (DataLayerManager.getImplementation() instanceof MySqlDataLayer))
                throw new AuthenticationConfigurationException(getAuthenticationManagerName()+" can run only with mysql data layer");
            Connection con = null;
            MySqlDataLayer dataLayer = null;
            PreparedStatement ps = null;
            try {
                dataLayer = (MySqlDataLayer)DataLayerManager.getImplementation();
                con = dataLayer.getConnection();
                String sql = "CREATE TABLE IF NOT EXISTS APHM_USERS (LOGIN VARCHAR(24) NOT NULL, ACCESS INT)";
                con.createStatement().executeUpdate(sql);
            } catch(SQLException sqlEx) {
                throw new AuthenticationConfigurationException(sqlEx);
            } finally {
                if(ps!=null) try { ps.close(); } catch(Throwable t) {}
                if(con!=null) dataLayer.releaseConnection(con);
            }
        } catch(DataConfigurationException dcEx) {
            // TODO
        }
    }
    
    public AuthChecker getChecker() {
        return this;
    }
    
    public AuthProvider getProvider() {
        if(provider==null) provider = new LdapProvider(getMainWindow());
        return provider;
    }
    
    public String getAuthenticationManagerName() {
        return "Authentification LDAP (AP-HM)";
    }
    
    @Override
    public void setProperties(Properties props) {
        super.setProperties(props);
        baseUrl = (String)getProperty("auth.ldap.baseUrl");
        adminLogin = (String)getProperty("auth.ldap.adminLogin");
        adminPassword = (String)getProperty("auth.ldap.adminPassword");
        searchPath = (String)getProperty("auth.ldap.searchPath");
        searchField = (String)getProperty("auth.ldap.searchField");
        if(adminPassword!=null) {
            // le mot de passe est crypté dans le fichier, il faut le décrypter
            adminPassword = Scramble.unScramblePassword(adminPassword);
        }
    }
    
    
    
}
