2 /*******************************************************************************
3 * Copyright (C) 2007 Easter-eggs
4 * http://ldapsaisie.labs.libre-entreprise.org
6 * Author: See AUTHORS file in top-level directory.
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.
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.
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.
21 ******************************************************************************/
24 * Gestion de l'accès à l'annaire Ldap
26 * Cette classe gère l'accès à l'annuaire ldap en s'appuyant sur PEAR :: Net_LDAP2
28 * @author Benjamin Renard <brenard@easter-eggs.com>
32 private static $config;
33 private static $cnx = NULL;
36 * Défini la configuration
38 * Cette methode définis la configuration de l'accès à l'annuaire
40 * @author Benjamin Renard <brenard@easter-eggs.com>
42 * @param[in] $config array Tableau de configuration au formar Net_LDAP2
46 function setConfig ($config) {
47 self :: $config = $config;
53 * Cette methode établie la connexion à l'annuaire Ldap
55 * @author Benjamin Renard <brenard@easter-eggs.com>
57 * @param[in] $config array Tableau de configuration au formar Net_LDAP2
59 * @retval boolean true si la connection est établie, false sinon
61 public static function connect($config = null) {
63 self :: setConfig($config);
65 self :: $cnx = Net_LDAP2::connect(self :: $config);
66 if (Net_LDAP2::isError(self :: $cnx)) {
67 LSerror :: addErrorCode('LSldap_01',self :: $cnx -> getMessage());
77 * Cette methode clos la connexion à l'annuaire Ldap
79 * @author Benjamin Renard <brenard@easter-eggs.com>
83 public static function close() {
84 self :: $cnx -> done();
88 * Recherche dans l'annuaire
90 * Cette methode effectue une recherche dans l'annuaire et retourne le resultat
91 * de celle-ci sous la forme d'un tableau.
93 * @author Benjamin Renard <brenard@easter-eggs.com>
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()
99 * @see Net_LDAP2::search()
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).
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());
113 foreach($ret -> entries() as $entry) {
114 $retInfos[]=array('dn' => $entry -> dn(), 'attrs' => $entry -> getValues());
120 * Compte le nombre de retour d'une recherche dans l'annuaire
122 * Cette methode effectue une recherche dans l'annuaire et retourne le nombre
125 * @author Benjamin Renard <brenard@easter-eggs.com>
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()
131 * @see Net_LDAP2::search()
133 * @retval numeric Le nombre d'entré trouvées
135 public static function getNumberResult ($filter,$basedn=NULL,$params = array() ) {
138 $ret = self :: $cnx -> search($basedn,$filter,$params);
139 if (Net_LDAP2::isError($ret)) {
140 LSerror :: addErrorCode('LSldap_02',$ret -> getMessage());
143 return $ret -> count();
147 * Charge les valeurs des attributs d'une entré de l'annuaire
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.
152 * @author Benjamin Renard <brenard@easter-eggs.com>
154 * @param[in] $dn string DN de l'entré Ldap
156 * @retval array Tableau associatif des valeurs des attributs avec en clef, le nom de l'attribut.
158 public static function getAttrs($dn) {
159 $infos = ldap_explode_dn($dn,0);
160 if((!$infos)||($infos['count']==0))
163 for ($i=1;$i<$infos['count'];$i++) {
164 $sep=($basedn=='')?'':',';
165 $basedn.=$sep.$infos[$i];
167 $return=self :: search($infos[0],$basedn);
168 return $return[0]['attrs'];
172 * Retourne une entrée existante ou nouvelle
174 * @author Benjamin Renard <brenard@easter-eggs.com>
176 * @param[in] $object_type string Type de l'objet Ldap
177 * @param[in] $dn string DN de l'entré Ldap
179 * @retval ldapentry|array Un objet ldapentry (PEAR::Net_LDAP2)
180 * ou un tableau (si c'est une nouvelle entrée):
182 * 'entry' => ldapentry,
186 public static function getEntry($object_type,$dn) {
187 $obj_conf=LSconfig :: get('LSobjects.'.$object_type);
188 if(is_array($obj_conf)){
189 $entry = self :: getLdapEntry($dn);
190 if ($entry === false) {
191 $newentry = self :: getNewEntry($dn,$obj_conf['objectclass'],array());
196 return array('entry' => $newentry,'new' => true);
203 LSerror :: addErrorCode('LSldap_03');
209 * Retourne un object NetLDAP d'une entree existante
211 * @author Benjamin Renard <brenard@easter-eggs.com>
213 * @param[in] $dn string DN de l'entré Ldap
215 * @retval ldapentry|boolean Un objet ldapentry (PEAR::Net_LDAP2) ou false en
218 public static function getLdapEntry($dn) {
219 $entry = self :: $cnx -> getEntry($dn);
220 if (Net_LDAP2::isError($entry)) {
229 * Retourne une nouvelle entrée
231 * @param[in] $dn string Le DN de l'objet
232 * @param[in] $objectClass array Un tableau contenant les objectClass de l'objet
233 * @param[in] $attrs array Un tabeau du type array('attr_name' => attr_value, ...)
235 * @retval mixed Le nouvelle objet en cas de succès, false sinon
237 public static function getNewEntry($dn,$objectClass,$attrs,$add=false) {
238 $newentry = Net_LDAP2_Entry::createFresh($dn,array_merge(array('objectclass' =>$objectClass),(array)$attrs));
239 if(Net_LDAP2::isError($newentry)) {
243 if(!self :: $cnx -> add($newentry)) {
251 * Met à jour une entrée dans l'annuaire
253 * Remarque : Supprime les valeurs vides de attributs et les attributs sans valeur.
255 * @author Benjamin Renard <brenard@easter-eggs.com>
257 * @param[in] $object_type string Type de l'objet Ldap
258 * @param[in] $dn string DN de l'entré Ldap
259 * @param[in] $change array Tableau des modifications à apporter
261 * @retval boolean true si l'objet a bien été mis à jour, false sinon
263 public static function update($object_type,$dn,$change) {
266 $entry=self :: getEntry($object_type,$dn);
267 if (is_array($entry)) {
268 $new = $entry['new'];
269 $entry = $entry['entry'];
276 foreach($change as $attrName => $attrVal) {
278 if (is_array($attrVal)) {
279 foreach($attrVal as $val) {
282 $changeData[$attrName][]=$val;
287 if (!empty($attrVal)) {
289 $changeData[$attrName][]=$attrVal;
293 $dropAttr[] = $attrName;
296 $entry -> replace($changeData);
297 LSdebug('change : <pre>'.print_r($changeData,true).'</pre>');
298 LSdebug('drop : <pre>'.print_r($dropAttr,true).'</pre>');
301 LSdebug('LSldap :: add()');
302 $ret = self :: $cnx -> add($entry);
305 LSdebug('LSldap :: update()');
306 $ret = $entry -> update();
309 if (Net_LDAP2::isError($ret)) {
310 LSerror :: addErrorCode('LSldap_05',$dn);
311 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
314 if (!empty($dropAttr)) {
315 foreach($dropAttr as $attr) {
316 if(Net_LDAP2::isError($entry -> getValue($attr))) {
317 // Attribut n'existe pas dans l'annuaire
320 // Méthode buggé : suppression impossible de certain attribut
321 // exemple : jpegPhoto
322 // $entry -> delete($attr);
323 $entry -> replace(array($attr =>array()));
325 $ret = $entry -> update();
326 if (Net_LDAP2::isError($ret)) {
327 LSerror :: addErrorCode('LSldap_06');
328 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
335 LSerror :: addErrorCode('LSldap_04');
343 * Cette methode établie une connexion à l'annuaire Ldap et test un bind
344 * avec un login et un mot de passe passé en paramètre
346 * @author Benjamin Renard <brenard@easter-eggs.com>
348 * @retval boolean true si la connection à réussi, false sinon
350 public static function checkBind($dn,$pwd) {
351 $config = self :: $config;
352 $config['binddn'] = $dn;
353 $config['bindpw'] = $pwd;
354 $cnx = Net_LDAP2::connect($config);
355 if (Net_LDAP2::isError($cnx)) {
362 * Retourne l'état de la connexion Ldap
364 * @retval boolean True si le serveur est connecté, false sinon.
366 public static function isConnected() {
367 return (self :: $cnx == NULL)?false:true;
371 * Supprime un objet de l'annuaire
373 * @param[in] string DN de l'objet à supprimer
375 * @retval boolean True si l'objet à été supprimé, false sinon
377 public static function remove($dn) {
378 $ret = self :: $cnx -> delete($dn,array('recursive' => true));
379 if (Net_LDAP2::isError($ret)) {
380 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
387 * Déplace un objet LDAP dans l'annuaire
389 * @param[in] $old string Le DN actuel
390 * @param[in] $new string Le futur DN
392 * @retval boolean True si l'objet a été déplacé, false sinon
394 public static function move($old,$new) {
395 $ret = self :: $cnx -> move($old,$new);
396 if (Net_LDAP2::isError($ret)) {
397 LSerror :: addErrorCode('LSldap_07');
398 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
405 * Combine LDAP Filters
407 * @params array Array of LDAP filters
409 * @retval Net_LDAP2_Filter | False The combined filter or False
411 public static function combineFilters($op,$filters,$asStr=false) {
412 if (is_array($filters) && !empty($filters)) {
413 if (count($filters)==1) {
416 $filter=Net_LDAP2_Filter::combine($op,$filters);
417 if (!Net_LDAP2::isError($filter)) {
419 return $filter->asString();
426 LSerror :: addErrorCode(0,$filter -> getMessage());
433 * Check LDAP Filters String
435 * @params string A LDAP filter as string
437 * @retval boolean True only if the filter could be parsed
439 public static function isValidFilter($filter) {
440 if (is_string($filter) && !empty($filter)) {
441 $filter=Net_LDAP2_Filter::parse($filter);
442 if (!Net_LDAP2::isError($filter)) {
446 LSerror :: addErrorCode(0,$filter -> getMessage());
456 LSerror :: defineError('LSldap_01',
457 _("LSldap : Error during the LDAP server connection (%{msg}).")
459 LSerror :: defineError('LSldap_02',
460 _("LSldap : Error during the LDAP search (%{msg}).")
462 LSerror :: defineError('LSldap_03',
463 _("LSldap : Object type unknown.")
465 LSerror :: defineError('LSldap_04',
466 _("LSldap : Error while fetching the LDAP entry.")
468 LSerror :: defineError('LSldap_05',
469 _("LSldap : Error while changing the LDAP entry (DN : %{dn}).")
471 LSerror :: defineError('LSldap_06',
472 _("LSldap : Error while deleting empty attributes.")
474 LSerror :: defineError('LSldap_07',
475 _("LSldap : Error while changing the DN of the object.")