<?php

require_once __DIR__.'/authenticator.abstract.class.php';

/**
 * Authentification des utilisateurs via un annuaire LDAP.
 */
class authenticator_ldap extends authenticator {

    /**
     * Authentifie l'utilisateur via un annuaire LDAP.
     *
     * @param  array  $data  Données concernant l'utilisateur à authentifier.
     *
     * @return array   format: (bool $authenticated, string $message, array $data)
     */
    public function authenticate(array $data) {
        list($login, $password) = $this->get_login_and_password($data);
        if (is_null($login) || empty($login)) {
            return array(false, __("Identifiant vide"), array());
        }

        // si la configuration de l'annaire LDAP est incorrecte
        if ($this->framework->isDirectoryAvailable() != true) {
            return array(false,
                __("Erreur de configuration. Contactez votre administrateur."),
                array());
        }
        $this->framework->log(__METHOD__, "configuration LDAP OK", 'DEBUG', 'ldap');

        // détermine le filtre LDAP utilisateur a fournir pour l'authentification LDAP
        $user_filter = null;

        // si une connexion LDAP via un utilisateur dédié est requise
        if (isset($this->framework->directory_config['ldap_login_dn'])) {

            // Authentification de l'administrateur du LDAP
            $auth = $this->framework->connectDirectory(
                $this->framework->directory_config['ldap_admin_login'],
                $this->framework->directory_config['ldap_admin_passwd']
            );
            if ($auth === false) {
                $class = 'error';
                $message = sprintf(
                    "Échec de la connexion à l'annuaire LDAP (login: %s)",
                    var_export($this->framework->directory_config['ldap_admin_login'], true));
                $this->framework->log(__METHOD__, $message, 'WARNING', 'ldap');
                return array(false,
                    __("Erreur de configuration. Contactez votre administrateur."),
                    array());
            }
            $this->framework->log(
                __METHOD__, "connexion et authentification à l'annuaire LDAP: OK", 'DEBUG', 'ldap');

            // recherche de l'utilisateur dans l'annuaire
            $this->framework->log(
                __METHOD__, "recherche de l'utilisateur '$login' dans l'annuaire LDAP ...", 'DEBUG', 'ldap');
            $this->framework->log(__METHOD__, "recherche LDAP ...", 'DEBUG', 'ldap');
            $this->framework->log(__METHOD__, "    base DN: ".
                var_export($this->framework->directory_config['ldap_base_users'], true), 'DEBUG', 'ldap');
            $this->framework->log(__METHOD__, "     filtre: ".
                var_export($this->framework->directory_config['ldap_login_attrib'], true)."=$login", 'DEBUG', 'ldap');
            $this->framework->log(__METHOD__, '  attributs: array("*")', 'DEBUG', 'ldap');

            $lres = false;
            $lres = ldap_search(
                $this->framework->dt,
                $this->framework->directory_config['ldap_base_users'],
                '('.$this->framework->directory_config["ldap_login_attrib"].'='.$login.')',
                array('*')
            );
            $this->framework->log(__METHOD__, "résultats: ".
                (gettype($lres) == 'resource' ? "$lres"
                : var_export($lres, true)), 'DEBUG', 'ldap');

            // en cas d'erreur
            list($err, $err_msg) = $this->framework->ldap_get_last_error($this->framework->dt);
            if ($err || $lres === false) {
                $message = __("Impossible de poursuivre la recherche des utilisateurs.").' '.
                           __("Une erreur est survenue lors de l'interrogation du serveur LDAP.");
                $message .= ! is_null($err_msg) ? $err_msg : '';
                $this->framework->log(__METHOD__, $message, 'ERROR', 'ldap');
                return array(false,
                    __("Erreur de configuration. Contactez votre administrateur."),
                    array());
            }

            // récupération des infos utilisateurs
            $this->framework->log(__METHOD__, "récupération des entrées de la requête LDAP ...", 'DEBUG', 'ldap');
            $entries = ldap_get_entries($this->framework->dt, $lres);

            // log et vérification de la cohérences du nombre d'éléments retournés
            $entries_count = null;
            if (is_iterable($entries)) {
                $entries_count = count($entries);
                if (isset($entries['count'])) {
                    $this->framework->log(__METHOD__, "résultats ('count' key): ".$entries['count'], 'DEBUG', 'ldap');
                    if (intval($entries['count']) != ($entries_count - 1)) {
                        $this->framework->log(__METHOD__,
                            "items counting differs (".intval($entries['count']).
                            " != ".($entries_count - 1).")", 'WARNING', 'ldap');
                    }
                    $entries_count = intval($entries['count']);
                } else {
                    $this->framework->log(__METHOD__, "résultats ('count' toplevel): $entries_count", 'DEBUG', 'ldap');
                }
            } else {
                $this->framework->log(__METHOD__, "résultats: ".var_export($entries, true), 'DEBUG', 'ldap');
                return array(false,
                    __("Erreur d'interrogation de l'annuaire LDAP. Contactez votre administrateur."),
                    array());
            }
            if ($entries_count === 0) {
                return array(false,
                    sprintf(__("Aucun utilisateur '%s' trouvé dans l'annuaire LDAP."),
                        $login),
                    array());
            }
            if ($entries_count > 1) {
                $this->framework->log(
                    __METHOD__, "plusieurs utilisateurs LDAP trouvés correspondants à '$login'",
                    'WARNING', 'ldap');
            }

            // détermine le filtre utilisateur
            $user_filter = $entries[0][$this->framework->directory_config['ldap_login_dn']] ?? null;
            if (empty($user_filter)) {
                $message = __("Impossible de poursuivre la recherche des utilisateurs.").' '.
                           __("Une erreur est survenue lors de l'interrogation du serveur LDAP.");
                $message .= ! is_null($err_msg) ? $err_msg : '';
                $this->framework->log(__METHOD__, $message, 'ERROR');
                return array(false,
                    __("Erreur de configuration. Contactez votre administrateur."),
                    array());
            }
            if (! empty($user_filter) && is_array($user_filter) && isset($user_filter[0])) {
                    $user_filter = $user_filter[0];
            }

            $this->framework->log(
                __METHOD__, "filtre LDAP pour '$login': ".var_export($user_filter, true), 'DEBUG', 'ldap');

            // déconnexion de l'annuaire LDAP
            $this->framework->disconnectDirectory();

        // pas besoin de se connecter au préalable via un utilisateur dédié
        } else {
            $user_filter = $this->framework->directory_config["ldap_login_attrib"].'='.$login.','.
                           $this->framework->directory_config["ldap_base_users"];
        }

        // Connexion à l'annuaire LDAP
        $authenticated = $this->framework->connectDirectory($user_filter, $password);

        // Déconnexion de l'annuaire
        $this->framework->disconnectDirectory();

        return array($authenticated, '', array());
    }
}
