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