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 $attributes = array();
192 foreach($obj_conf['attrs'] as $attr_name => $attr_conf) {
193 if( isset($attr_conf['default_value']) ) {
194 $attributes[$attr_name]=$attr_conf['default_value'];
198 $newentry = self :: getNewEntry($dn,$obj_conf['objectclass'],$attributes);
203 return array('entry' => $newentry,'new' => true);
210 LSerror :: addErrorCode('LSldap_03');
216 * Retourne un object NetLDAP d'une entree existante
218 * @author Benjamin Renard <brenard@easter-eggs.com>
220 * @param[in] $dn string DN de l'entré Ldap
222 * @retval ldapentry|boolean Un objet ldapentry (PEAR::Net_LDAP2) ou false en
225 public static function getLdapEntry($dn) {
226 $entry = self :: $cnx -> getEntry($dn);
227 if (Net_LDAP2::isError($entry)) {
236 * Retourne une nouvelle entrée
238 * @param[in] $dn string Le DN de l'objet
239 * @param[in] $objectClass array Un tableau contenant les objectClass de l'objet
240 * @param[in] $attrs array Un tabeau du type array('attr_name' => attr_value, ...)
242 * @retval mixed Le nouvelle objet en cas de succès, false sinon
244 public static function getNewEntry($dn,$objectClass,$attrs,$add=false) {
245 $newentry = Net_LDAP2_Entry::createFresh($dn,array_merge(array('objectclass' =>$objectClass),(array)$attrs));
246 if(Net_LDAP2::isError($newentry)) {
250 if(!self :: $cnx -> add($newentry)) {
258 * Met à jour une entrée dans l'annuaire
260 * Remarque : Supprime les valeurs vides de attributs et les attributs sans valeur.
262 * @author Benjamin Renard <brenard@easter-eggs.com>
264 * @param[in] $object_type string Type de l'objet Ldap
265 * @param[in] $dn string DN de l'entré Ldap
266 * @param[in] $change array Tableau des modifications à apporter
268 * @retval boolean true si l'objet a bien été mis à jour, false sinon
270 public static function update($object_type,$dn,$change) {
273 $entry=self :: getEntry($object_type,$dn);
274 if (is_array($entry)) {
275 $new = $entry['new'];
276 $entry = $entry['entry'];
283 foreach($change as $attrName => $attrVal) {
285 if (is_array($attrVal)) {
286 foreach($attrVal as $val) {
289 $changeData[$attrName][]=$val;
294 if (!empty($attrVal)) {
296 $changeData[$attrName][]=$attrVal;
300 $dropAttr[] = $attrName;
303 $entry -> replace($changeData);
304 LSdebug('change : <pre>'.print_r($changeData,true).'</pre>');
305 LSdebug('drop : <pre>'.print_r($dropAttr,true).'</pre>');
308 LSdebug('LSldap :: add()');
309 $ret = self :: $cnx -> add($entry);
312 LSdebug('LSldap :: update()');
313 $ret = $entry -> update();
316 if (Net_LDAP2::isError($ret)) {
317 LSerror :: addErrorCode('LSldap_05',$dn);
318 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
321 if (!empty($dropAttr)) {
322 foreach($dropAttr as $attr) {
323 if(Net_LDAP2::isError($entry -> getValue($attr))) {
324 // Attribut n'existe pas dans l'annuaire
327 // Méthode buggé : suppression impossible de certain attribut
328 // exemple : jpegPhoto
329 // $entry -> delete($attr);
330 $entry -> replace(array($attr =>array()));
332 $ret = $entry -> update();
333 if (Net_LDAP2::isError($ret)) {
334 LSerror :: addErrorCode('LSldap_06');
335 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
342 LSerror :: addErrorCode('LSldap_04');
350 * Cette methode établie une connexion à l'annuaire Ldap et test un bind
351 * avec un login et un mot de passe passé en paramètre
353 * @author Benjamin Renard <brenard@easter-eggs.com>
355 * @retval boolean true si la connection à réussi, false sinon
357 public static function checkBind($dn,$pwd) {
358 $config = self :: $config;
359 $config['binddn'] = $dn;
360 $config['bindpw'] = $pwd;
361 $cnx = Net_LDAP2::connect($config);
362 if (Net_LDAP2::isError($cnx)) {
369 * Retourne l'état de la connexion Ldap
371 * @retval boolean True si le serveur est connecté, false sinon.
373 public static function isConnected() {
374 return (self :: $cnx == NULL)?false:true;
378 * Supprime un objet de l'annuaire
380 * @param[in] string DN de l'objet à supprimer
382 * @retval boolean True si l'objet à été supprimé, false sinon
384 public static function remove($dn) {
385 $ret = self :: $cnx -> delete($dn,array('recursive' => true));
386 if (Net_LDAP2::isError($ret)) {
387 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
394 * Déplace un objet LDAP dans l'annuaire
396 * @param[in] $old string Le DN actuel
397 * @param[in] $new string Le futur DN
399 * @retval boolean True si l'objet a été déplacé, false sinon
401 public static function move($old,$new) {
402 $ret = self :: $cnx -> move($old,$new);
403 if (Net_LDAP2::isError($ret)) {
404 LSerror :: addErrorCode('LSldap_07');
405 LSerror :: addErrorCode(0,'NetLdap-Error : '.$ret->getMessage());
412 * Combine LDAP Filters
414 * @params array Array of LDAP filters
416 * @retval Net_LDAP2_Filter | False The combined filter or False
418 public static function combineFilters($op,$filters,$asStr=false) {
419 if (is_array($filters) && !empty($filters)) {
420 if (count($filters)==1) {
423 $filter=Net_LDAP2_Filter::combine($op,$filters);
424 if (!Net_LDAP2::isError($filter)) {
426 return $filter->asString();
433 LSerror :: addErrorCode(0,$filter -> getMessage());
440 * Check LDAP Filters String
442 * @params string A LDAP filter as string
444 * @retval boolean True only if the filter could be parsed
446 public static function isValidFilter($filter) {
447 if (is_string($filter) && !empty($filter)) {
448 $filter=Net_LDAP2_Filter::parse($filter);
449 if (!Net_LDAP2::isError($filter)) {
453 LSerror :: addErrorCode(0,$filter -> getMessage());
463 LSerror :: defineError('LSldap_01',
464 _("LSldap : Error during the LDAP server connection (%{msg}).")
466 LSerror :: defineError('LSldap_02',
467 _("LSldap : Error during the LDAP search (%{msg}).")
469 LSerror :: defineError('LSldap_03',
470 _("LSldap : Object type unknown.")
472 LSerror :: defineError('LSldap_04',
473 _("LSldap : Error while fetching the LDAP entry.")
475 LSerror :: defineError('LSldap_05',
476 _("LSldap : Error while changing the LDAP entry (DN : %{dn}).")
478 LSerror :: defineError('LSldap_06',
479 _("LSldap : Error while deleting empty attributes.")
481 LSerror :: defineError('LSldap_07',
482 _("LSldap : Error while changing the DN of the object.")