Difference between revisions of "Authentification"

From Dolibarr ERP CRM Wiki
Jump to navigation Jump to search
Tag: 2017 source edit
 
(33 intermediate revisions by 5 users not shown)
Line 1: Line 1:
== Introduction ==
+
<!-- BEGIN interlang links -->
 +
<!-- Do NOT edit this section
 +
    Links below are automatically managed by PolyglotBot
 +
    You can edit links on the English source page : Authentication -->
 +
[[en:Authentication]]
 +
[[es:Autentificación]]
 +
[[zh:认证]]
 +
<!-- END interlang links -->
  
Le système d'authentification de Dolibarr devient relativement complexe, et un bug peut être particulièrement difficile à trouver si l'on ne connaît par le processus d'authentification.
+
[[Category:Noyau]]
Cette page présente une découpe du processus, qui permet de suivre la procédure et d'intervenir là où il le faut.
+
{{TemplateDocDev}}
  
== Processus ==
 
  
Le processus démarre par les inclusions de htdocs/index.php, qui est la première page que l'on charge pour s'authentifier dans Dolibarr. Pourtant, ce n'est pas index.php qui commence réellement le boulot, mais bien main.inc.php, inclut par pre.inc.php, lui même inclut par index.php. Nous avons donc:
 
  
<index.php >
 
  < pre.inc.php>
 
    <main.inc.php>
 
      <master.inc.php>
 
        #1#
 
      </master.inc.php>
 
      #2#
 
    </main.inc.php>
 
  < /pre.inc.php>
 
</index.php >
 
  
#1# représente le chargement de tout un tas de librairie que nous utiliserons par la suite
 
#2# représente l'exécution du login
 
  
L'exécution du login, elle, se présente comme suit:
 
  
$authmode=array('http','dolibarr');
 
if (isset($dolibarr_auto_user)) $authmode=array('auto');
 
// Si la demande du login a déjà eu lieu, on le récupère depuis la session
 
// sinon appel du module qui réalise sa demande.
 
// A l'issu de cette phase, la variable $login sera définie.
 
$login='';
 
if (! session_id() || ! isset($_SESSION["dol_user"]) || ! isset($_SESSION["dol_token"]))
 
{
 
  # Procédure de login #
 
}
 
else
 
{
 
    // On est déjà en session
 
    $login=$_SESSION["dol_user"];
 
}
 
// Charge l'objet user depuis son login
 
$result=$user->fetch($login);
 
if ($result <= 0)
 
{
 
    dolibarr_print_error($db,$langs->trans("ErrorCantLoadUserFromDolibarrDatabase"));
 
    exit;
 
}
 
// Est-ce une nouvelle session
 
if (! isset($_SESSION["dol_user"]))
 
{
 
    // Nouvelle session pour ce login
 
    dolibarr_syslog("New session in DOLSESSID_".$dolibarr_main_db_name.": ".session_id());
 
    $user->update_last_login_date();
 
    $_SESSION["dol_user"]=$user;
 
}
 
  
Encore une fois, le code de plus grande complexité a été extrait pour l'analyser plus en détail.
 
  
Toutefois, en passant au travers du code ci-dessus de façon rapide, quelque chose pourrait vous avoir sauté aux yeux. C'est l'objet '''$user'''.
 
  
Même si j'ai supprimé une grande partie du code du login ici, cet objet n'est pas déclaré dans ce script.  
+
= Introduction =
En fait, il fait l'objet d'une instanciation au sein d'une méthode sur l'objet DOLIAuth, que nous verrons ci-dessous, et dont la classe est (re)définie dans htdocs/includes/pear/Auth/Auth.php.
+
Le système d'authentification de Dolibarr devient relativement complexe, et un bug peut être particulièrement difficile à trouver si l'on ne connaît pas le processus d'authentification.
C'est lorsque l'on appelle la méthode start() sur cet objet que l'objet $user est instancié.
+
Cette page présente une découpe du processus, qui permet de suivre la procédure et d'intervenir là où il le faut. Une connaissance de la notion de session PHP est requise.
  
