Commit c062ad0cf77068e55a07324466a555a292eff4d2

Authored by Etienne Pallier
1 parent be8d1531
Exists in master and in 2 other branches dev, dev-IRAP

vers un nouveau ldap tout beau... (espérons)

Showing 2 changed files with 302 additions and 48 deletions   Show diff stats
README.md
... ... @@ -54,11 +54,9 @@ Logiciel testé et validé sur les configurations suivantes :
54 54 VERSION ACTUELLE
55 55  
56 56 Date: 14/02/2019
57   -Version: 2.10.13.bugfix1
  57 +Version: 2.10.14
58 58 Author: EP
59   - (IRAP ONLY) Bugfix Ajout/Edit materiel
60   -
61   - LDAP refactorisation && optimisation (1)
  59 + (IRAP ONLY) LDAP refactorisation && optimisation (1)
62 60  
63 61 IMPORTANT:
64 62 - Pour connaitre la version actuelle, taper "./VERSION"
... ...
src/Model/Table/LdapConnectionsTable.php
... ... @@ -100,13 +100,14 @@ class LdapConnectionsTable extends AppTable
100 100 }
101 101 }
102 102  
103   - // EP (aout 2017)
104   - // ATTENTION : Utilisateur IMPORTANT.
105   - // Avec cet utilisateur, on simule un utilisateur qui n'est PAS dans la table utilisateurs
106   - // Il devrait donc se voir attribuer un role "Utilisateur" sans pour autant que ça soit écrit dans la table !!!
107   - // login = '_NouvelUtilisateur_username'
108   - // pass = '_NouvelUtilisateur_password'
109   - // $prefix = "_NouvelUtilisateur_";
  103 + /* EP (aout 2017)
  104 + * ATTENTION : Utilisateur IMPORTANT.
  105 + * Avec cet utilisateur, on simule un utilisateur qui n'est PAS dans la table utilisateurs
  106 + * Il devrait donc se voir attribuer un role "Utilisateur" sans pour autant que ça soit écrit dans la table !!!
  107 + * login = '_NouvelUtilisateur_username'
  108 + * pass = '_NouvelUtilisateur_password'
  109 + * $prefix = "_NouvelUtilisateur_";
  110 + */
