fe47383de1b6d4439c4f75f279643ec5f111972d
[ldapsaisie.git] / trunk / includes / class / class.LSldap.php
1 <?php
2 /*******************************************************************************
3  * Copyright (C) 2007 Easter-eggs
4  * http://ldapsaisie.labs.libre-entreprise.org
5  *
6  * Author: See AUTHORS file in top-level directory.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation.
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, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 ******************************************************************************/
22
23 /**
24  * Gestion de l'accès à l'annaire Ldap
25  *
26  * Cette classe gère l'accès à l'annuaire ldap en s'appuyant sur PEAR :: Net_LDAP2
27  *
28  * @author Benjamin Renard <brenard@easter-eggs.com>
29  */
30 class LSldap {
31
32   var $config;
33   var $cnx = NULL;
34
35   /**
36    * Constructeur
37    *
38    * Cette methode définis la configuration de l'accès à l'annuaire
39    * et établie la connexion.
40    *
41    * @author Benjamin Renard <brenard@easter-eggs.com>
42    *
43    * @param[in] $config array Tableau de configuration au formar Net_LDAP2
44    *
45    * @retval void
46    *
47    * @see Net_LDAP2::connect()
48    */
49   function LSldap ($config) {
50     $this -> config = $config;
51     $this -> connect();
52   }
53   
54   /**
55    * Connection
56    *
57    * Cette methode établie la connexion à l'annuaire Ldap
58    *
59    * @author Benjamin Renard <brenard@easter-eggs.com>
60    *
61    * @retval boolean true si la connection est établie, false sinon
62    */
63   function connect() {
64     $this -> cnx = Net_LDAP2::connect($this -> config);
65     if (Net_LDAP2::isError($this -> cnx)) {
66       LSerror :: addErrorCode('LSldap_01',$this -> cnx -> getMessage());
67       $this -> cnx = NULL;
68       return;
69     }
70     return true;
71   }
72   
73   /**
74    * Déconnection
75    *
76    * Cette methode clos la connexion à l'annuaire Ldap
77    *
78    * @author Benjamin Renard <brenard@easter-eggs.com>
79    *
80    * @retval void
81    */
82   function close() {
83     $this -> cnx -> done();
84   }
85   
86   /**
87    * Recherche dans l'annuaire
88    *
89    * Cette methode effectue une recherche dans l'annuaire et retourne le resultat
90    * de celle-ci sous la forme d'un tableau.
91    *
92    * @author Benjamin Renard <brenard@easter-eggs.com>
93    *
94    * @param[in] $filter [<b>required</b>] string Filtre de recherche Ldap
95    * @param[in] $basedn string DN de base pour la recherche
96    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
97    *
98    * @see Net_LDAP2::search()
99    *
100    * @retval array Retourne un tableau associatif contenant :
101    *               - ['dn'] : le DN de l'entré
102    *               - ['attrs'] : tableau associatif contenant les attributs (clé)
103    *                             et leur valeur (valeur).
104    */
105   function search ($filter,$basedn=NULL,$params = array()) {
106     $ret = $this -> cnx -> search($basedn,$filter,$params);
107     if (Net_LDAP2::isError($ret)) {
108       LSerror :: addErrorCode('LSldap_02',$ret -> getMessage());
109       return;
110     }
111     $retInfos=array();
112     foreach($ret -> entries() as $entry) {
113       $retInfos[]=array('dn' => $entry -> dn(), 'attrs' => $entry -> getValues());
114     }
115     return $retInfos;
116   }
117   
118   /**
119    * Compte le nombre de retour d'une recherche dans l'annuaire
120    *
121    * Cette methode effectue une recherche dans l'annuaire et retourne le nombre
122    * d'entrés trouvées.
123    *
124    * @author Benjamin Renard <brenard@easter-eggs.com>
125    *
126    * @param[in] $filter [<b>required</b>] string Filtre de recherche Ldap
127    * @param[in] $basedn string DN de base pour la recherche
128    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
129    *
130    * @see Net_LDAP2::search()
131    *
132    * @retval numeric Le nombre d'entré trouvées
133    */
134   function getNumberResult ($filter,$basedn=NULL,$params = array() ) {
135     if (empty($filter))
136       $filter=NULL;
137     $ret = $this -> cnx -> search($basedn,$filter,$params);
138     if (Net_LDAP2::isError($ret)) {
139       LSerror :: addErrorCode('LSldap_02',$ret -> getMessage());
140       return;
141     }
142     return $ret -> count();
143   }
144   
145   /**
146    * Charge les valeurs des attributs d'une entré de l'annuaire
147    *
148    * Cette methode recupère les valeurs des attributs d'une entrée de l'annaire
149    * et les retournes sous la forme d'un tableau.
150    *
151    * @author Benjamin Renard <brenard@easter-eggs.com>
152    *
153    * @param[in] $dn string DN de l'entré Ldap
154    *
155    * @retval array Tableau associatif des valeurs des attributs avec en clef, le nom de l'attribut.
156    */
157   function getAttrs($dn) {
158     $infos = ldap_explode_dn($dn,0);
159     if((!$infos)||($infos['count']==0))
160       return;
161     $basedn='';
162     for ($i=1;$i<$infos['count'];$i++) {
163       $sep=($basedn=='')?'':',';
164       $basedn.=$sep.$infos[$i];
165     }
166     $return=$this -> search($infos[0],$basedn);
167     return $return[0]['attrs'];
168   }
169   
170   /**
171    * Retourne une entrée existante ou nouvelle
172    *
173    * @author Benjamin Renard <brenard@easter-eggs.com>
174    *
175    * @param[in] $object_type string Type de l'objet Ldap
176    * @param[in] $dn string DN de l'entré Ldap
177    *
178    * @retval ldapentry|array Un objet ldapentry (PEAR::Net_LDAP2)
179    *                         ou un tableau (si c'est une nouvelle entrée):
180    *                          Array (
181    *                            'entry' => ldapentry,
182    *                            'new' => true
183    *                          )
184    */
185   function getEntry($object_type,$dn) {
186     if(isset($GLOBALS['LSobjects'][$object_type])){
187       $obj_conf=$GLOBALS['LSobjects'][$object_type];
188       $entry = $this -> cnx -> getEntry($dn);
189       if (Net_LDAP2::isError($entry)) {
190         //$newentry = new Net_LDAP2_Entry(&$this -> cnx);
191         //$newentry -> dn($dn);
192         //$newentry -> add(array('objectclass' => $obj_conf['objectclass']));
193         //foreach($obj_conf['attrs'] as $attr_name => $attr_conf) {
194         //  $newentry->add(array($attr_name => $attr_conf['default_value']));
195         //}
196         $attributes = array();
197         foreach($obj_conf['attrs'] as $attr_name => $attr_conf) {
198           if( isset($attr_conf['default_value']) ) {
199             $attributes[$attr_name]=$attr_conf['default_value'];
200           }
201         }
202         
203         $newentry = $this -> getNewEntry($dn,$obj_conf['objectclass'],$attributes);
204         
205         if (!$newentry) {
206           return;
207         }
208         return array('entry' => $newentry,'new' => true);
209       }
210       else {
211         return $entry;
212       }
213     }
214     else {
215       LSerror :: addErrorCode('LSldap_03');
216       return;
217     }
218   }
219   
220  /**
221   * Retourne une nouvelle entrée
222   * 
223   * @param[in] $dn string Le DN de l'objet
224   * @param[in] $objectClass array Un tableau contenant les objectClass de l'objet
225   * @param[in] $attrs array Un tabeau du type array('attr_name' => attr_value, ...)
226   * 
227   * @retval mixed Le nouvelle objet en cas de succès, false sinon
228   */
229   function getNewEntry($dn,$objectClass,$attrs,$add=false) {
230     $newentry = Net_LDAP2_Entry::createFresh($dn,array_merge(array('objectclass' =>$objectClass),(array)$attrs));
231     if(Net_LDAP2::isError($newentry)) {
232       return false;
233     }
234     if($add) {
235       if(!$this -> cnx -> add($newentry)) {
236         return;
237       }
238     }
239     return $newentry;
240   }
241   
242   /**
243    * Met à jour une entrée dans l'annuaire
244    * 
245    * Remarque : Supprime les valeurs vides de attributs et les attributs sans valeur.
246    *
247    * @author Benjamin Renard <brenard@easter-eggs.com>
248    *
249    * @param[in] $object_type string Type de l'objet Ldap
250    * @param[in] $dn string DN de l'entré Ldap
251    * @param[in] $change array Tableau des modifications à apporter
252    *
253    * @retval boolean true si l'objet a bien été mis à jour, false sinon
254    */
255   function update($object_type,$dn,$change) {
256     LSdebug($change);
257     $dropAttr=array();
258     $entry=$this -> getEntry($object_type,$dn);
259     if (is_array($entry)) {
260       $new = $entry['new'];
261       $entry = $entry['entry'];
262     }
263     else {
264       $new = false;
265     }
266
267     if($entry) {
268       foreach($change as $attrName => $attrVal) {
269         $drop = true;
270         if (is_array($attrVal)) {
271           foreach($attrVal as $val) {
272             if (!empty($val)) {
273               $drop = false;
274               $changeData[$attrName][]=$val;
275             }
276           }
277         }
278         else {
279           if (!empty($attrVal)) {
280             $drop = false;
281             $changeData[$attrName][]=$attrVal;
282           }
283         }
284         if($drop) {
285           $dropAttr[] = $attrName;
286         }
287       }
288       $entry -> replace($changeData);
289       LSdebug('change : <pre>'.print_r($changeData,true).'</pre>');
290       LSdebug('drop : <pre>'.print_r($dropAttr,true).'</pre>');
291
292       if ($new) {
293         LSdebug('LSldap :: add()');
294         $ret = $this -> cnx -> add($entry);
295       }
296       else {
297         LSdebug('LSldap :: update()');
298         $ret = $entry -> update();
299       }
300       
301       if (Net_LDAP2::isError($ret)) {
302         LSerror :: addErrorCode('LSldap_05',$dn);
303         LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
304       }
305       else {
306         if (!empty($dropAttr)) {
307           foreach($dropAttr as $attr) {
308             if(Net_LDAP2::isError($entry -> getValue($attr))) {
309               // Attribut n'existe pas dans l'annuaire
310               continue;
311             }
312             // Méthode buggé : suppression impossible de certain attribut
313             // exemple : jpegPhoto
314             // $entry -> delete($attr);
315             $entry -> replace(array($attr =>array()));
316           }
317           $ret = $entry -> update();
318           if (Net_LDAP2::isError($ret)) {
319             LSerror :: addErrorCode('LSldap_06');
320             LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
321           }
322         }
323         return true;
324       }
325     }
326     else {
327       LSerror :: addErrorCode('LSldap_04');
328       return;
329     }
330   }
331
332   /**
333    * Test de bind
334    *
335    * Cette methode établie une connexion à l'annuaire Ldap et test un bind
336    * avec un login et un mot de passe passé en paramètre
337    *
338    * @author Benjamin Renard <brenard@easter-eggs.com>
339    *
340    * @retval boolean true si la connection à réussi, false sinon
341    */
342   function checkBind($dn,$pwd) {
343     $config = $this -> config;
344     $config['binddn'] = $dn;
345     $config['bindpw'] = $pwd;
346     $cnx = Net_LDAP2::connect($config);
347     if (Net_LDAP2::isError($cnx)) {
348       return;
349     }
350     return true;
351   }
352
353   /**
354    * Retourne l'état de la connexion Ldap
355    *
356    * @retval boolean True si le serveur est connecté, false sinon.
357    */
358   function isConnected() {
359     return ($this -> cnx == NULL)?false:true;
360   }
361   
362   /**
363    * Supprime un objet de l'annuaire
364    *
365    * @param[in] string DN de l'objet à supprimer
366    * 
367    * @retval boolean True si l'objet à été supprimé, false sinon
368    */
369   function remove($dn) {
370     $ret = $this -> cnx -> delete($dn,array('recursive' => true));
371     if (Net_LDAP2::isError($ret)) {
372       LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
373       return;
374     }
375     return true;
376   }
377
378   /**
379    * Déplace un objet LDAP dans l'annuaire
380    * 
381    * @param[in] $old string Le DN actuel
382    * @param[in] $new string Le futur DN
383    * 
384    * @retval boolean True si l'objet a été déplacé, false sinon
385    */
386   function move($old,$new) {
387     $ret = $this -> cnx -> move($old,$new);
388     if (Net_LDAP2::isError($ret)) {
389       LSerror :: addErrorCode('LSldap_07');
390       LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
391       return;
392     }
393     return true;
394   }
395 }
396
397 /*
398  * Error Codes
399  */
400 $GLOBALS['LSerror_code']['LSldap_01'] = array (
401   'msg' => _("LSldap : Error during the LDAP server connection (%{msg}).")
402 );
403 $GLOBALS['LSerror_code']['LSldap_02'] = array (
404   'msg' => _("LSldap : Error during the LDAP search (%{msg}).")
405 );
406 $GLOBALS['LSerror_code']['LSldap_03'] = array (
407   'msg' => _("LSldap : Object type unkown.")
408 );
409 $GLOBALS['LSerror_code']['LSldap_04'] = array (
410   'msg' => _("LSldap : Error during fecthing the LDAP entry.")
411 );
412 $GLOBALS['LSerror_code']['LSldap_05'] = array (
413   'msg' => _("LSldap : Error during changing the LDAP entry (DN : %{dn}).")
414 );
415 $GLOBALS['LSerror_code']['LSldap_06'] = array (
416   'msg' => _("LSldap : Error during deleting the empty attributes.")
417 );
418 $GLOBALS['LSerror_code']['LSldap_07'] = array (
419   'msg' => _("LSldap : Error during changing the DN of the object.")
420 );
421 ?>