Mais analysons plus en détail le code d'appel de la méthode d'authentification (il y a plusieurs méthodes, donc plusieurs appels possibles et qui devraient être mutuellement exclusifs).
+
= Processus =
 +
Le processus démarre par l'appel de la page que l'on souhaite voir. Par exemple, la page d'accueil htdocs/index.php. Mais ce n'est pas ce fichier qui assure la demande d'authentification. En fait toute page de Dolibarr inclut un fichier main.inc.php qui lui même inclut le fichier master.inc.php.
 +
Nous avons donc:
  
    session_name("DOLSESSID_".$dolibarr_main_db_name);
+
{{Template:CodeSampleForLoginProcess}}
    session_start();
 
    // On est pas déjà authentifié, on demande le login/mot de passe
 
    // A l'issu de cette demande, le login et un jeton doivent avoir été placé
 
    // en session dans dol_user et dol_token et la page rappelée.
 
    // MODE AUTO
 
    if (in_array('auto',$authmode) && ! $login)
 
    {
 
        $login=$dolibarr_auto_user;
 
        dolibarr_syslog ("Authentification ok (en mode force)");
 
    }
 
    // MODE HTTP (Basic)
 
    if (in_array('http',$authmode) && ! $login)
 
    {
 
        $login=$_SERVER["REMOTE_USER"];
 
    }
 
    // MODE DOLIBARR
 
    if (in_array('dolibarr',$authmode) && ! $login)
 
    {
 
        require_once(DOL_DOCUMENT_ROOT."/includes/pear/Auth/Auth.php");
 
        $pear = $dolibarr_main_db_type.'://'.$dolibarr_main_db_user.':'.$dolibarr_main_db_pass.'@'.$dolibarr_main_db_host.'/'.$dolibarr_main_db_name;
 
        if ($conf->global->DATABASE_PWD_ENCRYPTED)
 
        {
 
            $cryptType = "md5";
 
        }
 
        else
 
        {
 
            $cryptType = "none";
 
        }
 
        $params = array(
 
            "dsn" => $pear,
 
            "table" => MAIN_DB_PREFIX."user",
 
            "usernamecol" => "login",
 
            "passwordcol" => "pass",
 
            "cryptType" => $cryptType,
 
        );
 
        $aDol = new DOLIAuth("DB", $params, "dol_loginfunction");
 
        $aDol->setSessionName("DOLSESSID_".$dolibarr_main_db_name);
 
        $aDol->start();        $result = $aDol->getAuth(); // Si deja logue avec succes, renvoie vrai, sinon effectue un redirect sur page loginfunction et renvoie false
 
        if ($result)
 
        {
 
            // Authentification Auth OK, on va chercher le login
 
            $login=$aDol->getUsername();
 
            dolibarr_syslog ("Authentification ok (en mode Pear Base Dolibarr)");
 
        }
 
        else
 
        {
 
            if (isset($_POST["loginfunction"]))
 
            {
 
                // Echec authentification
 
                dolibarr_syslog("Authentification ko (en mode Pear Base Dolibarr) pour '".$_POST["username"]."'");
 
            }
 
            else
 
            {
 
                // Non authentifie
 
                //dolibarr_syslog("Authentification non realise");
 
            }
 
            exit;
 
        }
 
    }
 
    // MODE LDAP
 
    if ($conf->ldap->enabled && in_array('ldap',$authmode) && ! $login)
 
    {
 
        // Authentification Apache KO ou non active, pas de mode force on demande le login
 
        require_once(DOL_DOCUMENT_ROOT."/includes/pear/Auth/Auth.php");
 
        $params = array(
 
            'dsn' => $ldap,
 
            'host' => $conf->global->LDAP_SERVER_HOST,
 
            'port' => $conf->global->LDAP_SERVER_PORT,            'version' => $conf->global->LDAP_SERVER_PORT,
 
            'basedn' => $conf->global->LDAP_SERVER_DN,
 
            'binddn' => $conf->global->LDAP_ADMIN_DN,
 
            'bindpw' => $conf->global->LDAP_ADMIN_PASS,
 
            //'userattr' => $conf->global->LDAP_FIELD_LOGIN_SAMBA,
 
            'userattr' => 'samAccountName',            'userfilter' => '(objectClass=user)',
 
        );
 
        $aDol = new DOLIAuth("LDAP", $params, "dol_loginfunction");
 
        $aDol->start();
 
        $result = $aDol->getAuth(); // Si deja logue avec succes, renvoie vrai, sinon effectue un redirect sur page loginfunction et renvoie false
 
        if ($result)
 
        {
 
            // Authentification Auth OK, on va chercher le login
 
            $login=$aDol->getUsername();
 
            dolibarr_syslog ("Authentification ok (en mode Pear Base LDAP)");
 
        }
 
        else
 
        {
 
            if (isset($_POST["loginfunction"]))
 
            {
 
                // Echec authentification
 
                dolibarr_syslog("Authentification ko (en mode Pear Base LDAP) pour '".$_POST["username"]."'");
 
            }
 
            else
 
            {
 
                // Non authentifie
 
                //dolibarr_syslog("Authentification non realise");
 
            }
 
            exit;
 
        }
 
    }
 
  
