Overview

Packages

  • Group

Classes

  • Horde_Group_Base
  • Horde_Group_Contactlists
  • Horde_Group_Exception
  • Horde_Group_Kolab
  • Horde_Group_Ldap
  • Horde_Group_Mock
  • Horde_Group_Sql
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * This class provides an LDAP driver for the Horde group system.
  4:  *
  5:  * Copyright 2005-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author   Ben Chavet <ben@horde.org>
 11:  * @author   Jan Schneider <jan@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 14:  * @package  Group
 15:  */
 16: class Horde_Group_Ldap extends Horde_Group_Base
 17: {
 18:     /**
 19:      * Handle for the current LDAP connection.
 20:      *
 21:      * @var Horde_Ldap
 22:      */
 23:     protected $_ldap;
 24: 
 25:     /**
 26:      * Any additional parameters for the driver.
 27:      *
 28:      * @var array
 29:      */
 30:     protected $_params;
 31: 
 32:     /**
 33:      * LDAP filter for searching groups.
 34:      *
 35:      * @var Horde_Ldap_Filter
 36:      */
 37:     protected $_filter;
 38: 
 39:     /**
 40:      * Constructor.
 41:      *
 42:      * @throws Horde_Group_Exception
 43:      */
 44:     public function __construct($params)
 45:     {
 46:         $params = array_merge(
 47:             array('binddn'               => '',
 48:                   'bindpw'               => '',
 49:                   'gid'                  => 'cn',
 50:                   'memberuid'            => 'memberUid',
 51:                   'objectclass'          => array('posixGroup'),
 52:                   'newgroup_objectclass' => array('posixGroup')),
 53:             $params
 54:         );
 55: 
 56:         /* Check mandatory parameters. */
 57:         foreach (array('ldap', 'basedn') as $param) {
 58:             if (!isset($params[$param])) {
 59:                 throw new Horde_Group_Exception('The \'' . $param . '\' parameter is missing.');
 60:             }
 61:         }
 62: 
 63:         /* Set Horde_Ldap object. */
 64:         $this->_ldap = $params['ldap'];
 65:         unset($params['ldap']);
 66: 
 67:         /* Lowercase attribute names. */
 68:         $params['gid']       = Horde_String::lower($params['gid']);
 69:         $params['memberuid'] = Horde_String::lower($params['memberuid']);
 70:         if (!is_array($params['newgroup_objectclass'])) {
 71:             $params['newgroup_objectclass'] = array($params['newgroup_objectclass']);
 72:         }
 73:         foreach ($params['newgroup_objectclass'] as &$objectClass) {
 74:             $objectClass = Horde_String::lower($objectClass);
 75:         }
 76: 
 77:         /* Generate LDAP search filter. */
 78:         try {
 79:             $this->_filter = Horde_Ldap_Filter::build($params['search']);
 80:         } catch (Horde_Ldap_Exception $e) {
 81:             throw new Horde_Group_Exception($e);
 82:         }
 83: 
 84:         $this->_params = $params;
 85:     }
 86: 
 87:     /**
 88:      * Returns whether the group backend is read-only.
 89:      *
 90:      * @return boolean
 91:      */
 92:     public function readOnly()
 93:     {
 94:         return !isset($this->_params['writedn']) ||
 95:                !isset($this->_params['writepw']);
 96:     }
 97: 
 98:     /**
 99:      * Returns whether groups can be renamed.
100:      *
101:      * @return boolean
102:      */
103:     public function renameSupported()
104:     {
105:         return false;
106:     }
107: 
108:     /**
109:      * Creates a new group.
110:      *
111:      * @param string $name   A group name.
112:      * @param string $email  The group's email address.
113:      *
114:      * @return mixed  The ID of the created group.
115:      * @throws Horde_Group_Exception
116:      */
117:     public function create($name, $email = null)
118:     {
119:         if ($this->readOnly()) {
120:             throw new Horde_Group_Exception('This group backend is read-only.');
121:         }
122: 
123:         $attributes = array(
124:             $this->_params['gid'] => $name,
125:             'objectclass'         => $this->_params['newgroup_objectclass'],
126:             'gidnumber'           => $this->_nextGid());
127:         if (!empty($email)) {
128:             $attributes['mail'] = $email;
129:         }
130: 
131:         return $this->_create($name, $attributes);
132:     }
133: 
134:     /**
135:      * Creates a new group.
136:      *
137:      * @param string $name       A group name.
138:      * @param array $attributes  The group's attributes.
139:      *
140:      * @return mixed  The ID of the created group.
141:      * @throws Horde_Group_Exception
142:      */
143:     protected function _create($name, array $attributes)
144:     {
145:         $dn = Horde_Ldap::quoteDN(array(array($this->_params['gid'], $name))) . ',' . $this->_params['basedn'];
146:         try {
147:             $entry = Horde_Ldap_Entry::createFresh($dn, $attributes);
148:             $this->_rebind(true);
149:             $this->_ldap->add($entry);
150:             $this->_rebind(false);
151:             return $dn;
152:         } catch (Horde_Ldap_Exception $e) {
153:             throw new Horde_Group_Exception($e);
154:         }
155:     }
156: 
157:     /**
158:      * Renames a group.
159:      *
160:      * @param mixed $gid    A group ID.
161:      * @param string $name  The new name.
162:      *
163:      * @throws Horde_Group_Exception
164:      */
165:     public function rename($gid, $name)
166:     {
167:         throw new Horde_Group_Exception('Renaming groups is not supported with the LDAP driver.');
168:     }
169: 
170:     /**
171:      * Removes a group.
172:      *
173:      * @param mixed $gid  A group ID.
174:      *
175:      * @throws Horde_Group_Exception
176:      */
177:     public function remove($gid)
178:     {
179:         if ($this->readOnly()) {
180:             throw new Horde_Group_Exception('This group backend is read-only.');
181:         }
182: 
183:         try {
184:             $this->_rebind(true);
185:             $this->_ldap->delete($gid);
186:             $this->_rebind(false);
187:         } catch (Horde_Ldap_Exception $e) {
188:             throw new Horde_Group_Exception($e);
189:         }
190:     }
191: 
192:     /**
193:      * Checks if a group exists.
194:      *
195:      * @param mixed $gid  A group ID.
196:      *
197:      * @return boolean  True if the group exists.
198:      * @throws Horde_Group_Exception
199:      */
200:     public function exists($gid)
201:     {
202:         try {
203:             return $this->_ldap->exists($gid);
204:         } catch (Horde_Ldap_Exception $e) {
205:             throw new Horde_Group_Exception($e);
206:         }
207:     }
208: 
209:     /**
210:      * Returns a group name.
211:      *
212:      * @param mixed $gid  A group ID.
213:      *
214:      * @return string  The group's name.
215:      * @throws Horde_Group_Exception
216:      * @throws Horde_Exception_NotFound
217:      */
218:     public function getName($gid)
219:     {
220:         try {
221:             $entry = $this->_ldap->getEntry($gid);
222:             return $entry->getValue($this->_params['gid'], 'single');
223:         } catch (Horde_Ldap_Exception $e) {
224:             throw new Horde_Group_Exception($e);
225:         }
226:     }
227: 
228:     /**
229:      * Returns all available attributes of a group.
230:      *
231:      * @param mixed $gid  A group ID.
232:      *
233:      * @return array  The group's date.
234:      * @throws Horde_Group_Exception
235:      * @throws Horde_Exception_NotFound
236:      */
237:     public function getData($gid)
238:     {
239:         try {
240:             $entry = $this->_ldap->getEntry($gid);
241:             $attributes = $entry->getValues();
242:         } catch (Horde_Ldap_Exception $e) {
243:             throw new Horde_Group_Exception($e);
244:         }
245:         $data = array();
246:         foreach ($attributes as $attribute => $value) {
247:             switch ($attribute) {
248:             case $this->_params['gid']:
249:                 $attribute = 'name';
250:                 break;
251:             case 'mail':
252:                 $attribute = 'email';
253:                 break;
254:             }
255:             $data[$attribute] = $value;
256:         }
257:         return $data;
258:     }
259: 
260:     /**
261:      * Sets one or more attributes of a group.
262:      *
263:      * @param mixed $gid               A group ID.
264:      * @param array|string $attribute  An attribute name or a hash of
265:      *                                 attributes.
266:      * @param string $value            An attribute value if $attribute is a
267:      *                                 string.
268:      *
269:      * @throws Horde_Group_Exception
270:      * @throws Horde_Exception_NotFound
271:      */
272:     public function setData($gid, $attribute, $value = null)
273:     {
274:         if ($this->readOnly()) {
275:             throw new Horde_Group_Exception('This group backend is read-only.');
276:         }
277: 
278:         $attributes = is_array($attribute)
279:             ? $attribute
280:             : array($attribute => $value);
281:         try {
282:             $entry = $this->_ldap->getEntry($gid);
283:             foreach ($attributes as $attribute => $value) {
284:                 switch ($attribute) {
285:                 case 'name':
286:                     $attribute = $this->_params['gid'];
287:                     break;
288:                 case 'email':
289:                     $attribute = 'mail';
290:                     break;
291:                 }
292:                 $entry->replace(array($attribute => $value));
293:             }
294:             $this->_rebind(true);
295:             $entry->update();
296:             $this->_rebind(false);
297:         } catch (Horde_Ldap_Exception $e) {
298:             throw new Horde_Group_Exception($e);
299:         }
300:     }
301: 
302:     /**
303:      * Returns a list of all groups a user may see, with IDs as keys and names
304:      * as values.
305:      *
306:      * @param string $member  Only return groups that this user is a member of.
307:      *
308:      * @return array  All existing groups.
309:      * @throws Horde_Group_Exception
310:      */
311:     public function listAll($member = null)
312:     {
313:         if (!is_null($member)) {
314:             return $this->listGroups($member);
315:         }
316: 
317:         $attr = $this->_params['gid'];
318:         try {
319:             $search = $this->_ldap->search($this->_params['basedn'],
320:                                            $this->_filter,
321:                                            array($attr));
322:         } catch (Horde_Ldap_Exception $e) {
323:             throw new Horde_Group_Exception($e);
324:         }
325: 
326:         $entries = array();
327:         foreach ($search->sortedAsArray(array($attr)) as $entry) {
328:             $entries[$entry['dn']] = $entry[$attr][0];
329:         }
330:         return $entries;
331:     }
332: 
333:     /**
334:      * Returns a list of users in a group.
335:      *
336:      * @param mixed $gid  A group ID.
337:      *
338:      * @return array  List of group users.
339:      * @throws Horde_Group_Exception
340:      * @throws Horde_Exception_NotFound
341:      */
342:     public function listUsers($gid)
343:     {
344:         $attr = $this->_params['memberuid'];
345:         try {
346:             $entry = $this->_ldap->getEntry($gid, array($attr));
347:             if (!$entry->exists($attr)) {
348:                 return array();
349:             }
350: 
351:             if (empty($this->_params['attrisdn'])) {
352:                 return $entry->getValue($attr, 'all');
353:             }
354: 
355:             $users = array();
356:             foreach ($entry->getValue($attr, 'all') as $user) {
357:                 $dn = Horde_Ldap_Util::explodeDN($user,
358:                                                  array('onlyvalues' => true));
359:                 // Very simplified approach: assume the first element of the DN
360:                 // contains the user ID.
361:                 $user = $dn[0];
362:                 // Check for multi-value RDNs.
363:                 if (is_array($element)) {
364:                     $user = $element[0];
365:                 }
366:                 $users[] = $user;
367:             }
368:             return $users;
369:         } catch (Horde_Ldap_Exception $e) {
370:             throw new Horde_Group_Exception($e);
371:         }
372:     }
373: 
374:     /**
375:      * Returns a list of groups a user belongs to.
376:      *
377:      * @param string $user  A user name.
378:      *
379:      * @return array  A list of groups, with IDs as keys and names as values.
380:      * @throws Horde_Group_Exception
381:      */
382:     public function listGroups($user)
383:     {
384:         $attr = $this->_params['gid'];
385:         try {
386:             if (!empty($this->_params['attrisdn'])) {
387:                 $user =  $this->_ldap->findUserDN($user);
388:             }
389:             $filter = Horde_Ldap_Filter::create($this->_params['memberuid'],
390:                                                 'equals', $user);
391:             $filter = Horde_Ldap_Filter::combine('and', array($this->_filter, $filter));
392:             $search = $this->_ldap->search($this->_params['basedn'], $filter,
393:                                            array($attr));
394:         } catch (Horde_Ldap_Exception $e) {
395:             throw new Horde_Group_Exception($e);
396:         }
397:         $entries = array();
398:         foreach ($search->sortedAsArray(array($attr)) as $entry) {
399:             $entries[$entry['dn']] = $entry[$attr][0];
400:         }
401:         return $entries;
402:     }
403: 
404:     /**
405:      * Add a user to a group.
406:      *
407:      * @param mixed $gid    A group ID.
408:      * @param string $user  A user name.
409:      *
410:      * @throws Horde_Group_Exception
411:      * @throws Horde_Exception_NotFound
412:      */
413:     public function addUser($gid, $user)
414:     {
415:         if ($this->readOnly()) {
416:             throw new Horde_Group_Exception('This group backend is read-only.');
417:         }
418: 
419:         $attr = $this->_params['memberuid'];
420:         try {
421:             if (!empty($this->_params['attrisdn'])) {
422:                 $user =  $this->_ldap->findUserDN($user);
423:             }
424:             $entry = $this->_ldap->getEntry($gid, array($attr));
425:             $entry->add(array($attr => $user));
426:             $this->_rebind(true);
427:             $entry->update();
428:             $this->_rebind(false);
429:         } catch (Horde_Ldap_Exception $e) {
430:             throw new Horde_Group_Exception($e);
431:         }
432:     }
433: 
434:     /**
435:      * Removes a user from a group.
436:      *
437:      * @param mixed $gid    A group ID.
438:      * @param string $user  A user name.
439:      *
440:      * @throws Horde_Group_Exception
441:      * @throws Horde_Exception_NotFound
442:      */
443:     public function removeUser($gid, $user)
444:     {
445:         $attr = $this->_params['memberuid'];
446:         try {
447:             if (!empty($this->_params['attrisdn'])) {
448:                 $user =  $this->_ldap->findUserDN($user);
449:             }
450:             $entry = $this->_ldap->getEntry($gid, array($attr));
451:             $entry->delete(array($attr => $user));
452:             $this->_rebind(true);
453:             $entry->update();
454:             $this->_rebind(false);
455:         } catch (Horde_Ldap_Exception $e) {
456:             throw new Horde_Group_Exception($e);
457:         }
458:     }
459: 
460:     /**
461:      * Searches for group names.
462:      *
463:      * @param string $name  A search string.
464:      *
465:      * @return array  A list of matching groups, with IDs as keys and names as
466:      *                values.
467:      * @throws Horde_Group_Exception
468:      */
469:     public function search($name)
470:     {
471:         $attr = $this->_params['gid'];
472:         try {
473:             $result = $this->_ldap->search(
474:                 $this->_params['basedn'],
475:                 Horde_Ldap_Filter::create($attr, 'contains', $name),
476:                 array($attr));
477:         } catch (Horde_Ldap_Exception $e) {
478:             throw new Horde_Group_Exception($e);
479:         }
480:         $entries = array();
481:         foreach ($result->sortedAsArray(array($attr)) as $entry) {
482:             $entries[$entry['dn']] = $entry[$attr][0];
483:         }
484:         return $entries;
485:     }
486: 
487:     /**
488:      * Searches existing groups for the highest gidnumber, and returns one
489:      * higher.
490:      *
491:      * @return integer  The next group ID.
492:      *
493:      * @throws Horde_Group_Exception
494:      */
495:     protected function _nextGid()
496:     {
497:         try {
498:             $search = $this->_ldap->search(
499:                 $this->_params['basedn'],
500:                 $this->_filter,
501:                 array('attributes' => array('gidnumber')));
502:         } catch (Horde_Ldap_Exception $e) {
503:             throw new Horde_Group_Exception($e);
504:         }
505: 
506:         if (!$search->count()) {
507:             return 1;
508:         }
509: 
510:         $nextgid = 0;
511:         foreach ($search as $entry) {
512:             $nextgid = max($nextgid, $entry->getValue('gidnumber', 'single'));
513:         }
514: 
515:         return $nextgid + 1;
516:     }
517: 
518:     /**
519:      * Rebinds to the LDAP server.
520:      *
521:      * @param boolean $write  Whether to rebind for write access. Use false
522:      *                        after finishing write actions.
523:      *
524:      * @throws Horde_Ldap_Exception
525:      */
526:     protected function _rebind($write)
527:     {
528:         if ($write) {
529:             $this->_ldap->bind($this->_params['writedn'], $this->_params['writepw']);
530:         } else {
531:             $this->_ldap->bind();
532:         }
533:     }
534: }
535: 
API documentation generated by ApiGen