From 3c35bcf17692e317ac3d11fc1fb4021abc51a2c1 Mon Sep 17 00:00:00 2001 From: Etienne Pallier Date: Fri, 23 Dec 2022 18:21:52 +0100 Subject: [PATCH] Gestion plus clean de la connexion --- CHANGELOG | 7 +++++++ README.md | 2 +- src/Controller/AppController.php | 34 +++++++++++++++++----------------- src/Controller/Component/LdapAuthComponent.php | 3 ++- src/Controller/PagesController.php | 4 ++-- src/Controller/UsersController.php | 3 ++- src/Model/Table/LdapConnectionsTable.php | 478 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/Template/Configurations/view.ctp | 4 ++-- 8 files changed, 302 insertions(+), 233 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ae3c0c8..dd296de 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -484,6 +484,13 @@ rien ? ======= CHANGES ======= ------- +23/12/2022 (EP) v5.7.0-3.10.1 + - (i) Gestion bien plus clean de la connexion (ldap ou fake-ldap, avec cache systématique dans table users) + => Si new user (ldap) => ADDED dans cache + => Si user changed (ldap) => UPDATED dans cache + => Si user removed (ldap) => DELETED dans cache + +------- 23/12/2022 (EP) v5.6.1-3.10.1 - (i) Suppression contrainte unicité du nom-pnom dans table users => car on peut autoriser 2 homonymes ou encore une meme personne (nom-pnom) avec 2 login différents diff --git a/README.md b/README.md index aeb4a6a..63db719 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Logiciel testé et validé sur les configurations suivantes : -------------------------------------------------------------------------------------------- Date: 23/12/2022 -Version: v5.6.1-3.10.1 +Version: v5.7.0-3.10.1 HISTORIQUE DES CHANGEMENTS DE VERSION : voir le fichier CHANGES.txt (ou la page web /pages/changes) diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 8b14b4f..3d746d4 100755 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -805,11 +805,11 @@ class AppController extends Controller public function isAuthorizedActionForCurrentUser($action, $id=null, $related_matos_id=null, $user=null) { //return $this->isAuthorizedActionForRole($this->user_role, $action, $id); //return $this->isAuthorizedActionForRole($this->getUserRole($user), $action, $id, $IS_RELATED_ENTITY_ID, $user); - $this->d("IN isAuthorizedActionForCurrentUser(): controleur $this->name, action $action"); + ####$this->d("IN isAuthorizedActionForCurrentUser(): controleur $this->name, action $action"); //return $this->isAuthorizedActionForRole($this->getUserRole($user), $action, $id, $related_matos_id, $user); //return $this->isAuthorizedActionForCurrentUser($action, $id, $related_matos_id, $user); $role_long = $this->getUserRole($user); - $this->d("*************************** CONTROLEUR ".$this->name.", ACTION $action, ROLE $role_long, id=$id, related_matos_id=$related_matos_id"); + ####$this->d("*************************** CONTROLEUR ".$this->name.", ACTION $action, ROLE $role_long, id=$id, related_matos_id=$related_matos_id"); //$this->d("********* USER :"); $this->d2($user); //if ($this->SUPERADMIN_CAN_DO_EVERYTHING) return TRUE; if ($this->confLabinvent->mode_nolimit) return TRUE; @@ -846,7 +846,7 @@ class AppController extends Controller //debug("role is $role"); //$access_condition = $this->is_authorized_action[$action][$role]; $access_condition = $this->getAccessConditionForActionAndRole($action,$role); - $this->d("0) Condition (complète) (NOT desaliased) :"); $this->d($access_condition); + ####$this->d("0) Condition (complète) (NOT desaliased) :"); $this->d($access_condition); //debug($this->is_authorized_action); //debug($this->is_authorized_action[$action]); //debug("access_condition"); debug($access_condition); @@ -859,7 +859,7 @@ class AppController extends Controller $access_condition = $this->is_authorized_action[$action][$role]; } // - si -1 => acccès refusé - $this->d("1) Condition (complète) (desaliased) :"); $this->d($access_condition); + ####$this->d("1) Condition (complète) (desaliased) :"); $this->d($access_condition); //if ($action=='debugOff') debug($access_condition); exit; if ($access_condition === -1) return FALSE; if ($access_condition === 0) return TRUE; @@ -1832,7 +1832,7 @@ class AppController extends Controller $SPECIFIC_METHOD_EXISTS = false; if ($this->confLabinvent->labNameShort) { $labshortname = $this->confLabinvent->labNameShort; - $this->d("lab name is set to $labshortname"); + ####$this->d("lab name is set to $labshortname"); $setAuthorizationsSpecific = "setAuthorizations_$labshortname"; // Si une méthode spécifique (dans le controleur courant) existe pour CE labo, on l'appelle à la place de la méthode générale // Par exemple pour l'IRAP, ça sera $this=>setAuthorizations_IRAP() : @@ -1932,16 +1932,16 @@ class AppController extends Controller // Affichages pour debug //pr($event); - $this->myDebug("step 1B (general): AppController.beforeFilter()"); + ####$this->myDebug("step 1B (general): AppController.beforeFilter()"); $controllerName = $this->request->getParam('controller'); - $this->myDebug("- controller passed : $controllerName"); + ####$this->myDebug("- controller passed : $controllerName"); $passedArgs = $this->request->getParam('pass'); - $this->myDebug("- args passed : ", $passedArgs); + ####$this->myDebug("- args passed : ", $passedArgs); $query = $this->request->getQueryParams(); - $this->myDebug("- query passed : ", $query); + ####$this->myDebug("- query passed : ", $query); // Initialisations pour la suite // - L'entité concernée par l'action @@ -1949,10 +1949,10 @@ class AppController extends Controller $this->e = null; // - L'action demandée et son id le cas échéant (nul par défaut, égal 0) $this->a = $this->getActionPassed(); - $this->myDebug("- action passed : ".$this->a); + ####$this->myDebug("- action passed : ".$this->a); //debug("- action passed : ".$this->a); $this->e_id = $this->getIdPassed(); - $this->myDebug("- id passed : ".$this->e_id); + ####$this->myDebug("- id passed : ".$this->e_id); parent::beforeFilter($event); @@ -1994,8 +1994,8 @@ class AppController extends Controller // - pour le controleur spécifique, // - et pour la vue // Utilisateur connecté (identifié) ? - $this->myDebug("- Utilisateur connecté ? : ", $this->LdapAuth->user() ? "oui" : "non"); - $this->myDebug($this->LdapAuth->user()); + ####$this->myDebug("- Utilisateur connecté ? : ", $this->LdapAuth->user() ? "oui" : "non"); + ####$this->myDebug($this->LdapAuth->user()); //if ($configuration->ldap_used && !$this->); if ($this->LdapAuth->user()) { @@ -2007,9 +2007,9 @@ class AppController extends Controller //$this->user_role = $this->getUserRole(); $this->user_role = $this->getUserRole(); $profile = self::PROFILES["$this->user_role"]; - $this->myDebug("- priviledgedUser is {$this->u}"); + ####$this->myDebug("- priviledgedUser is {$this->u}"); //debug("- priviledgedUser is {$this->u}"); exit; - $this->myDebug("- user_role is {$this->getUserRole()}", "- profile is: $profile"); + ####$this->myDebug("- user_role is {$this->getUserRole()}", "- profile is: $profile"); // L'utilisateur connecté (return null si pas connecté) // $this->Auth->user('id'); //$this->userName = $this->getCurrentUserName(); @@ -2080,7 +2080,7 @@ class AppController extends Controller // Autoriser TOUTES les actions SANS authentification //$this->LdapAuth->allow(); - $this->myDebug("- Actions allowed are: ", $this->LdapAuth->allowedActions); + ####$this->myDebug("- Actions allowed are: ", $this->LdapAuth->allowedActions); /* (EP 20200427 migré dans initialize()) // Message d'erreur en cas de refus de connexion @@ -2819,7 +2819,7 @@ class AppController extends Controller parent::afterFilter($event); - $this->myDebug("step ?? (general): AppController.afterFilter()"); + ####$this->myDebug("step ?? (general): AppController.afterFilter()"); // Tout le temps 'index', why ??N //debug($this->request->getAttribute('params')['action']); if (in_array($this->request->getAttribute('params')['action'], [ diff --git a/src/Controller/Component/LdapAuthComponent.php b/src/Controller/Component/LdapAuthComponent.php index ba88e55..3eec549 100755 --- a/src/Controller/Component/LdapAuthComponent.php +++ b/src/Controller/Component/LdapAuthComponent.php @@ -8,6 +8,7 @@ use Cake\ORM\TableRegistry; class LdapAuthComponent extends AuthComponent { + // return FALSE if bad authentication, OR user if ok public function connection() { try { @@ -15,7 +16,7 @@ class LdapAuthComponent extends AuthComponent $login = $this->request->getData('ldap'); $password = $this->request->getData('password'); // return user or FALSE - // calls src/Model/Table/LdapConnectionsTable.php/ldapAuthentication() + // calls /Model/Table/LdapConnectionsTable.php/ldapAuthentication() return TableRegistry::getTableLocator()->get('LdapConnections')->ldapAuthentication($login, $password); //return TableRegistry::get('LdapConnections')->ldapAuthentication($login, $password); } catch (Exception $e) { diff --git a/src/Controller/PagesController.php b/src/Controller/PagesController.php index bf98a2f..8afa2c9 100755 --- a/src/Controller/PagesController.php +++ b/src/Controller/PagesController.php @@ -68,7 +68,7 @@ class PagesController extends AppController */ public function initialize() { - $this->myDebug("step 0A (specific): PagesController.initialize()"); + ####$this->myDebug("step 0A (specific): PagesController.initialize()"); parent::initialize(); $this->modelClass = false; // On autorise l'action add SANS authentification (unauthenticated) @@ -78,7 +78,7 @@ class PagesController extends AppController // (20200424 EP) public function beforeFilter(\Cake\Event\Event $event) { - $this->myDebug("step 1A (specific): PagesController.beforeFilter()"); + ####$this->myDebug("step 1A (specific): PagesController.beforeFilter()"); // On donne d'abord les autorisations par défaut de AppController parent::beforeFilter($event); diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 1fd9dda..f26e0dd 100755 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -178,7 +178,7 @@ class UsersController extends AppController { // ref: https://book.cakephp.org/4/fr/controllers/components/authentication.html#identifier-les-utilisateurs-et-les-connecter public function login() { - $this->myDebug("step 2: UsersController.login()"); + ####$this->myDebug("step 2: UsersController.login()"); // Un utilisateur a essayé de se loguer if ($this->request->is('post')) { @@ -186,6 +186,7 @@ class UsersController extends AppController { // - sans LDAP //$user = $this->Auth->identify(); // - avec LDAP (ou fake ldap) + // (calls /Controller/Component/LdapAuthComponent.php/connection()) $user = $this->LdapAuth->connection(); //var_dump($user); //debug($user); diff --git a/src/Model/Table/LdapConnectionsTable.php b/src/Model/Table/LdapConnectionsTable.php index 89f07e4..f0d5ede 100644 --- a/src/Model/Table/LdapConnectionsTable.php +++ b/src/Model/Table/LdapConnectionsTable.php @@ -215,7 +215,7 @@ class LdapConnectionsTable extends AppTable ], */ DEFAULT_AUTH_TYPE => [ - $user[DEFAULT_AUTH_TYPE] + $user[DEFAULT_AUTH_TYPE] ], // Pass 'userpassword' => [ @@ -229,6 +229,7 @@ class LdapConnectionsTable extends AppTable return $this->_buildFakeLdapUsersFromDB(); } + // return users list from DB table fakeldapusers, formatted as LDAP private function _buildFakeLdapUsersFromDB() { //NEW @@ -369,7 +370,8 @@ class LdapConnectionsTable extends AppTable ->find() ->where(['id =' => 1]) ->first(); - // Activation forcée de l'option ldap_cached si LDAP_CACHE_ALWAYS_ON + + // Activation forcée de l'option ldap_cached si LDAP_CACHE_ALWAYS_ON // (EP) Ca résoud un bug dû au fait que des infos sont cherchées dans la table users alors que le user n'y est pas... if (LDAP_CACHE_ALWAYS_ON) $this->CONF->ldap_cached = true; $config = $this->CONF; @@ -483,10 +485,8 @@ class LdapConnectionsTable extends AppTable public function getFakeLdapUser($login) { foreach ($this->fakeLDAPUsers as $user) { - /* - debug($login); - debug($user); - */ + //debug($login); + //debug($user[DEFAULT_AUTH_TYPE][0]); //if ($login == $user[$this->authenticationType][0]) if ($login == $user[DEFAULT_AUTH_TYPE][0]) return $user; @@ -628,15 +628,16 @@ class LdapConnectionsTable extends AppTable } - // @return user from DB whith login = $userLogin (or NULL) - private function _getUserFromDB($userLogin) { + // @return user from DB user table whith login = $userLogin (or NULL) + //private function _getUserFromDBByLogin) { + private function _getCachedUserByLogin($userLogin) { //debug($userLogin); exit; //$ldapUser = TableRegistry::getTableLocator()->get('Users')->find() return $this->usersTable->find() - ->where([ - 'username' => $userLogin - ]) - ->first(); + ->where([ + 'username' => $userLogin + ]) + ->first(); } /** @@ -653,12 +654,12 @@ class LdapConnectionsTable extends AppTable * - a period (.) signifies ISO Y.M.D. => strtotime("11.12.10") */ $date_now = date("Y-m-d H:i:s"); - $this->mydebugmsg("now :".$date_now); + ####$this->mydebugmsg("now :".$date_now); $date_now = new \DateTime($date_now); $date_cached = $this->CONF->ldap_cache_last_update; // Pourquoi j'ai pas les secondes qui s'affichent ???!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - $this->mydebugmsg("cached :".$date_cached); + ####$this->mydebugmsg("cached :".$date_cached); /* bugfixing $date_cached pour Inventirap (IRAP) (php5 ou vieux mysql ou pb de config ???) : * Inventirap affiche l'année sur 2 chiffres : 'cached :05/06/19 15:36' * Ma version perso affiche l'année sur 4 chiffres : 'cached :05/06/2019 15:36' @@ -672,16 +673,15 @@ class LdapConnectionsTable extends AppTable //$year = '20'.substr($y,1,2); // '2019' $date_cached = substr($date_cached,0,6)."20".substr($year,1,2).substr($date_cached,8); // '05/06/19 15:36'; } - $this->mydebugmsg("cached2 :".$date_cached); + ####$this->mydebugmsg("cached2 :".$date_cached); $date_cached = \DateTime::createFromFormat('d/m/Y H:i',$date_cached); //debug("now :".$date_now->format('Y-m-d H:i:s') ); //debug("cached :".$date_cached->format('Y-m-d H:i:s') ); - $this->mydebugmsg("Temps écoulé depuis last save:"); - $this->mydebugmsg($date_now->diff($date_cached)->format('%i mn %s sec')); + ####$this->mydebugmsg("Temps écoulé depuis last save:"); $this->mydebugmsg($date_now->diff($date_cached)->format('%i mn %s sec')); $date_cached->add(new \DateInterval('PT'.$this->CONF->ldap_cache_validity_duration.'M')); - $this->mydebugmsg("date_cached (added) :".$date_cached->format('Y-m-d H:i:s') ); + ####$this->mydebugmsg("date_cached (added) :".$date_cached->format('Y-m-d H:i:s') ); /* $date_now = $date_now->getTimestamp(); @@ -722,8 +722,7 @@ class LdapConnectionsTable extends AppTable debug($d == $now); */ - $this->mydebugmsg("expired ?"); - $this->mydebugmsg($date_cached < $date_now); + ####$this->mydebugmsg("expired ?"); $this->mydebugmsg($date_cached < $date_now); return ($date_cached < $date_now); //return TRUE; } @@ -738,48 +737,124 @@ class LdapConnectionsTable extends AppTable } - private function _isUserAlreadyCached(array $user_logged) { + private function _isUserAlreadyCachedForLogin(array $user_login) { //debug($user_logged); - return $this->_getUserFromDB($user_logged[$this->authenticationType][0]) !== null; + //$user_login = $this->_getUserLogin($user_logged); + return $this->_getCachedUserByLogin($user_login) !== null; } - + + private function _getUserLogin(array $user) { + return $user[$this->authenticationType][0]; + } + + // Return true if logged user $user_logged is exactly same user as cached user $user_cached + private function _isExactSameUser($userFromLDAP,$user_cached) { + $BOURRIN=false; + //$this->mydebugmsg("user already exists => update it"); + + // Same email ? + if ( + isset($userFromLDAP['mail']) + && + ( $BOURRIN || ($user_cached->email != $userFromLDAP['mail'][0]) ) + ) return false; + + // Same name AND firstname ? + if ( + ( isset($userFromLDAP['sn']) && isset($userFromLDAP['givenname']) ) + && + strtolower($user_cached->nom) != strtolower( $userFromLDAP['sn'][0].' '.$userFromLDAP['givenname'][0] ) + ) return false; + + // SAME user + return true; + } + + private function _addNewUserInCacheFromLdapUser($userFromLDAP) { + $this->usersTable->save($this->_getLDAPuserFormattedAsDB($userFromLDAP)); + ####$this->mydebugmsg("added new user"); $this->mydebugmsg($userFromLDAP); + } + + private function _updateChangedUserInCacheFromLdapUser($userFromLDAP, $user_cached) { + $user_cached->email = $userFromLDAP['mail'][0]; + // Tant qu'on y est, on met aussi à jour le nom et prenom + $user_cached->nom = $userFromLDAP['sn'][0].' '.$userFromLDAP['givenname'][0]; + $this->usersTable->save($user_cached); + ####$this->mydebugmsg("updated changed user"); $this->mydebugmsg($userFromLDAP); + } + /* - * UPDATE users CACHE (save all (fake)LDAP users into "users" db table) + * UPDATE users CACHE + * (save all (fake)LDAP users into "users" db table) * ONLY if necessary */ //private function _updateLdapCacheIfNeeded() { private function _updateUsersCacheIfNeeded(array $user_logged = null) { + /* $user_logged looks like this (from fakeLDAP) : + [ + 'sn' => [ + (int) 0 => 'SuperAdmin' + ], + 'mail' => [ + (int) 0 => 'superadmin@emailnew.com' + ], + 'givenname' => [ + (int) 0 => 'Stephane' + ], + 'uid' => [ + (int) 0 => 'superadminnew' + ], + 'userpassword' => [ + (int) 0 => '$2y$10$LZzpws3oDidBcqO/Fy1RTedLLk3ENTmplny5J7bZ6R1PqFoGOw3Ma' + ] + ] + */ + // Si cache pas activé => return //debug("C1"); //if (!LDAP_CACHE_ALWAYS_ON && !$this->CONF->ldap_cached) return; + /* + TODO: + EP 22/12/2022 + DEAC + Je désactive cette ligne car on est bien OBLIGÉ d'utiliser un cache + Puisqu'on va toujours chercher la liste des users dans la table users + ET que c'est dans cette table qu'on trouve leurs rolez !!! + if (! $this->CONF->ldap_cached) return; + */ + + # + # 1) ADD or UPDATE user $user_logged (if not null), only if NEW or CHANGED + # + + if ($user_logged) { + //$user_cached = $this->_getUserCachedForUserLogged($user_logged); + $user_cached = $this->_getCachedUserByLogin($this->_getUserLogin($user_logged)); + // NEW user ? + if (is_null($user_cached)) + $this->_addNewUserInCacheFromLdapUser($user_logged); + // Existing user but CHANGED ? + elseif ( ! $this->_isExactSameUser($user_logged,$user_cached) ) + $this->_updateChangedUserInCacheFromLdapUser($user_logged, $user_cached); + // UPDATE THIS user in CACHE (users table) + } - /* - * On ne met à jour le cache des users QUE si : - * - le user qui vient de se loguer n'est pas dans le cache - * OU - * - le cache est périmé - * - * Sinon => RETURN - */ - //debug("C2"); - if ( - ! $this->_ldapCacheIsExpired() - && - ! ( $user_logged && !$this->_isUserAlreadyCached($user_logged) ) - ) - return; - DEBUG && debug("User pas dans DB users"); + # + # 2) Update all users, only if CACHE expired + # + if (! $this->_ldapCacheIsExpired()) return; + + #DEBUG && debug("NEW User (pas dans DB users table)"); - //debug("C3"); // Get all users from (fake)LDAP $usersFromLDAP = $this->_getAllUsersFromLDAP(); // LDAP should return at least some users, otherwise ERROR assert(!empty($usersFromLDAP)); // Get all users from DB - $usersFromDB = $this->_getAllUsersFromDB(FALSE); + $users_cached = $this->_getAllUsersFromDB(FALSE); // Add new(fake)ldap users (only) and update existing users foreach ($usersFromLDAP as $userFromLDAP) { @@ -801,7 +876,9 @@ class LdapConnectionsTable extends AppTable continue; } - $userFromLDAPLogin = $userFromLDAP[$this->authenticationType][0]; + //$userFromLDAPLogin = $userFromLDAP[$this->authenticationType][0]; + $userFromLDAPLogin = $this->_getUserLogin($userFromLDAP); + // (EP 5/6/19 : on n'a pas accès au password stocké dans le ldap, on ne peut donc pas le stocker dans le cache) //$currentLdapUserPwd = $userFromLDAP['userpassword'][0]; @@ -809,83 +886,25 @@ class LdapConnectionsTable extends AppTable // Do not save fake ldap user in DB if ($userFromLDAPLogin == '_fake_ldap_user_') continue; - $userFromDB = $this->_getUserFromDB($userFromLDAPLogin); - - // 1) ADD NEW user - if (is_null($userFromDB)) { - - //$this->mydebugmsg("user does not exist => add it to DB"); - // Ajout du nouvel utilisateur - $this->usersTable->save($this->_getLDAPuserFormattedAsDB($userFromLDAP)); - //$this->usersTable->query("FLUSH TABLES"); - - // Correction de son mot de passe - /* (EP 5/6/19 : on n'a pas accès au password stocké dans le ldap, on ne peut donc pas le stocker dans le cache) - $userFromDB = $this->_getUserFromDB($currentLdapUserLogin); - assert(!is_null($userFromDB)); - //$this->mydebugmsg("UPDATE users SET password = '".$currentLdapUserPwd."' WHERE id = ".$userFromDB->id); - //$this->usersTable->query("UPDATE users SET password = '".$userFromLDAP['userpassword'][0]."' WHERE id = ".$userFromDB->id); - $this->_updateUserPassword($userFromDB->id, $currentLdapUserPwd); - */ - /* - $query = $this->usersTable->query(); - $query->update() - ->set([ 'password' => $userFromLDAP['userpassword'][0] ]) - ->where(['id' => $userFromDB->id]) - ->execute(); - */ - //exit; - } // NEW user - - // 2) UPDATE Existing user => only if email changed - else { - //TODO: a virer, only for test - $BOURRIN=false; - //$this->mydebugmsg("user already exists => update it"); - - // 1) Update email (only if changed) - if ( - isset($userFromLDAP['mail']) - && - ( $BOURRIN || ($userFromDB->email != $userFromLDAP['mail'][0]) ) - ) { - //$this->mydebugmsg("email diff ? "); - //$this->mydebugmsg($userFromDB->email !== $userFromLDAP['mail'][0]); - $userFromDB->email = $userFromLDAP['mail'][0]; - // Tant qu'on y est, on met aussi à jour le nom et prenom - $userFromDB->nom = $userFromLDAP['sn'][0].' '.$userFromLDAP['givenname'][0]; - $this->usersTable->save($userFromDB); - } - - // 2) Update password (only if changed) - /* (EP 5/6/19 : on n'a pas accès au password stocké dans le ldap, on ne peut donc pas le stocker dans le cache) - //debug("password = ".$userFromDB->password); - //debug("userpassword = ".$userFromLDAP['userpassword'][0]); - /STAR (EP 28/5/19) Sauvegarde du mot de passe : ATTENTION ! - * Si on utilise la methode save() de cakephp, le mot de passe n'est pas copié tel quel, mais re-crypté !!! - * On doit donc le copier "à la hard" avec du code sql direct pour shunter la methode save() - * Ceci n'est donc pas possible : - $userFromDB->password = $userFromLDAP['userpassword'][0]; - $this->usersTable->save($userFromDB); - STAR/ - // Par contre, ceci fonctionne : - //debug("UPDATE users SET password = '".$userFromLDAP['userpassword'][0]."' WHERE id = ".$userFromDB->id); - if ( $BOURRIN || ($userFromDB->password != $currentLdapUserPwd) ) { - //$this->mydebugmsg("pass diff ? "); - //$this->mydebugmsg($userFromDB->password !== $userFromLDAP['userpassword'][0]); - //$this->usersTable->query("UPDATE users SET password = '".$userFromLDAP['userpassword'][0]."' WHERE id = ".$userFromDB->id); - $this->_updateUserPassword($userFromDB->id, $currentLdapUserPwd); - } - */ - } // existing user => update + $user_cached = $this->_getCachedUserByLogin($userFromLDAPLogin); + + // (1) NEW user ? => ADD + if (is_null($user_cached)) + $this->_addNewUserInCacheFromLdapUser($userFromLDAP); + + // (2) Existing user but CHANGED ? => UPDATE + elseif ( ! $this->_isExactSameUser($userFromLDAP,$user_cached) ) + $this->_updateChangedUserInCacheFromLdapUser($userFromLDAP, $user_cached); + } - // 3) DELETE old users (which are no more in LDAP) + // (3) DELETE old users (which are no more in LDAP) // dangereux, et faire attention de ne pas supprimer le user superadmin ldap créé au début du projet... - $DO_DELETE = false; + //$DO_DELETE = false; + $DO_DELETE = true; // - Select only the login (['uid']) column $usersFromLDAPLogins = array_column($usersFromLDAP, $this->authenticationType); - $this->mydebugmsg($usersFromLDAPLogins); + ####$this->mydebugmsg($usersFromLDAPLogins); /* * Avec LDAP IRAP, on obtient pour $usersFromLDAPLogins, 0 à 439 (440 users) : [ @@ -910,7 +929,7 @@ class LdapConnectionsTable extends AppTable */ // - Select only the [0] column $usersFromLDAPLogins = array_column($usersFromLDAPLogins, 0); - $this->mydebugmsg($usersFromLDAPLogins); + ####$this->mydebugmsg($usersFromLDAPLogins); /* * Avec LDAP IRAP, on obtient pour $usersFromLDAPLogins, 0 à 439 (440 users) : [ @@ -925,7 +944,7 @@ class LdapConnectionsTable extends AppTable ... ] */ - foreach ($usersFromDB as $userFromDB) { + foreach ($users_cached as $user_cached) { /* debug("DELETE"); debug("look for $userFromDB->username"); @@ -933,11 +952,11 @@ class LdapConnectionsTable extends AppTable */ //debug($usersFromLDAP); //$usersFromLDAPLogins = array_column($usersFromLDAP, 'uid'); - if (! in_array($userFromDB->username, $usersFromLDAPLogins)) { + if (! in_array($user_cached->username, $usersFromLDAPLogins)) { // (6/6/19) 3 users found (superadmin, imoro, mimelhaine) - $this->mydebugmsg("OLD user should be deleted:"); - $this->mydebugmsg($userFromDB->username); - $DO_DELETE && $userFromDB->delete(); + ####$this->mydebugmsg("OLD user should be deleted:"); $this->mydebugmsg($user_cached->username); + #$this->mydebugmsg($user_cached); + $DO_DELETE && $this->usersTable->delete($user_cached); } } @@ -967,7 +986,7 @@ class LdapConnectionsTable extends AppTable $this->_updateUsersCacheIfNeeded(); // 1) Search user in DB - $ldapUser = $this->_getUserFromDB($user_login); + $ldapUser = $this->_getUserFromDBByCachedLogir_login); /S $ldapUser = TableRegistry::getTableLocator()->get('Users')->find() ->where([ @@ -1069,81 +1088,60 @@ class LdapConnectionsTable extends AppTable return FALSE; } - - // MAIN ENTRY POINT of this class - /* - * @param string $user_login - * @param string $user_password - * @return logged user LDAP attributes (FALSE if user not found in LDAP) - */ - public function ldapAuthentication($user_login, $user_password) { - // Bad configuration => FAIL - if (! $this->_checkAndGetConfiguration()) return FALSE; - - // Par défaut, on retourne FALSE (échec de connexion) + + + + + private function _ldapAuthenticationREAL($user_login, $user_password) { + // No connexion allowed without password + if (strlen(trim($user_password)) == 0) return FALSE; + + /* + // TODO: optimisation possible + // 1) Search user in CACHE (DB) + $user_fetched = $this->checkAndFetchLDAPUserFromDB($user_login, $user_password); + $this->mydebugmsg("(1) user found in DB is:"); + $this->mydebugmsg($user_fetched); + //TODO: A VIRER !!! //$user_fetched = FALSE; - - // (EP 5/6/19 : on n'a pas accès au password stocké dans le ldap, on ne peut donc pas le stocker dans le cache) - - // 1 - REAL LDAP - //debug($this->LDAP_USED); exit; - if ($this->LDAP_USED) try { - // No connexion allowed without password - if (strlen(trim($user_password)) == 0) return FALSE; - - /* - // TODO: optimisation possible - // 1) Search user in CACHE (DB) - $user_fetched = $this->checkAndFetchLDAPUserFromDB($user_login, $user_password); - $this->mydebugmsg("(1) user found in DB is:"); - $this->mydebugmsg($user_fetched); - //TODO: A VIRER !!! - //$user_fetched = FALSE; - // 2) If not CACHED, search user in LDAP - if ($user_fetched === FALSE) { - */ - //$user_fetched = $this->checkAndFetchUserFromLdap($user_login, $user_password); - $just_these = []; - // TODO: vérifier si cette ligne est bien utile ou pas... (avant on faisait ça) - //if (! $this->ldap_authentified) $just_these = array("cn"); - // Construction du filtre avec le filtre de la base de données avec un & sur le login de l'utilisateur - // Si aucun filtre n'est défini dans la base de données on aura juste (& ($this->authenticationType=$user_login)) - // ex: "(&(objectClass=person)(memberOf:1.2.840.113556.1.4.1941:=cn=ucbl.osu.cral,ou=groups,ou=27,ou=sim,ou=univ-lyon1,dc=univ-lyon1,dc=fr)(sAMAccountName=$user_login))"; - $filter = '(&'.$this->filter.'('.$this->authenticationType.'='.$user_login.'))'; - //TODO: optimisation, refactoriser si comportement général - //$binddn .= ','.$this->baseDn; - //$user_fetched = $this->_ldapSearch($filter, $just_these, $user_login, $user_password); - $user_fetched = $this->_ldapSearch($filter, $just_these); - //$this->mydebugmsg("(1) user found in LDAP is:"); - //$this->mydebugmsg($user_fetched); - //$this->mydebugmsg($user_fetched[0]); - if ($user_fetched['count'] != 0) { - $user_fetched = $user_fetched[0]; - if ($this->_ldapAuth($user_fetched["dn"], $user_password)) { - // CACHE the new user in DB for next time - ///$this->_saveNewUserInDB($user_fetched[0]); - // CACHE ALL ldap users - $this->_updateUsersCacheIfNeeded($user_fetched); - // return logged user (authentified) - return $user_fetched; - } - //return null; - //return FALSE; + // 2) If not CACHED, search user in LDAP + if ($user_fetched === FALSE) { + */ + //$user_fetched = $this->checkAndFetchUserFromLdap($user_login, $user_password); + $just_these = []; + // TODO: vérifier si cette ligne est bien utile ou pas... (avant on faisait ça) + //if (! $this->ldap_authentified) $just_these = array("cn"); + // Construction du filtre avec le filtre de la base de données avec un & sur le login de l'utilisateur + // Si aucun filtre n'est défini dans la base de données on aura juste (& ($this->authenticationType=$user_login)) + // ex: "(&(objectClass=person)(memberOf:1.2.840.113556.1.4.1941:=cn=ucbl.osu.cral,ou=groups,ou=27,ou=sim,ou=univ-lyon1,dc=univ-lyon1,dc=fr)(sAMAccountName=$user_login))"; + $filter = '(&'.$this->filter.'('.$this->authenticationType.'='.$user_login.'))'; + //TODO: optimisation, refactoriser si comportement général + //$binddn .= ','.$this->baseDn; + //$user_fetched = $this->_ldapSearch($filter, $just_these, $user_login, $user_password); + $user_fetched = $this->_ldapSearch($filter, $just_these); + //$this->mydebugmsg("(1) user found in LDAP is:"); + //$this->mydebugmsg($user_fetched); + //$this->mydebugmsg($user_fetched[0]); + if ($user_fetched['count'] != 0) { + $user_fetched = $user_fetched[0]; + if ($this->_ldapAuth($user_fetched["dn"], $user_password)) { + // CACHE the new user in DB for next time + ///$this->_saveNewUserInDB($user_fetched[0]); + // CACHE ALL ldap users + ####$this->_updateUsersCacheIfNeeded($user_fetched); + // return logged user (authentified) + return $user_fetched; } - return FALSE; - //return $user_fetched; // Noter que $user_fetched peut etre egal a FALSE (si pas trouvé) - - } catch (Exception $e) { - //echo 'Exception LDAP : ', $e->getMessage(), "\n"; - debug('Exception LDAP : '); - debug($e->getMessage()); - exit; - } // REAL LDAP - - // OU BIEN - - // 2 - FAKE LDAP used + //return null; + //return FALSE; + } + return FALSE; + //return $user_fetched; // Noter que $user_fetched peut etre egal a FALSE (si pas trouvé) + } + + + private function _ldapAuthenticationFAKE($user_login, $user_password) { //debug($this->baseDn); $user_fetched = $this->getFakeLdapUser($user_login); /* @@ -1153,8 +1151,7 @@ class LdapConnectionsTable extends AppTable debug($user_fetched); exit; */ - $this->mydebugmsg("(1) user found in FAKE LDAP is:"); - $this->mydebugmsg($user_fetched); + ####$this->mydebugmsg("(1) user found in FAKE LDAP is:"); $this->mydebugmsg($user_fetched); /* Voici un exemple de ce qui est dans $user_fetched (fake ldap) : [ 'sn' => [ @@ -1177,13 +1174,39 @@ class LdapConnectionsTable extends AppTable // debug($user); //if ($user === false) return FALSE; if ($user_fetched !== false) { + + /* $user_fetched looks like this (from fakeLDAP) : + [ + 'sn' => [ + (int) 0 => 'SuperAdmin' + ], + 'mail' => [ + (int) 0 => 'superadmin@emailnew.com' + ], + 'givenname' => [ + (int) 0 => 'Stephane' + ], + 'uid' => [ + (int) 0 => 'superadminnew' + ], + 'userpassword' => [ + (int) 0 => '$2y$10$LZzpws3oDidBcqO/Fy1RTedLLk3ENTmplny5J7bZ6R1PqFoGOw3Ma' + ] + ] + */ + // $this->authenticationType peut valoir "uid" ou "cn"... (par défaut "uid" pour le fake ldap, à confirmer...) // if ($user['uid'][0] == "_NouvelUtilisateur_username" && $user['userpassword'][0] == "_NouvelUtilisateur_password") return $user; // if ($user[$this->authenticationType][0] == "_NouvelUtilisateur_username" && $user['userpassword'][0] == "_NouvelUtilisateur_password") return $user; - // Est-ce le user FAKE LDAP par defaut (_fake_ldap_user_) ? si oui, on le laisse passer + + /* EP 23/12/22 On n'utilise plus ça + * + // Est-ce le user FAKE LDAP par defaut (_fake_ldap_user_) ? si oui, on le laisse passer //if ($user_fetched[$this->authenticationType][0] == $this->_getTheFakeLdapUser()['login'] && $user_fetched['userpassword'][0] == $this->_getTheFakeLdapUser()['pass']) if ($user_fetched[DEFAULT_AUTH_TYPE][0] == $this->_getTheFakeLdapUser()['login'] && $user_fetched['userpassword'][0] == $this->_getTheFakeLdapUser()['pass']) return $user_fetched; + */ + /* debug("user_password = ".$user_password); debug("user found in db is "); @@ -1192,9 +1215,10 @@ class LdapConnectionsTable extends AppTable debug($user_fetched['userpassword'][0]); exit; */ - // Sinon, on regarde si c'est un user de la table fakeldapusers + + // Sinon, on regarde si c'est un user de la table fakeldapusers if ( (new DefaultPasswordHasher())->check($user_password,$user_fetched['userpassword'][0]) ) { - $this->_updateUsersCacheIfNeeded($user_fetched); + ####$this->_updateUsersCacheIfNeeded($user_fetched); return $user_fetched; } } @@ -1206,19 +1230,54 @@ class LdapConnectionsTable extends AppTable // On met à jour le cache des users (si nécessaire) //if ($user_fetched !== FALSE) $this->_updateUsersCacheIfNeeded($user_fetched); // Retourne le user authentifié ou bien FALSE - //return $user_fetched; - - } // ldapAuthentication() - - + //return $user_fetched; + } - - - -} // fin de class LdapConnectionsTable + + // MAIN ENTRY POINT of this class + /* + * @param string $user_login + * @param string $user_password + * @return logged user LDAP attributes (FALSE if user not found in LDAP) + */ + public function ldapAuthentication($user_login, $user_password) { + // Bad configuration => FAIL + if (! $this->_checkAndGetConfiguration()) return FALSE; + + // Par défaut, on retourne FALSE (échec de connexion) + //$user_fetched = FALSE; + + // (EP 5/6/19 : on n'a pas accès au password stocké dans le ldap, on ne peut donc pas le stocker dans le cache) + + # + # 1 - REAL LDAP + # + if ($this->LDAP_USED) + try { + $user_fetched = $this->_ldapAuthenticationREAL($user_login, $user_password); + } + catch (Exception $e) { + //echo 'Exception LDAP : ', $e->getMessage(), "\n"; + debug('Exception LDAP : '); + debug($e->getMessage()); + exit; + } // REAL LDAP + + # + # 2 - FAKE LDAP used + # + else + $user_fetched = $this->_ldapAuthenticationFAKE($user_login, $user_password); + if ($user_fetched !== FALSE) + // Update Users cache if needed (especially for the logged user) + $this->_updateUsersCacheIfNeeded($user_fetched); + + return $user_fetched; + } // ldapAuthentication() + /* Voici un exemple de ce qui est dans $user_fetched[0] (structure LDAP IRAP) : // ce qui est retourné par le fake ldap (imitation bien faite non ?) @@ -1440,6 +1499,7 @@ class LdapConnectionsTable extends AppTable ] */ - + +} // fin de class LdapConnectionsTable ?> diff --git a/src/Template/Configurations/view.ctp b/src/Template/Configurations/view.ctp index 9d1245e..169573d 100644 --- a/src/Template/Configurations/view.ctp +++ b/src/Template/Configurations/view.ctp @@ -181,7 +181,7 @@ function $this->MyHelper->echoSectionStop() { ******** */ - $this->MyHelper->echoSectionStart("Optmisation"); + $this->MyHelper->echoSectionStart("Optimisation"); /* LDAP optimisation */ if (is_null($configurationObj->ldap_cache_last_update)) $ldap_cache_elapsed_time = ''; else { @@ -206,7 +206,7 @@ function $this->MyHelper->echoSectionStop() { } $this->MyHelper->displayElement(__('Optimisation liste utilisateurs (utilisation Cache en BD)'), h($configurationObj->ldap_cached)?"Oui":"Non" ); $this->MyHelper->displayElement(__('Durée validité cache (mn)'), h($configurationObj->ldap_cache_validity_duration)); - $this->MyHelper->displayElement(__('Date dernière mise à jour cache'), h($configurationObj->ldap_cache_last_update)); + $this->MyHelper->displayElement(__('Date dernière mise à jour cache (UTC)'), h($configurationObj->ldap_cache_last_update)); $this->MyHelper->displayElement(__('Temps écoulé depuis dernière mise à jour'), $ldap_cache_elapsed_time); $this->MyHelper->echoSectionStop(); -- libgit2 0.21.2