Pour cette analyse, nous ignorerons le mode '''http''' et le mode '''LDAP''' pour nous concentrer sur le mode '''dolibarr'''.
+
Le #1# représente le chargement de tout un tas de librairie que nous utiliserons par la suite, ainsi que l'initialisation du contexte d'exécution du code PHP (langue, configuration, utilisateur vierge).
Il y a deux choses qui nous intéressent en particulier ici.
 
- l'instanciation de l'objet DOLIAuth (avec en paramètres les informations nécessaires à la connexion à la DB, et une string 'dol_loginfunction' que l'on devine être le nom de la fonction de login)
 
- l'appel à la méthode '''start()''' sur cet objet DOLIAuth
 
  
L'instanciation est simple à analyser. En allant faire un tour dans htdocs/includes/pear/Auth/Auth.php, on a vite fait de comprendre qu'on assigne ''dol_loginfunction'' à un attribut de l'objet DOLIAuth. Sans plus.
+
Le #2# représente le code d'authentification: Le programme vérifie si on est dans une session loguée (cela signifie que $_SESSION["dol_login"] existe). Si non, on vérifie si on a reçu des données issu du formulaire de login/mote passe. Au premier appel, ce n'est pas le cas puisque nous n'avons pas encore eu le formulaire à l'écran. Aussi on rentre dans le if, et $login étant faux, on affiche alors le formulaire HTML de login et le script se termine.
  
Pour la méthode ''start()'', par contre, nous irons un petit peu plus loin dans l'analyse. Toujours dans htdocs/includes/pear/Auth/Auth.php, on retrouve la définition de la méthode en question:
+
Après la soumission du login, la même page (donc index.php) est appelée, nous allons toujours dans le #1#, puis #2# et cette fois $_POST["username"] est défini. Aussi nous vérifions si le user et mot de passe sont ok (la vérifivation est faite dans la base, en LDAP ou autre, cela dépend de la variable $dolibarr_main_authentication du fichier de configuration). Si le couple login/pass est ok, la variable $login est définie, aussi nous n'affichons pas à nouveau le formulaire mais nous continuons et positionnons $_SESSION["dol_login"], ainsi au prochain appel d'une page, nous ne rentrons plus dans le "if (! isset($_SESSION["dol_login"]))".
  
    /**
+
Le #3# correspond à la vérification des permissions métiers et à l'affichage de la page si c'est ok. Voir la page  See [[Permissions]] pour plus d'informations.
    * Start new auth session
 
    *
 
    * @access public
 
    * @return void
 
    */
 
    function start()
 
    {
 
        $this->assignData();
 
  
        session_start();
+
= Les mode d'authentification et modules de login =
  
        if (!$this->checkAuth()) {
+
L'appel de checkLoginPassEntity pour valider le couple utilisateur/mot de passe (ou uniquement l'utilisateur dans certains cas) appellera la fonction '''check_user_password_xxx''' d'un module de connexion. Le module de connexion appelé dépend du mode d'authentification défini dans votre fichier '''conf/conf.php'''.
            $this->login();
 
        }
 
    }
 
  
Et cela nous mène donc vers la méthode ''assignData()'' (on notera au passage que l'on appelle aussi session_start() et $this->login() un peu plus tard).
+
Le fichier utilisé est nommé '''htdocs/core/login/functions_xxx.php''' avec la valeur '''xxx''' qui correspond à la valeur définie dans '''dolibarr_main_authentication''' dans le fichier de configuration '''conf/ conf.php'''.
  
    /**
+
Voir [[Authentication,_SSO_and_SSL]] pour une liste des modes d'authentification (valeurs possibles pour 'xxx' et leur spécificité)
    * Assign data from login form to internal values
 
    *
 
    * This function takes the values for username and password
 
    * from $HTTP_POST_VARS and assigns them to internal variables.
 
    * If you wish to use another source apart from $HTTP_POST_VARS,
 
    * you have to derive this function.
 
    *
 
    * @access private
 
    * @global $HTTP_POST_VARS
 
    * @see    Auth
 
    * @return void
 
    */
 
    function assignData()
 
    {
 
        $post = &$this->_importGlobalVariable("post");
 
 
 
        if (isset($post['username']) && $post['username'] != "") {
 
            $this->username = (get_magic_quotes_gpc() == 1 ? stripslashes($post['username']) : $post['username']);
 
        }
 
        if (isset($post['password']) && $post['password'] != "") {
 
            $this->password = (get_magic_quotes_gpc() == 1 ? stripslashes($post['password']) : $post['password'] );
 
        }
 
    }
 
 
 
Fonction toute simple donc, mais qui, par l'intermédiaire de ''_importGlobalVariable("post")'', va récupérer username et password comme ils ont été entrés dans le formulaire d'authentification.
 
 
 
Toutefois, toujours pas de vérification de ces données jusqu'ici. C'est le bon moment pour analyser la méthode '''login()'''.
 
 
 
    /**
 
    * Login function
 
    *
 
    * @access private
 
    * @return void
 
    */
 
    function login()
 
    {
 
        $login_ok = false;
 
        /**
 
        * When the user has already entered a username,
 
        * we have to validate it.
 
        */
 
        if (!empty($this->username)) {
 
            if (true === $this->storage->fetchData($this->username, $this->password)) {
 
                $login_ok = true;
 
            }
 
        }
 
        if (!empty($this->username) && $login_ok) {
 
            $this->setAuth($this->username);
 
            if (!empty($this->loginCallback)) {
 
                call_user_func($this->loginCallback,$this->username);
 
            }
 
        }
 
        /**
 
        * If the login failed or the user entered no username,
 
        * output the login screen again.
 
        */
 
        if (!empty($this->username) && !$login_ok) {
 
            $this->status = AUTH_WRONG_LOGIN;
 
        }
 
        if ((empty($this->username) || !$login_ok) && $this->showLogin) {
 
            $this->drawLogin($this->storage->activeUser);
 
            return;
 
        }
 
    }
 
 
 
Voilà. Je n'irai pas jusqu'à expliquer le fonctionnement de cette dernière méthode (qui appelle drawLogin() en cas d'échec, laquelle affiche le formulaire de login), mais cette explication s'arrête ici. Si vous avez des problèmes d'identification sur votre plateforme, cette procédure détaillée devrait vous faire gagner pas mal de temps.
 

Latest revision as of 08:41, 17 January 2023





Introduction

Le système d'authentification de Dolibarr devient relativement complexe, et un bug peut être particulièrement difficile à trouver si l'on ne connaît pas le processus d'authentification. Cette page présente une découpe du processus, qui permet de suivre la procédure et d'intervenir là où il le faut. Une connaissance de la notion de session PHP est requise.

Processus

Le processus démarre par l'appel de la page que l'on souhaite voir. Par exemple, la page d'accueil htdocs/index.php. Mais ce n'est pas ce fichier qui assure la demande d'authentification. En fait toute page de Dolibarr inclut un fichier main.inc.php qui lui même inclut le fichier master.inc.php. Nous avons donc:

 <mypage.php>
     <main.inc.php>
       <master.inc.php>
         #1#
       </master.inc.php>
       #2#
       if (!isset($_SESSION["dol_login"]))
       {
         // Set goontestloop to true if we are coming from a post of the login page ($_POST["username"] and $_POST["actionlogin"] is defined.
         // We may also set goontestloop to true for some other cases depending on the authentication mode (see next chapter)
         $goontestloop = ...  
         if ($goontestloop)
         {
           checkLoginPassEntity(username, password, ...);
           ...
           // if user and pass are ok, we set $login
           $login=
         }
         if (! $login)
         { 
	       // We show login page
	       include_once(DOL_DOCUMENT_ROOT."/lib/security.lib.php");
	       dol_loginfunction($langs,$conf,$mysoc);
	       exit;
         }
         ...
       }
     </main.inc.php>
   #3#
 </mypage.php>

Le #1# représente le chargement de tout un tas de librairie que nous utiliserons par la suite, ainsi que l'initialisation du contexte d'exécution du code PHP (langue, configuration, utilisateur vierge).

Le #2# représente le code d'authentification: Le programme vérifie si on est dans une session loguée (cela signifie que $_SESSION["dol_login"] existe). Si non, on vérifie si on a reçu des données issu du formulaire de login/mote passe. Au premier appel, ce n'est pas le cas puisque nous n'avons pas encore eu le formulaire à l'écran. Aussi on rentre dans le if, et $login étant faux, on affiche alors le formulaire HTML de login et le script se termine.

Après la soumission du login, la même page (donc index.php) est appelée, nous allons toujours dans le #1#, puis #2# et cette fois $_POST["username"] est défini. Aussi nous vérifions si le user et mot de passe sont ok (la vérifivation est faite dans la base, en LDAP ou autre, cela dépend de la variable $dolibarr_main_authentication du fichier de configuration). Si le couple login/pass est ok, la variable $login est définie, aussi nous n'affichons pas à nouveau le formulaire mais nous continuons et positionnons $_SESSION["dol_login"], ainsi au prochain appel d'une page, nous ne rentrons plus dans le "if (! isset($_SESSION["dol_login"]))".

Le #3# correspond à la vérification des permissions métiers et à l'affichage de la page si c'est ok. Voir la page See Permissions pour plus d'informations.

Les mode d'authentification et modules de login

L'appel de checkLoginPassEntity pour valider le couple utilisateur/mot de passe (ou uniquement l'utilisateur dans certains cas) appellera la fonction check_user_password_xxx d'un module de connexion. Le module de connexion appelé dépend du mode d'authentification défini dans votre fichier conf/conf.php.

Le fichier utilisé est nommé htdocs/core/login/functions_xxx.php avec la valeur xxx qui correspond à la valeur définie dans dolibarr_main_authentication dans le fichier de configuration conf/ conf.php.

Voir Authentication,_SSO_and_SSL pour une liste des modes d'authentification (valeurs possibles pour 'xxx' et leur spécificité)