110 111 $ldapUsers[] = [
111 112 'sn' => [
112 113 'UTILISATEUR'
... ... @@ -134,15 +135,13 @@ class LdapConnectionsTable extends AppTable
134 135  
135 136 private function checkConfiguration()
136 137 {
137   - $config = TableRegistry::get('Configurations')->find()
138   - ->where([
139   - 'id =' => 1
140   - ])
  138 + $config = TableRegistry::getTableLocator()->get('Configurations')
  139 + ->find()
  140 + ->where(['id =' => 1])
141 141 ->first();
  142 +
142 143  
143   - //$this->LDAP_USED = $config->LDAP_USED ? TRUE : FALSE;
144 144 $this->LDAP_USED = $config->ldap_used;
145   -
146 145 if (! $this->LDAP_USED) {
147 146 //$this->authenticationType = $config->authentificationType_ldap;
148 147 $this->authenticationType = $config->ldap_authenticationType;
... ... @@ -155,14 +154,6 @@ class LdapConnectionsTable extends AppTable
155 154 $ldapConfig = $config->toArray();
156 155  
157 156 if (! empty($config->ldap_host) && ! empty($config->ldap_port) && ! empty($config->ldap_baseDn) && ! empty($config->ldap_authenticationType) && ! empty($config->ldap_filter)) {
158   - //if (! empty($config->host_ldap) && ! empty($config->port_ldap) && ! empty($config->baseDn_ldap) && ! empty($config->authentificationType_ldap) && ! empty($config->filter_ldap)) {
159   - /*
160   - $this->host = $config->host_ldap;
161   - $this->port = $config->port_ldap;
162   - $this->baseDn = $config->baseDn_ldap;
163   - $this->filter = $config->filter_ldap;
164   - $this->authenticationType = $config->authentificationType_ldap;
165   - */
166 157 $this->host = $config->ldap_host;
167 158 $this->port = $config->ldap_port;
168 159 $this->baseDn = $config->ldap_baseDn;
... ... @@ -184,13 +175,52 @@ class LdapConnectionsTable extends AppTable
184 175 <li>authenticationType = ' . @$ldapConfig['authenticationType'] . '</li>
185 176 </ul>');
186 177 }
187   -
  178 +
  179 +
  180 +
  181 + // REAL LDAP only
  182 + /**
  183 + * @return array or boolean : all users from DB (CACHE of the LDAP) or FALSE (if table emtpy or expired data)
  184 + */
  185 + private function fetchAllUsersFromDB() {
  186 + // On remet à jour tous les 7 jours
  187 + $PEREMPTION_NB_DAYS = 7;
  188 +
  189 + // Doit aussi retourner FALSE si la ligne FAKE de la table users
  190 + // (celle qui contient le user_name "FAKE_USER")
  191 + // a une date "updated" périmée (now - updated > $PEREMPTION_NB_DAYS)
  192 + // (update automatique de tous les users, chaque semaine, pour rester synced avec le LDAP)
  193 + // By default, no user in CACHE
  194 + return FALSE;
  195 + }
  196 +
  197 + // REAL LDAP only
  198 + // Sauvegarde de tous les users du LDAP en BD (avec un rythme de mise à jour hebdo)
  199 + // Seulement les champs: nom, pnom, login, pass, email, create, updated, profile
  200 + private function saveAllUsersInDB($users_fetched) {
  201 +
  202 + // START TRANSACTION
  203 + // 1) Update (ou création) de la ligne FAKE (contient le user_name "FAKE_USER") => avec une date "updated"
  204 + // 2) Update (ou création) de chaque user contenu dans $users_fetched
  205 + // Attention à ne pas perdre l'attribut "profile", surtout pour les users privilégiés!!! (les autres ont un profile = "Utilisateur")
  206 + // END TRANSACTION (COMMIT)
  207 +
  208 + // SAVE s'est bien passé
  209 + return TRUE;
  210 + }
  211 +
  212 + /**
  213 + * @return $users_fetched or FALSE
  214 + */
  215 + // REAL or FAKE LDAP
188 216 public function getAllLdapUsers()
189 217 {
190 218 try {
191 219 if ($this->checkConfiguration()) {
  220 +
192 221 // REAL LDAP
193 222 if ($this->LDAP_USED) {
  223 +
194 224 $ldapConnection = ldap_connect($this->host, $this->port);
195 225 ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
196 226  
... ... @@ -206,7 +236,10 @@ class LdapConnectionsTable extends AppTable
206 236 } catch (Exception $e) {}
207 237 return false;
208 238 }
  239 +
  240 +
209 241  
  242 + // TODO: avirer, VIEUX CODE, à remplacer par getLdap1UserOrAllUsersAttributes()
210 243 // $userName = login
211 244 public function getUserAttributes($userName)
212 245 {
... ... @@ -227,7 +260,77 @@ class LdapConnectionsTable extends AppTable
227 260  
228 261 return false;
229 262 }
230   -
  263 +
  264 +
  265 +
  266 +
  267 + // REAL LDAP only
  268 + /*
  269 + * @param string $ldapConnection
  270 + * @param string $filter
  271 + * @param array $just_these
  272 + * @param string $userName (= login) => for FAKE LDAP only
  273 + * @return $res = ldap search result (1 user or all users attributes) or FALSE
  274 + */
  275 + //public function getUserAttributes($userName, $ldapConnection='', $filter='', $just_these=[])
  276 + //public function getUserAttributes($ldapConnection='', $filter='', $just_these=[], $userName=NULL)
  277 + //public function getLdapUsersAttributes($ldapConnection, $filter='', $just_these=[])
  278 + public function getLdap1UserOrAllUsersAttributes($ldapConnection, $filter='', $just_these=[])
  279 + {
  280 + try {
  281 + if ($this->checkConfiguration()) {
  282 +
  283 + // LDAP mode
  284 + //if ($this->LDAP_USED) {
  285 + /* (EP)
  286 + Fonction ldap_search ($link_identifier, $base_dn, $filter, array $attributes = null, $attrsonly = null, $sizelimit = null, $timelimit = null, $deref = null)
  287 + Concernant le paramètre $attributes (ici, $just_these) :
  288 + - An array of the required attributes, e.g. array("mail", "sn", "cn").
  289 + - Note that the "dn" is always returned irrespective of which attributes types are requested.
  290 + Telle que notre connexion au LDAP est conçue, on s'attend à recevoir AU MINIMUM
  291 + ces attributs dans la réponse de la fonction ldap_search():
  292 + - 'sn'
  293 + - 'mail'
  294 + - 'givenname'
  295 + - 'uid'
  296 + - 'userpassword'
  297 + Pour récupérer tous ces attributs, il ne faut pas utiliser la variable $just_these,
  298 + ou alors il faut qu'elle soit égale à un tableau vide ([]).
  299 + (par exemple, si elle vaut "['cn']" ça signifie qu'on veut "seulement l'attribut 'cn'")
  300 + Quand on n'utilise pas $just_these, la fonction ldap_search() retourne TOUS les attributs disponibles,
  301 + donc c'est le comportement qu'on veut ici.
  302 + */
  303 +
  304 + //$ldapConnection = ldap_connect($this->host, $this->port);
  305 + //ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
  306 + // OLD: $results = ldap_search($ldapConnection, $this->baseDn, '(' . $this->authenticationType . '=' . $userName . ')');
  307 + // NEW: $filter = "(&".$this->filter."(".$this->authenticationType . '=' . $user_login."))";
  308 +
  309 + // OLD à virer
  310 + $filter = '(' . $this->authenticationType . '=' . $userName . ')';
  311 + $just_these = [];
  312 +
  313 + $results = ldap_search($ldapConnection, $this->baseDn, $filter, $just_these)
  314 + or die("Could not search to LDAP server response was: " . ldap_error($ldapConnection) );
  315 + $res = ldap_get_entries($ldapConnection, $results);
  316 + //echo $info["count"]." entries returned\n";
  317 + return $res;
  318 + //}
  319 +
  320 + // FAKE LDAP mode
  321 + //else return array( $this->getFakeLdapUser($userName) );
  322 + //else return $this->fakeLDAPUsers;
  323 +
  324 + }
  325 + } catch (Exception $e) {
  326 + echo 'Exception LDAP : ', $e->getMessage(), "\n";
  327 + }
  328 +
  329 + return false;
  330 + }
  331 +
  332 +
  333 +
231 334 public function getAuthenticationType()
232 335 {
233 336 return $this->authenticationType;
... ... @@ -269,22 +372,27 @@ class LdapConnectionsTable extends AppTable
269 372 */
270 373 public function getListUsers()
271 374 {
272   - $u = $this->getAllLdapUsers();
273 375 $utilisateurs = [];
274 376  
275   - if ($this->LDAP_USED) {
276   - for ($i = 0; $i < $u['count']; $i ++) {
277   - $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0];
278   - }
279   - } else {
280   - for ($i = 0; $i < sizeof($u) - 1; $i ++) {
281   - $utilisateurs[$u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0]] = $u[$i]['sn'][0] . ' ' . $u[$i]['givenname'][0];
282   - }
283   - }
  377 + // Get all users (with ALL their attributes)
  378 + $u = $this->getAllLdapUsers();
  379 + // Sort users
  380 + //sort($u);
  381 + //debug($u);
284 382  
285   - return $utilisateurs;
  383 + // (EP) Refactorisation pour éviter code redondant ci-dessous, c'était pourtant pas compliqué, poil dans la main...
  384 + $nb_users = $this->LDAP_USED ? $u['count'] : sizeof($u)-1;
  385 + // $utilisateurs["Pallier Etienne"] = "Pallier Etienne"
  386 + for ($i = 0; $i < $nb_users; $i ++)
  387 + $utilisateurs[ $u[$i]['sn'][0].' '.$u[$i]['givenname'][0] ] = $u[$i]['sn'][0].' '.$u[$i]['givenname'][0];
  388 + //debug($utilisateurs);
  389 + // Sort users (without modifying the keys, don't use sort() but asort() !!!!!!!!!!!!!)
  390 + ksort($utilisateurs);
  391 + //debug($utilisateurs);
  392 + return $utilisateurs;
286 393 }
287   -
  394 +
  395 +
288 396 /**
289 397 * Return a list of login ofUsers with key = username & value = login
290 398 */
... ... @@ -355,26 +463,167 @@ class LdapConnectionsTable extends AppTable
355 463 'pass' => '_fake_ldap_user_pass'
356 464 ];
357 465 }
  466 +
  467 +
358 468  
359   - public function ldapAuthentication($login, $password)
360   - {
  469 + // TODO: implement
  470 + // REAL LDAP only
  471 + private function checkAndFetchUserFromDB($user_login, $user_password) {
  472 + // Doit aussi return false si ce user_login est "périmé" (sa date "created" est > 2 mois par exemple),
  473 + // ce qui obligera à relire ses données dans le LDAP et donc se mettre à jour
  474 + // By default, user is not in DB
  475 + return FALSE;
  476 + }
  477 +
  478 +
  479 + // REAL LDAP only
  480 + private function searchLdap($filter, $just_these, $user_login=NULL, $user_password=NULL) {
  481 +
  482 + // CONNEXION
  483 + $ldapConnection = ldap_connect($this->host, $this->port)
  484 + or die("Could not connect to $this->host (port $this->port)");
  485 +
  486 + if ($ldapConnection) {
  487 +
  488 + // OPTIONS
  489 + ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
  490 +
  491 + // BINDING
  492 +
  493 + // - Authentified
  494 + if ($this->ldap_authentified)
  495 + $ldapbind = ldap_bind($ldapConnection, $this->bindDn, $this->bindPass);
  496 + // or die("Could not bind to LDAP server.". ldap_error($ldapConnection) );
  497 +
  498 + // - Anonymous
  499 + // En cas de LDAP anonyme, binding quand même pour vérifier le mot de passe de l'utilisateur.
  500 + // Sans cette ligne, on passe avec n'importe quel password !!!
  501 + else {
  502 + $ldapbind = TRUE;
  503 + // function ldap_bind ($link_identifier, $bind_rdn = null, $bind_password = null)
  504 + //debug("log, pass= " . $user_login . ' ' . $user_password);
  505 + //if ($user_login && $user_password) $ldapbind = ldap_bind($ldapConnection, $this->authenticationType.'='.$user_login, $user_password);
  506 + if ($user_login && $user_password)
  507 + $ldapbind = ldap_bind($ldapConnection, $this->authenticationType . '=' . $user_login . ',' . $this->baseDn, $user_password);
  508 + //debug("ldapbind " . $ldapbind);
  509 + }
  510 +
  511 + // SEARCH
  512 + if ($ldapbind) {
  513 + //$search = $this->getLdapUserAttributes($ldapConnection, $filter, $just_these, $user_login);
  514 + $search = $this->getLdap1UserOrAllUsersAttributes($ldapConnection, $filter, $just_these);
  515 + if ($search === FALSE) die("Could not get user attributes from LDAP server, response was: " . ldap_error($ldapConnection) );
  516 + //return $search[0];
  517 + return $search;
  518 + }
  519 +
  520 + }
  521 +
  522 + // Il y a eu un pb, utilisateur non reconnu
  523 + return FALSE;
  524 +
  525 + }
  526 +
  527 +
  528 +
  529 +
  530 + // REAL LDAP only
  531 + private function checkAndFetchUserFromLdap($user_login, $user_password) {
  532 + // Set LDAP parameters
  533 + // - Liste des attributs à récupérer dans le ldap (vide = TOUS les attributs)
  534 + $just_these = [];
  535 + // TODO: vérifier si cette ligne est bien utile ou pas... (avant on faisait ça)
  536 + if (! $this->ldap_authentified) $just_these = array("cn");
  537 +
  538 + /* Examples :
  539 + *
  540 + * - ANONYMOUS LDAP (IRAP) :
  541 + * $this->authenticationType = 'uid'
  542 + * $this->baseDn = "ou=users,dc=irap,dc=omp,dc=eu"
  543 + *
  544 + * - AUTHENTIFIED LDAP connection (CRAL) :
  545 + * $this->authenticationType = 'sAMAccountName'
  546 + * $this->baseDn = "dc=univ-lyon1,dc=fr"
  547 + * $binddn="CN=svc_ldap_cral,OU=users,OU=27,OU=sim,OU=univ-lyon1,DC=univ-lyon1,DC=fr";
  548 + * ($binddn = "CN=svc_ldap_cral,OU=users,OU=27,OU=sim,OU=univ-lyon1,".$this->baseDn;)
  549 + * $filter = "(&(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))";
  550 + */
  551 + // Construction du filtre avec le filtre de la base de données avec un & sur le login de l'utilisateur
  552 + // Si aucun filtre n'est défini dans la base de données on aura juste (& ($this->authenticationType=$user_login))
  553 + // 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))";
  554 + $filter = "(&".$this->filter."(".$this->authenticationType . '=' . $user_login."))";
  555 +
  556 + //TODO: optimisation, refactoriser si comportement général
  557 + //$binddn .= ','.$this->baseDn;
  558 +
  559 + $res = $this->searchLdap($filter, $just_these, $user_login, $user_password);
  560 + if ($res != FALSE) return $res[0];
  561 +
  562 + /*
  563 + // CONNEXION
  564 + $ldapConnection = ldap_connect($this->host, $this->port)
  565 + or die("Could not connect to $this->host (port $this->port)");
  566 +
  567 + if ($ldapConnection) {
  568 +
  569 + // OPTIONS
  570 + ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
  571 +
  572 + // BINDING
  573 + if ($this->ldap_authentified)
  574 + $ldapbind = ldap_bind($ldapConnection, $this->bindDn, $this->bindPass); // or die("Could not bind to LDAP server.". ldap_error($ldapConnection) );
  575 + // En cas de LDAP anonyme, binding quand même pour vérifier le mot de passe de l'utilisateur.
  576 + // Sans cette ligne, on passe avec n'importe quel password !!!
  577 + else
  578 + $ldapbind = ldap_bind($ldapConnection, $this->authenticationType.'='.$user_login, $user_password);
  579 +
  580 + // SEARCH
  581 + if ($ldapbind) {
  582 + $search = $this->getUserAttributes($user_login, $ldapConnection, $filter, $just_these);
  583 + if ($search === false) die("Could not get user attributes from LDAP server, response was: " . ldap_error($ldapConnection) );
  584 + return $search[0];
  585 + }
  586 +
  587 + }
  588 + */
  589 +
  590 + // Il y a eu un pb, utilisateur non reconnu
  591 + return FALSE;
  592 + }
  593 +
  594 +
  595 + // TODO: implement
  596 + private function saveNewUserInDB($user_fetched) {
  597 + // SAVE new user in DB
  598 + return TRUE;
  599 + }
  600 +
  601 +
  602 +
  603 +
  604 + /*
  605 + * @param string $user_login
  606 + * @param string $user_password
  607 + * @return logged user LDAP attributes or FALSE if user not found in LDAP
  608 + */
  609 + public function ldapAuthentication($user_login, $user_password) {
361 610 try {
362 611 if ($this->checkConfiguration()) {
363 612  
364 613 if ($this->LDAP_USED) {
365   - if (strlen(trim($password)) == 0)
  614 + if (strlen(trim($user_password)) == 0)
366 615 return FALSE;
367 616 $ldapConnection = ldap_connect($this->host, $this->port);
368 617 ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
369   - if (@ldap_bind($ldapConnection, $this->authenticationType . '=' . $login . ',' . $this->baseDn, $password)) {
370   - return $this->getUserAttributes($login)[0];
  618 + if (@ldap_bind($ldapConnection, $this->authenticationType . '=' . $user_login . ',' . $this->baseDn, $user_password)) {
  619 + return $this->getUserAttributes($user_login)[0];
371 620 /*
372 621 * } else {
373 622 * return false;
374 623 */
375 624 }
376 625 } else {
377   - $user = $this->getFakeLdapUser($login);
  626 + $user = $this->getFakeLdapUser($user_login);
378 627 // debug($user);
379 628 if ($user === false)
380 629 return FALSE;
... ... @@ -383,13 +632,20 @@ class LdapConnectionsTable extends AppTable
383 632 // if ($user[$this->authenticationType][0] == "_NouvelUtilisateur_username" && $user['userpassword'][0] == "_NouvelUtilisateur_password") return $user;
384 633 if ($user[$this->authenticationType][0] == $this->getTheFakeLdapUser()['login'] && $user['userpassword'][0] == $this->getTheFakeLdapUser()['pass'])
385 634 return $user;
386   - if ((new DefaultPasswordHasher())->check($password, $user['userpassword'][0]))
  635 + if ((new DefaultPasswordHasher())->check($user_password, $user['userpassword'][0]))
387 636 return $user;
388 637 // if ($user != false && $user['userpassword'][0] == $password) {
389 638 }
390 639 }
391   - } catch (Exception $e) {}
  640 + } catch (Exception $e) {
  641 + //echo 'Exception LDAP : ', $e->getMessage(), "\n";
  642 + }
  643 +
  644 + // Il y a eu un problème, l'utilisateur n'est pas reconnu
392 645 return FALSE;
393   - }
  646 + } // end ldapAuthentication()
  647 +
  648 +
  649 +
394 650 }
395 651 ?>
396 652 \ No newline at end of file
... ...