DEBUG_MODE) { //Configure::write('debug', true); debug($arg); if ($stop) exit(); } } public function __construct() { parent::__construct(); } // EP public function useFakeLdap() { return ! $this->useLdap(); } public function useLdap() { $this->checkConfiguration(); return $this->LDAP_USED; } private function buildFakeLdapUsers() { return $this->buildFakeLdapUsersFromDB(); } private function buildFakeLdapUsersFromDB() { $users = TableRegistry::getTableLocator()->get('Users')->find(); $ldapUsers = []; foreach ($users as $user) { $names = explode(" ", $user['nom']); $givenName = isset($names[1]) ? $names[1] : " "; $ldapUsers[] = [ 'sn' => [ $names[0] ], 'mail' => [ $user['email'] ], 'givenname' => [ $givenName ], $this->authenticationType => [ $user['username'] ], 'userpassword' => [ $user['password'] ] ]; } /* EP (aout 2017) * ATTENTION : Utilisateur IMPORTANT. * Avec cet utilisateur, on simule un utilisateur qui n'est PAS dans la table utilisateurs * Il devrait donc se voir attribuer un role "Utilisateur" sans pour autant que ça soit écrit dans la table !!! * login = '_NouvelUtilisateur_username' * pass = '_NouvelUtilisateur_password' * $prefix = "_NouvelUtilisateur_"; */ $ldapUsers[] = [ 'sn' => [ 'UTILISATEUR' ], 'givenname' => [ 'FAKE_LDAP' ], // 'mail' => [$login.'email'], 'mail' => [ 'fakeldapuser@domain.fr' ], // $this->authenticationType => [$prefix.'username'], $this->authenticationType => [ $this->getTheFakeLdapUser()['login'] ], // $this->authenticationType => ['usere'], 'userpassword' => [ $this->getTheFakeLdapUser()['pass'] ] // 'userpassword' => ['toto'], ]; return $ldapUsers; } private function checkConfiguration() { $config = TableRegistry::getTableLocator()->get('Configurations') ->find() ->where(['id =' => 1]) ->first(); $this->DEBUG_MODE = $config->mode_debug; $this->LDAP_USED = $config->ldap_used; if (! $this->LDAP_USED) { //$this->authenticationType = $config->authentificationType_ldap; $this->authenticationType = $config->ldap_authenticationType; if (empty($this->fakeLDAPUsers)) $this->fakeLDAPUsers = $this->buildFakeLdapUsers(); return true; } // debug($this->fakeLDAPUsers); $ldapConfig = $config->toArray(); if (! empty($config->ldap_host) && ! empty($config->ldap_port) && ! empty($config->ldap_baseDn) && ! empty($config->ldap_authenticationType) && ! empty($config->ldap_filter)) { $this->host = $config->ldap_host; $this->port = $config->ldap_port; $this->baseDn = $config->ldap_baseDn; $this->filter = $config->ldap_filter; $this->authenticationType = $config->ldap_authenticationType; $this->ldap_authentified = $config->ldap_authentified; $this->bindDn = $config->ldap_bindDn; $this->bindPass = $config->ldap_bindPass; return true; } throw new Exception('The ldap configuration is not valid :
'); } // REAL LDAP only /** * @return array or boolean : all users from DB (CACHE of the LDAP) or FALSE (if table emtpy or expired data) */ private function fetchAllUsersFromDB() { // On remet à jour tous les 7 jours $PEREMPTION_NB_DAYS = 7; // Doit aussi retourner FALSE si la ligne FAKE de la table users // (celle qui contient le user_name "FAKE_USER") // a une date "updated" périmée (now - updated > $PEREMPTION_NB_DAYS) // (update automatique de tous les users, chaque semaine, pour rester synced avec le LDAP) // By default, no user in CACHE return FALSE; } // REAL LDAP only // Sauvegarde de tous les users du LDAP en BD (avec un rythme de mise à jour hebdo) // Seulement les champs: nom, pnom, login, pass, email, create, updated, profile private function saveAllUsersInDB($users_fetched) { // START TRANSACTION // 1) Update (ou création) de la ligne FAKE (contient le user_name "FAKE_USER") => avec une date "updated" // 2) Update (ou création) de chaque user contenu dans $users_fetched // Attention à ne pas perdre l'attribut "profile", surtout pour les users privilégiés!!! (les autres ont un profile = "Utilisateur") // END TRANSACTION (COMMIT) // SAVE s'est bien passé return TRUE; } /** * @return $users_fetched or FALSE */ // REAL or FAKE LDAP public function getAllLdapUsers() { try { if ($this->checkConfiguration()) { // REAL LDAP if ($this->LDAP_USED) { // 1) Search users in CACHE (DB) $users_fetched = $this->fetchAllUsersFromDB(); // 2) Not found in CACHE, so search users in LDAP if ($users_fetched === FALSE) { $users_fetched = $this->searchLdap($this->filter, []); // CACHE the new user in DB for next time if ($users_fetched !== FALSE) $this->saveAllUsersInDB($users_fetched); } } // FAKE LDAP else { $users_fetched = $this->fakeLDAPUsers; } // Noter que $user_fetched peut etre egal a FALSE (si rien trouvé) return $users_fetched; } } catch (Exception $e) {} // Pb, rien trouvé return FALSE; } public function getAuthenticationType() { return $this->authenticationType; } // EP added public function getFakeLdapUser($login) { foreach ($this->fakeLDAPUsers as $user) { if ($login == $user[$this->authenticationType][0]) return $user; } return FALSE; } /** * Return a list of Users with key = username & value = email */ public function getUsersLoginAndEmail() { $usersWithNameAndEmail = []; // Get all users (with ALL their attributes) $u = $this->getAllLdapUsers(); // Sort users //sort($u); //debug($u); $this->mydebugmsg("ldap users 0 and 1:"); $this->mydebugmsg($u[0]); $this->mydebugmsg($u[1]); // (EP) Refactorisation pour éviter code redondant du temps des stagiaires... $nb_users = $this->LDAP_USED ? $u['count'] : sizeof($u)-1; for ($i = 0; $i < $nb_users; $i ++) // $utilisateurs["Pallier Etienne"] = ["email"] ////$usersWithNameAndEmail[ $u[$i]['sn'][0].' '.$u[$i]['givenname'][0] ] = $u[$i]['mail'][0]; $usersWithNameAndEmail[ $u[$i]['sn'][0].' '.$u[$i]['givenname'][0] ] = array( "login" => $u[$i]['uid'][0], "email" => $u[$i]['mail'][0] ); // Sort users (without modifying the keys, don't use sort() but asort() !!!!!!!!!!!!!) ksort($usersWithNameAndEmail); //debug($usersWithNameAndEmail); return $usersWithNameAndEmail; } /** * Return a list of Users with key = username & value = username */ public function getListUsers() { $utilisateurs = []; // Get all users (with ALL their attributes) $u = $this->getAllLdapUsers(); // Sort users //sort($u); //debug($u); // (EP) Refactorisation pour éviter code redondant ci-dessous, c'était pourtant pas compliqué, poil dans la main... $nb_users = $this->LDAP_USED ? $u['count'] : sizeof($u)-1; // $utilisateurs["Pallier Etienne"] = "Pallier Etienne" for ($i = 0; $i < $nb_users; $i ++) $utilisateurs[ $u[$i]['sn'][0].' '.$u[$i]['givenname'][0] ] = $u[$i]['sn'][0].' '.$u[$i]['givenname'][0]; //debug($utilisateurs); // Sort users (without modifying the keys, don't use sort() but asort() !!!!!!!!!!!!!) ksort($utilisateurs); //debug($utilisateurs); return $utilisateurs; } /** * Return a list of login ofUsers with key = username & value = login */ public function getListLoginUsers() { $u = $this->getAllLdapUsers(); $utilisateurs = []; if ($this->LDAP_USED) { for ($i = 0; $i < $u['count']; $i ++) { $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i][$this->authenticationType][0]; } } else { for ($i = 0; $i < sizeof($u) - 1; $i ++) { $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i][$this->authenticationType][0]; } } return $utilisateurs; } /** * Return a list of mail of Users with key = username & value = mail */ public function getListEmailUsers() { $u = $this->getAllLdapUsers(); $utilisateurs = []; if ($this->LDAP_USED) { for ($i = 0; $i < $u['count']; $i ++) { if (isset($u[$i]['mail'][0])) { $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i]['mail'][0]; } else { $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = 'N/A'; } } } else { for ($i = 0; $i < sizeof($u) - 1; $i ++) { $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i]['mail'][0]; } } return $utilisateurs; } /** * Return size of list users public function getNbUsers() { $u = $this->getAllLdapUsers(); if ($this->LDAP_USED) { $nbUsers = $u['count']; } else { $nbUsers = sizeof($u) - 1; } return $nbUsers; } */ // Utilisateur du ldap qui n'est pas dans la table utilisateurs // => il a donc le role "Utilisateur" PAR DEFAUT private function getTheFakeLdapUser() { return [ 'login' => '_fake_ldap_user_', 'pass' => '_fake_ldap_user_pass' ]; } // TODO: implement // REAL LDAP only private function checkAndFetchUserFromDB($user_login, $user_password) { // Doit aussi return false si ce user_login est "périmé" (sa date "created" est > 2 mois par exemple), // ce qui obligera à relire ses données dans le LDAP et donc se mettre à jour // By default, user is not in DB return FALSE; } // REAL LDAP only // SEARCH en 4 étapes private function searchLdap($filter, $just_these, $user_login=NULL, $user_password=NULL) { // (1) CONNEXION $ldapConnection = ldap_connect($this->host, $this->port) or die("Could not connect to $this->host (port $this->port)"); if ($ldapConnection) { // (2) SET OPTIONS ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); // (3) BINDING OPTIONNEL (true by default if not done) $ldapbind = TRUE; // - Authentified LDAP // (EP) ATTENTION: Ne pas faire die() ici car ça stopperait net la mauvaise connexion d'un utilisateur, avec ce message d'erreur ! // Il vaut mieux retourner FALSE et afficher un joli message de refus sur la page d'accueil if ($this->ldap_authentified) //$ldapbind = @ldap_bind($ldapConnection, $this->bindDn, $this->bindPass); $ldapbind = ldap_bind($ldapConnection, $this->bindDn, $this->bindPass); //or die("Could not bind to LDAP server.". ldap_error($ldapConnection) ); // - Anonymous LDAP // (EP) En cas de LDAP anonyme, binding uniquement si login session (pour vérifier le mot de passe de l'utilisateur). // Car sans cette ligne, on passe avec n'importe quel password !!! // NB: pas de die() ici, voir remarque juste au-dessus else if ($user_login && $user_password) //$ldapbind = @ldap_bind($ldapConnection, $this->authenticationType . '=' . $user_login . ',' . $this->baseDn, $user_password); $ldapbind = ldap_bind($ldapConnection, $this->authenticationType . '=' . $user_login . ',' . $this->baseDn, $user_password); // or die("Could not bind to LDAP server: ". ldap_error($ldapConnection) ); // (4) SEARCH if ($ldapbind) { // $filter = "(&".$this->filter."(".$this->authenticationType . '=' . $user_login."))"; // ex: (&(compteinfo=Oui)(uid=epallier)) $results = ldap_search($ldapConnection, $this->baseDn, $filter, $just_these) or die("Could not search to LDAP server response was: " . ldap_error($ldapConnection) ); $search = ldap_get_entries($ldapConnection, $results); //echo $results["count"]." entries returned\n"; if ($search === FALSE) die("Could not get user attributes from LDAP server, response was: " . ldap_error($ldapConnection) ); //return $search[0]; return $search; } } // Il y a eu un pb, utilisateur non reconnu return FALSE; } // searchLdap() // TODO: implement private function saveNewUserInDB($user_fetched) { // SAVE new user in DB return TRUE; } /* * @param string $user_login * @param string $user_password * @return logged user LDAP attributes or FALSE if user not found in LDAP */ public function ldapAuthentication($user_login, $user_password) { try { if ($this->checkConfiguration()) { // REAL LDAP if ($this->LDAP_USED) { // 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->checkAndFetchUserFromDB($user_login, $user_password); // 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->searchLdap($filter, $just_these, $user_login, $user_password); // CACHE the new user in DB for next time if ($user_fetched != FALSE) { $this->saveNewUserInDB($user_fetched[0]); return $user_fetched[0]; } } //return $user_fetched; // Noter que $user_fetched peut etre egal a FALSE (si pas trouvé) } // FAKE LDAP else { //debug($this->USE_LDAP); //debug($this->baseDn); $user = $this->getFakeLdapUser($user_login); // debug($user); if ($user === false) return FALSE; // $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; if ($user[$this->authenticationType][0] == $this->getTheFakeLdapUser()['login'] && $user['userpassword'][0] == $this->getTheFakeLdapUser()['pass']) return $user; if ( (new DefaultPasswordHasher())->check($user_password,$user['userpassword'][0]) ) return $user; // if ($user != false && $user['userpassword'][0] == $password) { } } } catch (Exception $e) { //echo 'Exception LDAP : ', $e->getMessage(), "\n"; } // Il y a eu un problème, l'utilisateur n'est pas reconnu return FALSE; } // end ldapAuthentication() } ?>