--- /dev/null
+<?php
+/**
+ * The Horde_Perms_Datatree:: class provides a DataTree driver for the Horde
+ * permissions system.
+ *
+ * Copyright 2001-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package Horde_Perms
+ */
+class Horde_Perms_Datatree extends Horde_Perms
+{
+ /**
+ * Pointer to a DataTree instance to manage the different permissions.
+ *
+ * @var DataTree
+ */
+ protected $_datatree;
+
+ /**
+ * Incrementing version number if cached classes change.
+ *
+ * @var integer
+ */
+ private $_cacheVersion = 2;
+
+ /**
+ * Cache for getPermission().
+ *
+ * @var array
+ */
+ protected $_permsCache = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Configuration parameters (in addition to base
+ * Horde_Perms parameters):
+ * <pre>
+ * 'datatree' - (DataTree) A datatree object. [REQUIRED]
+ * </pre>
+ *
+ * @throws Horde_Perms_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (empty($params['datatree'])) {
+ throw new Horde_Perms_Exception('You must configure a DataTree backend.');
+ }
+
+ $this->_datatree = $params['datatree'];
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Returns a new permissions object.
+ *
+ * @param string $name The permission's name.
+ *
+ * @return DataTreeObject_Permissions A new permissions object.
+ */
+ public function newPermission($name)
+ {
+ $type = 'matrix';
+ $params = null;
+
+ if ($pos = strpos($name, ':')) {
+ try {
+ $info = $this->getApplicationPermissions(substr($name, 0, $pos));
+ if (isset($info['type']) && isset($info['type'][$name])) {
+ $type = $info['type'][$name];
+ }
+
+ if (isset($info['params']) && isset($info['params'][$name])) {
+ $params = $info['params'][$name];
+ }
+ } catch (Horde_Perms_Exception $e) {}
+ }
+
+ $perm = new Horde_Perms_Permission_DataTreeObject($name, $this->_cacheVersion, $type, $params);
+ $perm->setCacheOb($this->_cache);
+ $perm->setDataTree($this->_datatree);
+
+ return $perm;
+ }
+
+ /**
+ * Returns a permission object corresponding to the named permission,
+ * with the users and other data retrieved appropriately.
+ *
+ * @param string $name The name of the permission to retrieve.
+ *
+ * @return TODO
+ */
+ public function getPermission($name)
+ {
+ if (isset($this->_permsCache[$name])) {
+ return $this->_permsCache[$name];
+ }
+
+ $perm = $this->_cache->get('perm_' . $this->_cacheVersion . $name, $GLOBALS['conf']['cache']['default_lifetime']);
+ if ($perm === false) {
+ $perm = $this->_datatree->getObject($name, 'Horde_Perms_Permission_DataTreeObject');
+ $perm->setCacheVersion($this->_cacheVersion);
+ $this->_cache->set('perm_' . $this->_cacheVersion . $name, serialize($perm), $GLOBALS['conf']['cache']['default_lifetime']);
+ $this->_permsCache[$name] = $perm;
+ } else {
+ $this->_permsCache[$name] = unserialize($perm);
+ }
+
+ $this->_permsCache[$name]->setCacheOb($this->_cache);
+ $this->_permsCache[$name]->setDataTree($this->_datatree);
+
+ return $this->_permsCache[$name];
+ }
+
+ /**
+ * Returns a permission object corresponding to the given unique ID,
+ * with the users and other data retrieved appropriately.
+ *
+ * @param integer $cid The unique ID of the permission to retrieve.
+ */
+ public function getPermissionById($cid)
+ {
+ if ($cid == Horde_Perms::ROOT) {
+ return $this->newPermission(Horde_Perms::ROOT);
+ }
+ $perm = $this->_datatree->getObjectById($cid, 'Horde_Perms_Permission_DataTreeObject');
+ $perm->setCacheVersion($this->_cacheVersion);
+ return $perm;
+ }
+
+ /**
+ * Adds a permission to the permissions system. The permission must first
+ * be created with newPermission(), and have any initial users added to
+ * it, before this function is called.
+ *
+ * @param Horde_Perms_Permission_DataTreeObject $perm The new perm
+ * object.
+ * @throws Horde_Perms_Exception
+ */
+ public function addPermission(Horde_Perms_Permission_DataTreeObject $perm)
+ {
+ $name = $perm->getName();
+ if (empty($name)) {
+ throw Horde_Perms_Exception('Permission names must be non-empty.');
+ }
+ $this->_cache->expire('perm_' . $this->_cacheVersion . $name);
+ $this->_cache->expire('perm_exists_' . $this->_cacheVersion . $name);
+
+ return $this->_datatree->add($perm);
+ }
+
+ /**
+ * Removes a permission from the permissions system permanently.
+ *
+ * @param Horde_Perms_Permission_DataTreeObject $perm The permission to
+ * remove.
+ * @param boolean $force Force to remove
+ * every child.
+ */
+ public function removePermission(Horde_Perms_Permission_DataTreeObject $perm, $force = false)
+ {
+ $keys = $this->_datatree->get(DATATREE_FORMAT_FLAT, $perm->name, true);
+ foreach ($keys as $key) {
+ $this->_cache->expire('perm_' . $this->_cacheVersion . $key);
+ $this->_cache->expire('perm_exists_' . $this->_cacheVersion . $key);
+ }
+
+ return $this->_datatree->remove($perm->name, $force);
+ }
+
+ /**
+ * Returns the unique identifier of this permission.
+ *
+ * @param Horde_Perms_Permission_DataTreeObject $perm The permission
+ * object to get the
+ * ID of.
+ *
+ * @return integer The unique id.
+ */
+ public function getPermissionId($permission)
+ {
+ return $this->_datatree->getId($permission->getName());
+ }
+
+ /**
+ * Checks if a permission exists in the system.
+ *
+ * @param string $permission The permission to check.
+ *
+ * @return boolean True if the permission exists.
+ */
+ public function exists($permission)
+ {
+ $key = 'perm_exists_' . $this->_cacheVersion . $permission;
+ $exists = $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
+ if ($exists === false) {
+ $exists = $this->_datatree->exists($permission);
+ $this->_cache->set($key, (string)$exists);
+ }
+
+ return (bool)$exists;
+ }
+
+ /**
+ * Returns a list of parent permissions.
+ *
+ * @param string $child The name of the child to retrieve parents for.
+ *
+ * @return array A hash with all parents in a tree format.
+ */
+ public function getParents($child)
+ {
+ return $this->_datatree->getParents($child);
+ }
+
+ /**
+ * Returns a child's direct parent ID.
+ *
+ * @param mixed $child Either the object, an array containing the
+ * path elements, or the object name for which
+ * to look up the parent's ID.
+ *
+ * @return mixed The unique ID of the parent or PEAR_Error on error.
+ */
+ public function getParent($child)
+ {
+ return $this->_datatree->getParent($child);
+ }
+
+ /**
+ * Returns all permissions of the system in a tree format.
+ *
+ * @return array A hash with all permissions in a tree format.
+ */
+ public function getTree()
+ {
+ return $this->_datatree->get(DATATREE_FORMAT_FLAT, Horde_Perms::ROOT, true);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Extension of the DataTreeObject class for storing Permission information in
+ * the DataTree driver. If you want to store specialized Permission
+ * information, you should extend this class instead of extending
+ * DataTreeObject directly.
+ *
+ * @TODO This class duplicates most of the functionality of the
+ * Horde_Permission class. However, because for BC/DataTree reasons it
+ * must extend DataTreeObject, we can't remove these methods yet.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package Horde_Perms
+ */
+class Horde_Perms_Permission_DataTreeObject extends DataTreeObject
+{
+ /**
+ * Cache object.
+ *
+ * @var Horde_Cache
+ */
+ protected $_cache;
+
+ /**
+ * Constructor. Just makes sure to call the parent constructor so that
+ * the perm's name is set properly.
+ *
+ * @param string $name The name of the perm.
+ * @param string $type The permission type.
+ * @param array $params A hash with any parameters that the permission
+ * type needs.
+ */
+ public function __construct($name, $type = 'matrix', $params = null)
+ {
+ parent::DataTreeObject($name);
+
+ $this->data['type'] = $type;
+ if (is_array($params)) {
+ $this->data['params'] = $params;
+ }
+ }
+
+ /**
+ * Don't store cache object on serialize().
+ *
+ * @return array List of variables to serialize.
+ */
+ public function __sleep()
+ {
+ return array_diff(array_keys(get_class_vars(__CLASS__)), array('_cache'));
+ }
+
+ /**
+ * Sets the cache instance in the object.
+ *
+ * @param Horde_Cache $cache The cache object.
+ */
+ public function setCacheOb(Horde_Cache $cache)
+ {
+ $this->_cache = $cache;
+ }
+
+ /**
+ * Gets one of the attributes of the object, or null if it isn't defined.
+ *
+ * @param string $attribute The attribute to get.
+ *
+ * @return mixed The value of the attribute, or null.
+ */
+ public function get($attribute)
+ {
+ $value = parent::get($attribute);
+
+ return (is_null($value) && $attribute == 'type')
+ ? 'matrix'
+ : $value;
+ }
+
+ /**
+ * Updates the permissions based on data passed in the array.
+ *
+ * @param array $perms An array containing the permissions which are to
+ * be updated.
+ */
+ public function updatePermissions($perms)
+ {
+ $type = $this->get('type');
+
+ if ($type == 'matrix') {
+ /* Array of permission types to iterate through. */
+ $perm_types = Horde_Perms::getPermsArray();
+ }
+
+ foreach ($perms as $perm_class => $perm_values) {
+ switch ($perm_class) {
+ case 'default':
+ case 'guest':
+ case 'creator':
+ if ($type == 'matrix') {
+ foreach ($perm_types as $val => $label) {
+ if (!empty($perm_values[$val])) {
+ $this->setPerm($perm_class, $val, false);
+ } else {
+ $this->unsetPerm($perm_class, $val, false);
+ }
+ }
+ } elseif (!empty($perm_values)) {
+ $this->setPerm($perm_class, $perm_values, false);
+ } else {
+ $this->unsetPerm($perm_class, null, false);
+ }
+ break;
+
+ case 'u':
+ case 'g':
+ $permId = array('class' => $perm_class == 'u' ? 'users' : 'groups');
+ /* Figure out what names that are stored in this permission
+ * class have not been submitted for an update, ie. have been
+ * removed entirely. */
+ $current_names = isset($this->data[$permId['class']])
+ ? array_keys($this->data[$permId['class']])
+ : array();
+ $updated_names = array_keys($perm_values);
+ $removed_names = array_diff($current_names, $updated_names);
+
+ /* Remove any names that have been completely unset. */
+ foreach ($removed_names as $name) {
+ unset($this->data[$permId['class']][$name]);
+ }
+
+ /* If nothing to actually update finish with this case. */
+ if (is_null($perm_values)) {
+ continue;
+ }
+
+ /* Loop through the names and update permissions for each. */
+ foreach ($perm_values as $name => $name_values) {
+ $permId['name'] = $name;
+
+ if ($type == 'matrix') {
+ foreach ($perm_types as $val => $label) {
+ if (!empty($name_values[$val])) {
+ $this->setPerm($permId, $val, false);
+ } else {
+ $this->unsetPerm($permId, $val, false);
+ }
+ }
+ } elseif (!empty($name_values)) {
+ $this->setPerm($permId, $name_values, false);
+ } else {
+ $this->unsetPerm($permId, null, false);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * TODO
+ */
+ public function setPerm($permId, $permission, $update = true)
+ {
+ if (is_array($permId)) {
+ if (empty($permId['name'])) {
+ return;
+ }
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data[$permId['class']][$permId['name']])) {
+ $this->data[$permId['class']][$permId['name']] |= $permission;
+ } else {
+ $this->data[$permId['class']][$permId['name']] = $permission;
+ }
+ } else {
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data[$permId])) {
+ $this->data[$permId] |= $permission;
+ } else {
+ $this->data[$permId] = $permission;
+ }
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * TODO
+ */
+ public function unsetPerm($permId, $permission, $update = true)
+ {
+ if (is_array($permId)) {
+ if (empty($permId['name'])) {
+ return;
+ }
+ if ($this->get('type') == 'matrix') {
+ if (isset($this->data[$permId['class']][$permId['name']])) {
+ $this->data[$permId['class']][$permId['name']] &= ~$permission;
+ if (empty($this->data[$permId['class']][$permId['name']])) {
+ unset($this->data[$permId['class']][$permId['name']]);
+ }
+ if ($update) {
+ $this->save();
+ }
+ }
+ } else {
+ unset($this->data[$permId['class']][$permId['name']]);
+ if ($update) {
+ $this->save();
+ }
+ }
+ } else {
+ if ($this->get('type') == 'matrix') {
+ if (isset($this->data[$permId])) {
+ $this->data[$permId] &= ~$permission;
+ if ($update) {
+ $this->save();
+ }
+ }
+ } else {
+ unset($this->data[$permId]);
+ if ($update) {
+ $this->save();
+ }
+ }
+ }
+ }
+
+ /**
+ * Grants a user additional permissions to this object.
+ *
+ * @param string $user The user to grant additional permissions
+ * to.
+ * @param integer $permission The permission (DELETE, etc.) to add.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function addUserPermission($user, $permission, $update = true)
+ {
+ if (empty($user)) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data['users'][$user])) {
+ $this->data['users'][$user] |= $permission;
+ } else {
+ $this->data['users'][$user] = $permission;
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Grants guests additional permissions to this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to add.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function addGuestPermission($permission, $update = true)
+ {
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data['guest'])) {
+ $this->data['guest'] |= $permission;
+ } else {
+ $this->data['guest'] = $permission;
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Grants creators additional permissions to this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to add.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function addCreatorPermission($permission, $update = true)
+ {
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data['creator'])) {
+ $this->data['creator'] |= $permission;
+ } else {
+ $this->data['creator'] = $permission;
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Grants additional default permissions to this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to add.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function addDefaultPermission($permission, $update = true)
+ {
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data['default'])) {
+ $this->data['default'] |= $permission;
+ } else {
+ $this->data['default'] = $permission;
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Grants a group additional permissions to this object.
+ *
+ * @param integer $groupId The id of the group to grant additional
+ * permissions to.
+ * @param integer $permission The permission (DELETE, etc.) to add.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function addGroupPermission($groupId, $permission, $update = true)
+ {
+ if (empty($groupId)) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix' &&
+ isset($this->data['groups'][$groupId])) {
+ $this->data['groups'][$groupId] |= $permission;
+ } else {
+ $this->data['groups'][$groupId] = $permission;
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Removes a permission that a user currently has on this object.
+ *
+ * @param string $user The user to remove the permission from.
+ * @param integer $permission The permission (DELETE, etc.) to
+ * remove.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function removeUserPermission($user, $permission, $update = true)
+ {
+ if (empty($user) || !isset($this->data['users'][$user])) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix') {
+ $this->data['users'][$user] &= ~$permission;
+ if (empty($this->data['users'][$user])) {
+ unset($this->data['users'][$user]);
+ }
+ } else {
+ unset($this->data['users'][$user]);
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Removes a permission that guests currently have on this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to remove.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function removeGuestPermission($permission, $update = true)
+ {
+ if (!isset($this->data['guest'])) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix') {
+ $this->data['guest'] &= ~$permission;
+ } else {
+ unset($this->data['guest']);
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Removes a permission that creators currently have on this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to remove.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function removeCreatorPermission($permission, $update = true)
+ {
+ if (!isset($this->data['creator'])) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix') {
+ $this->data['creator'] &= ~$permission;
+ } else {
+ unset($this->data['creator']);
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Removes a default permission on this object.
+ *
+ * @param integer $permission The permission (DELETE, etc.) to remove.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function removeDefaultPermission($permission, $update = true)
+ {
+ if (!isset($this->data['default'])) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix') {
+ $this->data['default'] &= ~$permission;
+ } else {
+ unset($this->data['default']);
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Removes a permission that a group currently has on this object.
+ *
+ * @param integer $groupId The id of the group to remove the
+ * permission from.
+ * @param integer $permission The permission (DELETE, etc.) to remove.
+ * @param boolean $update Whether to automatically update the
+ * backend.
+ */
+ public function removeGroupPermission($groupId, $permission,
+ $update = true)
+ {
+ if (empty($groupId) || !isset($this->data['groups'][$groupId])) {
+ return;
+ }
+
+ if ($this->get('type') == 'matrix') {
+ $this->data['groups'][$groupId] &= ~$permission;
+ if (empty($this->data['groups'][$groupId])) {
+ unset($this->data['groups'][$groupId]);
+ }
+ } else {
+ unset($this->data['groups'][$groupId]);
+ }
+
+ if ($update) {
+ $this->save();
+ }
+ }
+
+ /**
+ * Returns an array of all user permissions on this object.
+ *
+ * @param integer $perm List only users with this permission level.
+ * Defaults to all users.
+ *
+ * @return array All user permissions for this object, indexed by user.
+ */
+ public function getUserPermissions($perm = null)
+ {
+ if (!isset($this->data['users']) || !is_array($this->data['users'])) {
+ return array();
+ } elseif (!$perm) {
+ return $this->data['users'];
+ }
+
+ $users = array();
+ foreach ($this->data['users'] as $user => $uperm) {
+ if ($uperm & $perm) {
+ $users[$user] = $uperm;
+ }
+ }
+
+ return $users;
+ }
+
+ /**
+ * Returns the guest permissions on this object.
+ *
+ * @return integer The guest permissions on this object.
+ */
+ public function getGuestPermissions()
+ {
+ return empty($this->data['guest'])
+ ? null
+ : $this->data['guest'];
+ }
+
+ /**
+ * Returns the creator permissions on this object.
+ *
+ * @return integer The creator permissions on this object.
+ */
+ public function getCreatorPermissions()
+ {
+ return empty($this->data['creator'])
+ ? null
+ : $this->data['creator'];
+ }
+
+ /**
+ * Returns the default permissions on this object.
+ *
+ * @return integer The default permissions on this object.
+ */
+ public function getDefaultPermissions()
+ {
+ return empty($this->data['default'])
+ ? null
+ : $this->data['default'];
+ }
+
+ /**
+ * Returns an array of all group permissions on this object.
+ *
+ * @param integer $perm List only users with this permission level.
+ * Defaults to all users.
+ *
+ * @return array All group permissions for this object, indexed by group.
+ */
+ public function getGroupPermissions($perm = null)
+ {
+ if (!isset($this->data['groups']) ||
+ !is_array($this->data['groups'])) {
+ return array();
+ } elseif (!$perm) {
+ return $this->data['groups'];
+ }
+
+ $groups = array();
+ foreach ($this->data['groups'] as $group => $gperm) {
+ if ($gperm & $perm) {
+ $groups[$group] = $gperm;
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Saves any changes to this object to the backend permanently. New
+ * objects are added instead.
+ *
+ * @throws Horde_Perms_Exception
+ */
+ public function save()
+ {
+ $name = $this->getName();
+ if (empty($name)) {
+ throw new Horde_Perms_Exception('Permission names must be non-empty');
+ }
+
+ parent::save();
+
+ if ($this->_cache) {
+ $this->_cache->expire('perm_' . $this->_cacheVersion . $name);
+ $this->_cache->expire('perm_exists_' . $this->_cacheVersion . $name);
+ }
+ }
+
+}