dolibarr  7.0.0-beta
ldap.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
4  * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@capnetworks.com>
5  * Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  * or see http://www.gnu.org/
20  */
21 
30 class Ldap
31 {
32  var $error;
33 
37  var $server=array();
41  var $dn;
49  var $domain;
63  var $people;
67  var $groups;
76 
77 
78  //Fetch user
79  var $name;
80  var $firstname;
81  var $login;
82  var $phone;
83  var $skype;
84  var $fax;
85  var $mail;
86  var $mobile;
87 
88  var $uacf;
89  var $pwdlastset;
90 
91  var $ldapcharset='UTF-8'; // LDAP should be UTF-8 encoded
92 
93 
101  var $result;
102 
103 
107  function __construct()
108  {
109  global $conf;
110 
111  // Server
112  if (! empty($conf->global->LDAP_SERVER_HOST)) $this->server[] = $conf->global->LDAP_SERVER_HOST;
113  if (! empty($conf->global->LDAP_SERVER_HOST_SLAVE)) $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE;
114  $this->serverPort = $conf->global->LDAP_SERVER_PORT;
115  $this->ldapProtocolVersion = $conf->global->LDAP_SERVER_PROTOCOLVERSION;
116  $this->dn = $conf->global->LDAP_SERVER_DN;
117  $this->serverType = $conf->global->LDAP_SERVER_TYPE;
118  $this->domain = $conf->global->LDAP_SERVER_DN;
119  $this->searchUser = $conf->global->LDAP_ADMIN_DN;
120  $this->searchPassword = $conf->global->LDAP_ADMIN_PASS;
121  $this->people = $conf->global->LDAP_USER_DN;
122  $this->groups = $conf->global->LDAP_GROUP_DN;
123 
124  $this->filter = $conf->global->LDAP_FILTER_CONNECTION; // Filter on user
125  $this->filtermember = $conf->global->LDAP_MEMBER_FILTER; // Filter on member
126 
127  // Users
128  $this->attr_login = $conf->global->LDAP_FIELD_LOGIN; //unix
129  $this->attr_sambalogin = $conf->global->LDAP_FIELD_LOGIN_SAMBA; //samba, activedirectory
130  $this->attr_name = $conf->global->LDAP_FIELD_NAME;
131  $this->attr_firstname = $conf->global->LDAP_FIELD_FIRSTNAME;
132  $this->attr_mail = $conf->global->LDAP_FIELD_MAIL;
133  $this->attr_phone = $conf->global->LDAP_FIELD_PHONE;
134  $this->attr_skype = $conf->global->LDAP_FIELD_SKYPE;
135  $this->attr_fax = $conf->global->LDAP_FIELD_FAX;
136  $this->attr_mobile = $conf->global->LDAP_FIELD_MOBILE;
137  }
138 
139 
140 
141  // Connection handling methods -------------------------------------------
142 
150  function connect_bind()
151  {
152  global $langs, $conf;
153 
154  $connected=0;
155  $this->bind=0;
156 
157  // Check parameters
158  if (count($this->server) == 0 || empty($this->server[0]))
159  {
160  $this->error='LDAP setup (file conf.php) is not complete';
161  dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
162  return -1;
163  }
164 
165  if (! function_exists("ldap_connect"))
166  {
167  $this->error='LDAPFunctionsNotAvailableOnPHP';
168  dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
169  $return=-1;
170  }
171 
172  if (empty($this->error))
173  {
174  // Loop on each ldap server
175  foreach ($this->server as $key => $host)
176  {
177  if ($connected) break;
178  if (empty($host)) continue;
179 
180  if (preg_match('/^ldap/',$host))
181  {
182  if ($this->serverPing($host) === true) {
183  $this->connection = ldap_connect($host);
184  }
185  else continue;
186  }
187  else
188  {
189  if ($this->serverPing($host, $this->serverPort) === true) {
190  $this->connection = ldap_connect($host,$this->serverPort);
191  }
192  else continue;
193  }
194 
195  if (is_resource($this->connection))
196  {
197  // Begin TLS if requested by the configuration
198  if (! empty($conf->global->LDAP_SERVER_USE_TLS))
199  {
200  if (! ldap_start_tls($this->connection))
201  {
202  dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING);
203  $connected = 0;
204  $this->close();
205  }
206  }
207 
208  // Execute the ldap_set_option here (after connect and before bind)
209  $this->setVersion();
210  ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true.
211 
212 
213  if ($this->serverType == "activedirectory")
214  {
215  $result=$this->setReferrals();
216  dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
217  $this->result=$this->bindauth($this->searchUser,$this->searchPassword);
218  if ($this->result)
219  {
220  $this->bind=$this->result;
221  $connected=2;
222  break;
223  }
224  else
225  {
226  $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
227  }
228  }
229  else
230  {
231  // Try in auth mode
232  if ($this->searchUser && $this->searchPassword)
233  {
234  dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
235  $this->result=$this->bindauth($this->searchUser,$this->searchPassword);
236  if ($this->result)
237  {
238  $this->bind=$this->result;
239  $connected=2;
240  break;
241  }
242  else
243  {
244  $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
245  }
246  }
247  // Try in anonymous
248  if (! $this->bind)
249  {
250  dol_syslog(get_class($this)."::connect_bind try bind on ".$host,LOG_DEBUG);
251  $result=$this->bind();
252  if ($result)
253  {
254  $this->bind=$this->result;
255  $connected=1;
256  break;
257  }
258  else
259  {
260  $this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
261  }
262  }
263  }
264  }
265 
266  if (! $connected) $this->close();
267  }
268  }
269 
270  if ($connected)
271  {
272  $return=$connected;
273  dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG);
274  }
275  else
276  {
277  $this->error='Failed to connect to LDAP'.($this->error?': '.$this->error:'');
278  $return=-1;
279  dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING);
280  }
281  return $return;
282  }
283 
284 
285 
292  function close()
293  {
294  if ($this->connection && ! @ldap_close($this->connection))
295  {
296  return false;
297  }
298  else
299  {
300  return true;
301  }
302  }
303 
310  function bind()
311  {
312  if (! $this->result=@ldap_bind($this->connection))
313  {
314  $this->ldapErrorCode = ldap_errno($this->connection);
315  $this->ldapErrorText = ldap_error($this->connection);
316  $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
317  return false;
318  }
319  else
320  {
321  return true;
322  }
323  }
324 
335  function bindauth($bindDn,$pass)
336  {
337  if (! $this->result = @ldap_bind($this->connection, $bindDn, $pass))
338  {
339  $this->ldapErrorCode = ldap_errno($this->connection);
340  $this->ldapErrorText = ldap_error($this->connection);
341  $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
342  return false;
343  }
344  else
345  {
346  return true;
347  }
348  }
349 
355  function unbind()
356  {
357  if (!$this->result=@ldap_unbind($this->connection))
358  {
359  return false;
360  } else {
361  return true;
362  }
363  }
364 
365 
371  function getVersion()
372  {
373  $version = 0;
374  $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
375  return $version;
376  }
377 
383  function setVersion() {
384  // LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17
385  $ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion);
386  return $ldapsetversion;
387  }
388 
394  function setReferrals() {
395  // LDAP_OPT_REFERRALS est une constante qui vaut ?
396  $ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
397  return $ldapreferrals;
398  }
399 
400 
410  function add($dn, $info, $user)
411  {
412  global $conf;
413 
414  dol_syslog(get_class($this)."::add dn=".$dn." info=".join(',',$info));
415 
416  // Check parameters
417  if (! $this->connection)
418  {
419  $this->error="NotConnected";
420  return -2;
421  }
422  if (! $this->bind)
423  {
424  $this->error="NotConnected";
425  return -3;
426  }
427 
428  // Encode to LDAP page code
429  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
430  foreach($info as $key => $val)
431  {
432  if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
433  }
434 
435  $this->dump($dn,$info);
436 
437  //print_r($info);
438  $result=@ldap_add($this->connection, $dn, $info);
439 
440  if ($result)
441  {
442  dol_syslog(get_class($this)."::add successfull", LOG_DEBUG);
443  return 1;
444  }
445  else
446  {
447  $this->ldapErrorCode = @ldap_errno($this->connection);
448  $this->ldapErrorText = @ldap_error($this->connection);
449  $this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
450  dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR);
451  return -1;
452  }
453  }
454 
464  function modify($dn, $info, $user)
465  {
466  global $conf;
467 
468  dol_syslog(get_class($this)."::modify dn=".$dn." info=".join(',',$info));
469 
470  // Check parameters
471  if (! $this->connection)
472  {
473  $this->error="NotConnected";
474  return -2;
475  }
476  if (! $this->bind)
477  {
478  $this->error="NotConnected";
479  return -3;
480  }
481 
482  // Encode to LDAP page code
483  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
484  foreach($info as $key => $val)
485  {
486  if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
487  }
488 
489  $this->dump($dn,$info);
490 
491  //print_r($info);
492  $result=@ldap_modify($this->connection, $dn, $info);
493 
494  if ($result)
495  {
496  dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG);
497  return 1;
498  }
499  else
500  {
501  $this->error=@ldap_error($this->connection);
502  dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR);
503  return -1;
504  }
505  }
506 
518  function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true)
519  {
520  global $conf;
521 
522  dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn?1:0));
523 
524  // Check parameters
525  if (! $this->connection)
526  {
527  $this->error="NotConnected";
528  return -2;
529  }
530  if (! $this->bind)
531  {
532  $this->error="NotConnected";
533  return -3;
534  }
535 
536  // Encode to LDAP page code
537  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
538  $newrdn=$this->convFromOutputCharset($newrdn,$this->ldapcharset);
539  $newparent=$this->convFromOutputCharset($newparent,$this->ldapcharset);
540 
541  //print_r($info);
542  $result=@ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
543 
544  if ($result)
545  {
546  dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG);
547  return 1;
548  }
549  else
550  {
551  $this->error=@ldap_error($this->connection);
552  dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR);
553  return -1;
554  }
555  }
556 
569  function update($dn, $info, $user, $olddn, $newrdn=false, $newparent=false)
570  {
571  global $conf;
572 
573  dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn);
574 
575  // Check parameters
576  if (! $this->connection)
577  {
578  $this->error="NotConnected";
579  return -2;
580  }
581  if (! $this->bind)
582  {
583  $this->error="NotConnected";
584  return -3;
585  }
586 
587  if (! $olddn || $olddn != $dn)
588  {
589  if (! empty($olddn) && ! empty($newrdn) && ! empty($newparent) && $conf->global->LDAP_SERVER_PROTOCOLVERSION === '3')
590  {
591  // This function currently only works with LDAPv3
592  $result = $this->rename($olddn, $newrdn, $newparent, $user, true);
593  }
594  else
595  {
596  // If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one.
597  $result = $this->add($dn, $info, $user);
598  if ($result > 0 && $olddn && $olddn != $dn) $result = $this->delete($olddn); // If add fails, we do not try to delete old one
599  }
600  }
601  else
602  {
603  //$result = $this->delete($olddn);
604  $result = $this->add($dn, $info, $user); // If record has been deleted from LDAP, we recreate it. We ignore error if it already exists.
605  $result = $this->modify($dn, $info, $user); // We use add/modify instead of delete/add when olddn is received
606  }
607  if ($result <= 0)
608  {
609  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection)." ".$this->error;
610  dol_syslog(get_class($this)."::update ".$this->error,LOG_ERR);
611  //print_r($info);
612  return -1;
613  }
614  else
615  {
616  dol_syslog(get_class($this)."::update done successfully");
617  return 1;
618  }
619  }
620 
621 
629  function delete($dn)
630  {
631  global $conf;
632 
633  dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn);
634 
635  // Check parameters
636  if (! $this->connection)
637  {
638  $this->error="NotConnected";
639  return -2;
640  }
641  if (! $this->bind)
642  {
643  $this->error="NotConnected";
644  return -3;
645  }
646 
647  // Encode to LDAP page code
648  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
649 
650  $result=@ldap_delete($this->connection, $dn);
651 
652  if ($result) return 1;
653  return -1;
654  }
655 
663  function dump_content($dn, $info)
664  {
665  $content='';
666 
667  // Create file content
668  if (preg_match('/^ldap/',$this->server[0]))
669  {
670  $target="-H ".join(',',$this->server);
671  }
672  else
673  {
674  $target="-h ".join(',',$this->server)." -p ".$this->serverPort;
675  }
676  $content.="# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
677  $content.="# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
678  $content.="# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
679  if (in_array('localhost',$this->server)) $content.="# If commands fails to connect, try without -h and -p\n";
680  $content.="dn: ".$dn."\n";
681  foreach($info as $key => $value)
682  {
683  if (! is_array($value))
684  {
685  $content.="$key: $value\n";
686  }
687  else
688  {
689  foreach($value as $valuekey => $valuevalue)
690  {
691  $content.="$key: $valuevalue\n";
692  }
693  }
694  }
695  return $content;
696  }
697 
705  function dump($dn, $info)
706  {
707  global $conf;
708 
709  // Create content
710  $content=$this->dump_content($dn, $info);
711 
712  //Create file
713  $result=dol_mkdir($conf->ldap->dir_temp);
714 
715  $outputfile=$conf->ldap->dir_temp.'/ldapinput.in';
716  $fp=fopen($outputfile,"w");
717  if ($fp)
718  {
719  fputs($fp, $content);
720  fclose($fp);
721  if (! empty($conf->global->MAIN_UMASK))
722  @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
723  return 1;
724  }
725  else
726  {
727  return -1;
728  }
729  }
730 
739  function serverPing($host, $port=389, $timeout=1)
740  {
741  $op = @fsockopen($host, $port, $errno, $errstr, $timeout);
742  if (!$op) return false; //DC is N/A
743  else {
744  fclose($op); //explicitly close open socket connection
745  return true; //DC is up & running, we can safely connect with ldap_connect
746  }
747  }
748 
749 
750  // Attribute methods -----------------------------------------------------
751 
761  function addAttribute($dn, $info, $user)
762  {
763  global $conf;
764 
765  dol_syslog(get_class($this)."::addAttribute dn=".$dn." info=".join(',',$info));
766 
767  // Check parameters
768  if (! $this->connection)
769  {
770  $this->error="NotConnected";
771  return -2;
772  }
773  if (! $this->bind)
774  {
775  $this->error="NotConnected";
776  return -3;
777  }
778 
779  // Encode to LDAP page code
780  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
781  foreach($info as $key => $val)
782  {
783  if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
784  }
785 
786  $this->dump($dn,$info);
787 
788  //print_r($info);
789  $result=@ldap_mod_add($this->connection, $dn, $info);
790 
791  if ($result)
792  {
793  dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG);
794  return 1;
795  }
796  else
797  {
798  $this->error=@ldap_error($this->connection);
799  dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR);
800  return -1;
801  }
802  }
803 
813  function updateAttribute($dn, $info, $user)
814  {
815  global $conf;
816 
817  dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',',$info));
818 
819  // Check parameters
820  if (! $this->connection)
821  {
822  $this->error="NotConnected";
823  return -2;
824  }
825  if (! $this->bind)
826  {
827  $this->error="NotConnected";
828  return -3;
829  }
830 
831  // Encode to LDAP page code
832  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
833  foreach($info as $key => $val)
834  {
835  if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
836  }
837 
838  $this->dump($dn,$info);
839 
840  //print_r($info);
841  $result=@ldap_mod_replace($this->connection, $dn, $info);
842 
843  if ($result)
844  {
845  dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG);
846  return 1;
847  }
848  else
849  {
850  $this->error=@ldap_error($this->connection);
851  dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR);
852  return -1;
853  }
854  }
855 
865  function deleteAttribute($dn, $info, $user)
866  {
867  global $conf;
868 
869  dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',',$info));
870 
871  // Check parameters
872  if (! $this->connection)
873  {
874  $this->error="NotConnected";
875  return -2;
876  }
877  if (! $this->bind)
878  {
879  $this->error="NotConnected";
880  return -3;
881  }
882 
883  // Encode to LDAP page code
884  $dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
885  foreach($info as $key => $val)
886  {
887  if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
888  }
889 
890  $this->dump($dn,$info);
891 
892  //print_r($info);
893  $result=@ldap_mod_del($this->connection, $dn, $info);
894 
895  if ($result)
896  {
897  dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG);
898  return 1;
899  }
900  else
901  {
902  $this->error=@ldap_error($this->connection);
903  dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR);
904  return -1;
905  }
906  }
907 
915  function getAttribute($dn,$filter)
916  {
917  // Check parameters
918  if (! $this->connection)
919  {
920  $this->error="NotConnected";
921  return -2;
922  }
923  if (! $this->bind)
924  {
925  $this->error="NotConnected";
926  return -3;
927  }
928 
929  $search = ldap_search($this->connection,$dn,$filter);
930 
931  // Only one entry should ever be returned
932  $entry = ldap_first_entry($this->connection, $search);
933 
934  if (!$entry)
935  {
936  $this->ldapErrorCode = -1;
937  $this->ldapErrorText = "Couldn't find entry";
938  return 0; // Couldn't find entry...
939  }
940 
941  // Get values
942  if (! $values = ldap_get_attributes($this->connection, $entry))
943  {
944  $this->ldapErrorCode = ldap_errno($this->connection);
945  $this->ldapErrorText = ldap_error($this->connection);
946  return 0; // No matching attributes
947  }
948 
949  // Return an array containing the attributes.
950  return $values;
951  }
952 
960  function getAttributeValues($filterrecord,$attribute)
961  {
962  $attributes=array();
963  $attributes[0] = $attribute;
964 
965  // We need to search for this user in order to get their entry.
966  $this->result = @ldap_search($this->connection,$this->people,$filterrecord,$attributes);
967 
968  // Pourquoi cette ligne ?
969  //$info = ldap_get_entries($this->connection, $this->result);
970 
971  // Only one entry should ever be returned (no user will have the same uid)
972  $entry = ldap_first_entry($this->connection, $this->result);
973 
974  if (!$entry)
975  {
976  $this->ldapErrorCode = -1;
977  $this->ldapErrorText = "Couldn't find user";
978  return false; // Couldn't find the user...
979  }
980 
981  // Get values
982  if (! $values = @ldap_get_values($this->connection, $entry, $attribute))
983  {
984  $this->ldapErrorCode = ldap_errno($this->connection);
985  $this->ldapErrorText = ldap_error($this->connection);
986  return false; // No matching attributes
987  }
988 
989  // Return an array containing the attributes.
990  return $values;
991  }
992 
1005  function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter=0, $attributeAsArray=array())
1006  {
1007  $fulllist=array();
1008 
1009  dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',',$attributeArray).") activefilter=".$activefilter);
1010 
1011  // if the directory is AD, then bind first with the search user first
1012  if ($this->serverType == "activedirectory")
1013  {
1014  $this->bindauth($this->searchUser, $this->searchPassword);
1015  dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser);
1016  }
1017 
1018  // Define filter
1019  if (! empty($activefilter))
1020  {
1021  if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter)
1022  {
1023  $filter = '('.$this->filter.')';
1024  }
1025  elseif (((string) $activefilter == 'member') && $this->filter)
1026  {
1027  $filter = '('.$this->filtermember.')';
1028  }
1029  else // If this->filter is empty, make fiter on * (all)
1030  {
1031  $filter = '('.$useridentifier.'=*)';
1032  }
1033  }
1034  else
1035  {
1036  $filter = '('.$useridentifier.'='.$search.')';
1037  }
1038 
1039  if (is_array($attributeArray))
1040  {
1041  // Return list with required fields
1042  $attributeArray=array_values($attributeArray); // This is to force to have index reordered from 0 (not make ldap_search fails)
1043  dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter. " attributeArray=(".join(',',$attributeArray).")");
1044  //var_dump($attributeArray);
1045  $this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray);
1046  }
1047  else
1048  {
1049  // Return list with fields selected by default
1050  dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter);
1051  $this->result = @ldap_search($this->connection, $userDn, $filter);
1052  }
1053  if (!$this->result)
1054  {
1055  $this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection);
1056  return -1;
1057  }
1058 
1059  $info = @ldap_get_entries($this->connection, $this->result);
1060 
1061  // Warning: Dans info, les noms d'attributs sont en minuscule meme si passe
1062  // a ldap_search en majuscule !!!
1063  //print_r($info);
1064 
1065  for ($i = 0; $i < $info["count"]; $i++)
1066  {
1067  $recordid=$this->convToOutputCharset($info[$i][$useridentifier][0],$this->ldapcharset);
1068  if ($recordid)
1069  {
1070  //print "Found record with key $useridentifier=".$recordid."<br>\n";
1071  $fulllist[$recordid][$useridentifier]=$recordid;
1072 
1073  // Add to the array for each attribute in my list
1074  $num = count($attributeArray);
1075  for ($j = 0; $j < $num; $j++)
1076  {
1077  $keyattributelower=strtolower($attributeArray[$j]);
1078  //print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n";
1079 
1080  //permet de recuperer le SID avec Active Directory
1081  if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid")
1082  {
1083  $objectsid = $this->getObjectSid($recordid);
1084  $fulllist[$recordid][$attributeArray[$j]] = $objectsid;
1085  }
1086  else
1087  {
1088  if(in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) {
1089  $valueTab = array();
1090  foreach($info[$i][$keyattributelower] as $key => $value) {
1091  $valueTab[$key] = $this->convToOutputCharset($value,$this->ldapcharset);
1092  }
1093  $fulllist[$recordid][$attributeArray[$j]] = $valueTab;
1094  } else {
1095  $fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0],$this->ldapcharset);
1096  }
1097  }
1098  }
1099  }
1100  }
1101 
1102  asort($fulllist);
1103  return $fulllist;
1104  }
1105 
1113  function littleEndian($hex)
1114  {
1115  for ($x=dol_strlen($hex)-2; $x >= 0; $x=$x-2) {
1116  $result .= substr($hex,$x,2);
1117  }
1118  return $result;
1119  }
1120 
1121 
1129  function getObjectSid($ldapUser)
1130  {
1131  $criteria = '('.$this->getUserIdentifier().'='.$ldapUser.')';
1132  $justthese = array("objectsid");
1133 
1134  // if the directory is AD, then bind first with the search user first
1135  if ($this->serverType == "activedirectory")
1136  {
1137  $this->bindauth($this->searchUser, $this->searchPassword);
1138  }
1139 
1140  $i = 0;
1141  $searchDN = $this->people;
1142 
1143  while ($i <= 2)
1144  {
1145  $ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese);
1146 
1147  if (!$ldapSearchResult)
1148  {
1149  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1150  return -1;
1151  }
1152 
1153  $entry = ldap_first_entry($this->connection, $ldapSearchResult);
1154 
1155  if (!$entry)
1156  {
1157  // Si pas de resultat on cherche dans le domaine
1158  $searchDN = $this->domain;
1159  $i++;
1160  }
1161  else
1162  {
1163  $i++;
1164  $i++;
1165  }
1166  }
1167 
1168  if ($entry)
1169  {
1170  $ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid");
1171  $SIDText = $this->binSIDtoText($ldapBinary[0]);
1172  return $SIDText;
1173  }
1174  else
1175  {
1176  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1177  return '?';
1178  }
1179  }
1180 
1188  function binSIDtoText($binsid)
1189  {
1190  $hex_sid=bin2hex($binsid);
1191  $rev = hexdec(substr($hex_sid,0,2)); // Get revision-part of SID
1192  $subcount = hexdec(substr($hex_sid,2,2)); // Get count of sub-auth entries
1193  $auth = hexdec(substr($hex_sid,4,12)); // SECURITY_NT_AUTHORITY
1194  $result = "$rev-$auth";
1195  for ($x=0;$x < $subcount; $x++)
1196  {
1197  $result .= "-".hexdec($this->littleEndian(substr($hex_sid,16+($x*8),8))); // get all SECURITY_NT_AUTHORITY
1198  }
1199  return $result;
1200  }
1201 
1202 
1214  function search($checkDn, $filter)
1215  {
1216  dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter);
1217 
1218  $checkDn=$this->convFromOutputCharset($checkDn,$this->ldapcharset);
1219  $filter=$this->convFromOutputCharset($filter,$this->ldapcharset);
1220 
1221  // if the directory is AD, then bind first with the search user first
1222  if ($this->serverType == "activedirectory") {
1223  $this->bindauth($this->searchUser, $this->searchPassword);
1224  }
1225 
1226  $this->result = @ldap_search($this->connection, $checkDn, $filter);
1227 
1228  $result = @ldap_get_entries($this->connection, $this->result);
1229  if (! $result)
1230  {
1231  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1232  return -1;
1233  }
1234  else
1235  {
1236  ldap_free_result($this->result);
1237  return $result;
1238  }
1239  }
1240 
1241 
1250  function fetch($user,$filter)
1251  {
1252  // Perform the search and get the entry handles
1253 
1254  // if the directory is AD, then bind first with the search user first
1255  if ($this->serverType == "activedirectory") {
1256  $this->bindauth($this->searchUser, $this->searchPassword);
1257  }
1258 
1259  $searchDN = $this->people; // TODO Why searching in people then domain ?
1260 
1261  $result = '';
1262  $i=0;
1263  while ($i <= 2)
1264  {
1265  dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter);
1266  $this->result = @ldap_search($this->connection, $searchDN, $filter);
1267  if ($this->result)
1268  {
1269  $result = @ldap_get_entries($this->connection, $this->result);
1270  if ($result['count'] > 0) dol_syslog('Ldap::fetch search found '.$result['count'].' records');
1271  else dol_syslog('Ldap::fetch search returns but found no records');
1272  //var_dump($result);exit;
1273  }
1274  else
1275  {
1276  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1277  dol_syslog(get_class($this)."::fetch search fails");
1278  return -1;
1279  }
1280 
1281  if (! $result)
1282  {
1283  // Si pas de resultat on cherche dans le domaine
1284  $searchDN = $this->domain;
1285  $i++;
1286  }
1287  else
1288  {
1289  break;
1290  }
1291  }
1292 
1293  if (! $result)
1294  {
1295  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1296  return -1;
1297  }
1298  else
1299  {
1300  $this->name = $this->convToOutputCharset($result[0][$this->attr_name][0],$this->ldapcharset);
1301  $this->firstname = $this->convToOutputCharset($result[0][$this->attr_firstname][0],$this->ldapcharset);
1302  $this->login = $this->convToOutputCharset($result[0][$this->attr_login][0],$this->ldapcharset);
1303  $this->phone = $this->convToOutputCharset($result[0][$this->attr_phone][0],$this->ldapcharset);
1304  $this->skype = $this->convToOutputCharset($result[0][$this->attr_skype][0],$this->ldapcharset);
1305  $this->fax = $this->convToOutputCharset($result[0][$this->attr_fax][0],$this->ldapcharset);
1306  $this->mail = $this->convToOutputCharset($result[0][$this->attr_mail][0],$this->ldapcharset);
1307  $this->mobile = $this->convToOutputCharset($result[0][$this->attr_mobile][0],$this->ldapcharset);
1308 
1309  $this->uacf = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0],$this->ldapcharset));
1310  if (isset($result[0]["pwdlastset"][0])) // If expiration on password exists
1311  {
1312  $this->pwdlastset = ($result[0]["pwdlastset"][0] != 0)?$this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0],$this->ldapcharset)):0;
1313  }
1314  else
1315  {
1316  $this->pwdlastset = -1;
1317  }
1318  if (!$this->name && !$this->login) $this->pwdlastset = -1;
1319  $this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0],$this->ldapcharset));
1320 
1321  // FQDN domain
1322  $domain = str_replace('dc=','',$this->domain);
1323  $domain = str_replace(',','.',$domain);
1324  $this->domainFQDN = $domain;
1325 
1326  // Set ldapUserDn (each user can have a different dn)
1327  //var_dump($result[0]);exit;
1328  $this->ldapUserDN=$result[0]['dn'];
1329 
1330  ldap_free_result($this->result);
1331  return 1;
1332  }
1333  }
1334 
1335 
1336  // helper methods
1337 
1344  {
1345  if ($this->serverType == "activedirectory") {
1346  return $this->attr_sambalogin;
1347  } else {
1348  return $this->attr_login;
1349  }
1350  }
1351 
1358  function parseUACF($uacf)
1359  {
1360  //All flags array
1361  $flags = array(
1362  "TRUSTED_TO_AUTH_FOR_DELEGATION" => 16777216,
1363  "PASSWORD_EXPIRED" => 8388608,
1364  "DONT_REQ_PREAUTH" => 4194304,
1365  "USE_DES_KEY_ONLY" => 2097152,
1366  "NOT_DELEGATED" => 1048576,
1367  "TRUSTED_FOR_DELEGATION" => 524288,
1368  "SMARTCARD_REQUIRED" => 262144,
1369  "MNS_LOGON_ACCOUNT" => 131072,
1370  "DONT_EXPIRE_PASSWORD" => 65536,
1371  "SERVER_TRUST_ACCOUNT" => 8192,
1372  "WORKSTATION_TRUST_ACCOUNT" => 4096,
1373  "INTERDOMAIN_TRUST_ACCOUNT" => 2048,
1374  "NORMAL_ACCOUNT" => 512,
1375  "TEMP_DUPLICATE_ACCOUNT" => 256,
1376  "ENCRYPTED_TEXT_PWD_ALLOWED" => 128,
1377  "PASSWD_CANT_CHANGE" => 64,
1378  "PASSWD_NOTREQD" => 32,
1379  "LOCKOUT" => 16,
1380  "HOMEDIR_REQUIRED" => 8,
1381  "ACCOUNTDISABLE" => 2,
1382  "SCRIPT" => 1
1383  );
1384 
1385  //Parse flags to text
1386  $retval = array();
1387  while (list($flag, $val) = each($flags)) {
1388  if ($uacf >= $val) {
1389  $uacf -= $val;
1390  $retval[$val] = $flag;
1391  }
1392  }
1393 
1394  //Return human friendly flags
1395  return($retval);
1396  }
1397 
1404  function parseSAT($samtype)
1405  {
1406  $stypes = array(
1407  805306368 => "NORMAL_ACCOUNT",
1408  805306369 => "WORKSTATION_TRUST",
1409  805306370 => "INTERDOMAIN_TRUST",
1410  268435456 => "SECURITY_GLOBAL_GROUP",
1411  268435457 => "DISTRIBUTION_GROUP",
1412  536870912 => "SECURITY_LOCAL_GROUP",
1413  536870913 => "DISTRIBUTION_LOCAL_GROUP"
1414  );
1415 
1416  $retval = "";
1417  while (list($sat, $val) = each($stypes)) {
1418  if ($samtype == $sat) {
1419  $retval = $val;
1420  break;
1421  }
1422  }
1423  if (empty($retval)) $retval = "UNKNOWN_TYPE_" . $samtype;
1424 
1425  return($retval);
1426  }
1427 
1434  function convert_time($value)
1435  {
1436  $dateLargeInt=$value; // nano secondes depuis 1601 !!!!
1437  $secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601
1438  $ADToUnixConvertor=((1970-1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes
1439  $unixTimeStamp=intval($secsAfterADEpoch-$ADToUnixConvertor); // Unix time stamp
1440  return $unixTimeStamp;
1441  }
1442 
1443 
1451  private function convToOutputCharset($str,$pagecodefrom='UTF-8')
1452  {
1453  global $conf;
1454  if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str=utf8_encode($str);
1455  if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str=utf8_decode($str);
1456  return $str;
1457  }
1458 
1466  function convFromOutputCharset($str,$pagecodeto='UTF-8')
1467  {
1468  global $conf;
1469  if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str=utf8_decode($str);
1470  if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str=utf8_encode($str);
1471  return $str;
1472  }
1473 
1474 
1481  function getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
1482  {
1483  global $conf;
1484 
1485  if (empty($keygroup)) $keygroup='LDAP_KEY_GROUPS';
1486 
1487  $search='('.$conf->global->$keygroup.'=*)';
1488  $result = $this->search($this->groups,$search);
1489  if ($result)
1490  {
1491  $c = $result['count'];
1492  $gids = array();
1493  for($i=0;$i<$c;$i++)
1494  {
1495  $gids[] = $result[$i]['gidnumber'][0];
1496  }
1497  rsort($gids);
1498 
1499  return $gids[0]+1;
1500  }
1501 
1502  return 0;
1503  }
1504 }
setReferrals()
changement du referrals.
Definition: ldap.class.php:394
deleteAttribute($dn, $info, $user)
Delete a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:865
update($dn, $info, $user, $olddn, $newrdn=false, $newparent=false)
Modify a LDAP entry (to use if dn != olddn) Ldap object connect and bind must have been done...
Definition: ldap.class.php:569
addAttribute($dn, $info, $user)
Add a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:761
binSIDtoText($binsid)
Returns the textual SID Indispensable pour Active Directory.
add($dn, $info, $user)
Add a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:410
modify($dn, $info, $user)
Modify a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:464
$domain
Version du protocole ldap.
Definition: ldap.class.php:49
getVersion()
Verification de la version du serveur ldap.
Definition: ldap.class.php:371
$ldapErrorCode
Code erreur retourne par le serveur Ldap.
Definition: ldap.class.php:71
getAttribute($dn, $filter)
Returns an array containing attributes and values for first record.
Definition: ldap.class.php:915
$searchUser
User administrateur Ldap Active Directory ne supporte pas les connexions anonymes.
Definition: ldap.class.php:54
$ldapErrorText
Message texte de l'erreur.
Definition: ldap.class.php:75
getAttributeValues($filterrecord, $attribute)
Returns an array containing values for an attribute and for first record matching filterrecord...
Definition: ldap.class.php:960
unbind()
Unbind du serveur ldap.
Definition: ldap.class.php:355
bind()
Anonymously binds to the connection.
Definition: ldap.class.php:310
parseUACF($uacf)
UserAccountControl Flgs to more human understandable form...
convToOutputCharset($str, $pagecodefrom='UTF-8')
Convert a string into output/memory charset.
convert_time($value)
Convertit le temps ActiveDirectory en Unix timestamp.
updateAttribute($dn, $info, $user)
Update a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:813
$people
DN des utilisateurs.
Definition: ldap.class.php:63
getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
Return available value of group GID.
$searchPassword
Mot de passe de l'administrateur Active Directory ne supporte pas les connexions anonymes.
Definition: ldap.class.php:59
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
$result
Result of any connections etc.
Definition: ldap.class.php:101
dump($dn, $info)
Dump a LDAP message to ldapinput.in file.
Definition: ldap.class.php:705
serverPing($host, $port=389, $timeout=1)
Ping a server before ldap_connect for avoid waiting.
Definition: ldap.class.php:739
dump_content($dn, $info)
Build a LDAP message.
Definition: ldap.class.php:663
bindauth($bindDn, $pass)
Binds as an authenticated user, which usually allows for write access.
Definition: ldap.class.php:335
$groups
DN des groupes.
Definition: ldap.class.php:67
convFromOutputCharset($str, $pagecodeto='UTF-8')
Convert a string from output/memory charset.
getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter=0, $attributeAsArray=array())
Returns an array containing a details or list of LDAP record(s) ldapsearch -LLLx -hlocalhost -Dcn=adm...
parseSAT($samtype)
SamAccountType value to text.
$dn
Base DN (e.g.
Definition: ldap.class.php:41
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:104
__construct()
Constructor.
Definition: ldap.class.php:107
search($checkDn, $filter)
Fonction de recherche avec filtre this->connection doit etre defini donc la methode bind ou bindauth ...
littleEndian($hex)
Converts a little-endian hex-number to one, that 'hexdec' can convert Required by Active Directory...
Class to manage LDAP features.
Definition: ldap.class.php:30
dol_mkdir($dir, $dataroot='', $newmask=null)
Creation of a directory (this can create recursive subdir)
close()
Simply closes the connection set up earlier.
Definition: ldap.class.php:292
rename($dn, $newrdn, $newparent, $user, $deleteoldrdn=true)
Rename a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:518
fetch($user, $filter)
Load all attribute of a LDAP user.
setVersion()
Change ldap protocol version to use.
Definition: ldap.class.php:383
getObjectSid($ldapUser)
Recupere le SID de l'utilisateur Required by Active Directory.
$server
Tableau des serveurs (IP addresses ou nom d'hotes)
Definition: ldap.class.php:37
getUserIdentifier()
Returns the correct user identifier to use, based on the ldap server type.
$connection
The internal LDAP connection handle.
Definition: ldap.class.php:97
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
$serverType
type de serveur, actuellement OpenLdap et Active Directory
Definition: ldap.class.php:45
connect_bind()
Connect and bind Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType, this->searchUser, this->searchPassword After return, this->connection and $this->bind are defined.
Definition: ldap.class.php:150