From 2389d66da849798f8d4ec5f10e3b07c11da49185 Mon Sep 17 00:00:00 2001 From: Michael Francis Date: Sat, 28 May 2011 13:28:16 -0400 Subject: Initial Commit --- ACL.php | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 ACL.php (limited to 'ACL.php') diff --git a/ACL.php b/ACL.php new file mode 100644 index 0000000..aacd406 --- /dev/null +++ b/ACL.php @@ -0,0 +1,319 @@ +To Configure + + array( + 'adapter' => 'Permissions', + 'model' => 'app\models\Perms', + 'defaultNoUser' => array(), + 'defaultUser' => array( + 'route' => array( + 'users' => array( + 'logout', 'account' + ) + ) + ), + 'userIdentifier' => 'PrincipalID' + ) +)); + +Perms::applyFilter('find', function($self, $params, $chain) { + if($params['type'] != 'first') { + return $chain->next($self, $params, $chain); + } + $cacheKey = 'permissions_' . $params['options']['conditions']['id']; + $cache = Cache::read('default', $cacheKey); + if($cache) { + return $cache; + } + $result = $chain->next($self, $params, $chain); + Cache::write('default', $cacheKey, $result, '+1 day'); + return $result; +}); +?> + + 'app\models\perms', + 'defaultNoUser' => array(), + 'defaultUser' => array(), + 'userIdentifier' => 'id' + ); + parent::__construct($config + $defaults); + $this->_model = $this->_config['model']; + } + + /** + * @throws \lithium\core\ConfigException + * @param $user + * @param $request + * @param array $options + * @return array + */ + public function check($user, $request, array $options = array()) { + $config = $this->_config; + $model = $this->_model; + $params = compact('user', 'request', 'options'); + return $this->_filter(__METHOD__, $params, function($self, $params) use($config, $model) { + $user = $params['user']; + $request = $params['request']; + $options = $params['options']; + $reqIsObject = is_object($request); + $path = array(); + + switch (true) { + case $reqIsObject: + $path = array( + Permissions::pathRoute, + $request->params['controller'], + $request->params['action'] + ); + break; + case (!$reqIsObject && is_string($request)): + $path = explode('.', $request); + array_unshift($path, Permissions::pathCustom); + break; + case (!$reqIsObject && is_array($request)): + $path = $request; + break; + } + switch(true) { + case !$user: + $hasAccess = $self->_processPath($path, $config['defaultNoUser']); + return $hasAccess ? false : $options; + case ($result = $self->_processPath($path, $config['defaultUser'])): + return $result ? false : $options; + default: + $userId = $config['userIdentifier']; + if(!isset($user[$userId])) { + $message = "The user identifier '{$userId}' is not available."; + throw new ConfigException($message); + } + $perms = $model::find('first', array( + 'conditions' => array( + 'id' => $user[$userId] + ) + )); + if(!$perms) { + return false; + } + $userPath = unserialize($perms->perms); + $result = $self->_processPath($path, $userPath); + return $result ? array() : $options; + } + }); + } + + /** + * Adds a custom route to the users permission list. + * + * $customRoute is formatted as a dot path string, this is done as 'foo.bar.baz' for example. + * Asterisks are usable at the end of the path however not in the middle. A user with access + * to 'foo.bar.*' will have access to foo.bar.baz, foo.bar.aaa etc. + * + * @param $user + * @param $customRoute + * @return bool + */ + public function addCustomPath($user, $customRoute) { + if(!is_string($customRoute)) { + return false; + } + $parts = explode('.', $customRoute); + $value = array_pop($parts); + $parts = array_merge((array)self::pathCustom, $parts, (array)0); + return $this->add($user, Set::expand(array(implode('.', $parts) => $value))); + } + + /** + * Adds an action to the users permission list. If the action is set to * the user will have + * access to all of the controllers actions. + */ + public function addAction($user, $controller, $action) { + return $this->add($user, array( + self::pathRoute => array( + $controller => array( + $action + ) + ) + )); + } + + /** + * $user must contain the 'userIdentifier' key defined in config + * $paths are the paths which are to be added this is an array representation of the path and + * is from the origin, so 'route' or 'custom' must be specified. Multiple paths can be defined + * using this function + * + * @throws \lithium\core\ConfigException + * @param $user + * @param array $paths + * @return bool + */ + public function add($user, array $paths = array()) { + $model = $this->_model; + $userId = $this->_config['userIdentifier']; + $params = compact('model', 'userId', 'user', 'paths'); + return $this->_filter(__METHOD__, $params, function($self, $params) { + $model = $params['model']; + $userId = $params['userId']; + $user = $params['user']; + $paths = $params['paths']; + + if(!isset($user[$userId])) { + throw new ConfigException("The user identifier '{$userId}' is not available."); + } + $result = $model::find('first', array( + 'conditions' => array( + 'id' => $user[$userId] + ) + )); + if(!$result) { + $perms = $model::create(array( + 'id' => $user[$userId], + 'perms' => serialize($paths) + )); + return $perms->save(); + } + $allowedPaths = unserialize($result->perms); + $allowedPaths = array_merge_recursive($allowedPaths, $paths); + $result->perms = serialize($allowedPaths); + return $result->save(); + }); + } + + public function removeCustomPath($user, $customRoute) { + if(!is_string($customRoute)) { + return false; + } + $parts = explode('.', $customRoute); + $value = array_pop($parts); + $parts = array_merge((array)self::pathCustom, $parts, (array)0); + return $this->remove($user, Set::expand(array(implode('.', $parts) => $value))); + } + + /** + * Removes an action from a users permission list. Setting action to * removes all actions + * in the controller thus removing the controller from the users permission list. + */ + public function removeAction($user, $controller, $action) { + return $this->remove($user, array( + self::pathRoute => array( + $controller => array( + $action + ) + ) + )); + } + + /** + * use this to remove permissions from a user, multiple permissions can be defined in the paths + * array. The user must have the configured userIdentifier available. + * + * @throws \lithium\core\ConfigException + * @param $user + * @param array $paths + * @return bool + */ + public function remove($user, array $paths = array()) { + $model = $this->_model; + $userId = $this->_config['userIdentifier']; + $params = compact('model', 'userId', 'user', 'paths'); + return $this->_filter(__METHOD__, $params, function($self, $params) { + $model = $params['model']; + $userId = $params['userId']; + $user = $params['user']; + $paths = $params['paths']; + + if (!isset($user[$userId])) { + throw new ConfigException("The user identifier '{$userId}' is not available."); + } + $result = $model::find('first', array( + 'conditions' => array( + 'id' => $user[$userId] + ) + )); + if (!$result) { + return true; + } + $allowedPaths = unserialize($result->perms); + $pathsFlat = Set::flatten($paths); + foreach ($pathsFlat as $path => $value) { + $pointer = &$allowedPaths; + $pathParts = explode('.', $path); + array_pop($pathParts); + foreach ($pathParts as $pathPart) { + if (!isset($pointer[$pathPart])) { + unset($pointer); + $pointer = null; + break; + } + $pointer = &$pointer[$pathPart]; + } + switch(true) { + case !$pointer: + break; + case $value == '*': + $pointer = null; + break; + case (($index = array_search($value, $pointer)) !== false): + unset($pointer[$index]); + break; + } + } + $result->perms = serialize($self->_cleanPaths($allowedPaths)); + return $result->save(); + }); + } + + public function listPerms($user) { + $userId = $user[$this->_config['userIdentifier']]; + $model = $this->_model; + $result = $model::find('first', array( + 'conditions' => array( + 'id' => $userId + ) + )); + return $result ? unserialize($result->perms) : array(); + } + + public function _cleanPaths($paths) { + foreach ($paths as &$path) { + if (is_array($path)) { + $path = $this->_cleanPaths($path); + } + } + return array_filter($paths); + } + + public function _processPath($path, &$allowedPaths) { + $pointer = &$allowedPaths; + foreach($path as $item) { + switch(true) { + case (in_array('*', $pointer)): + return true; + case (in_array($item, $pointer)): + $pointer = array(); + continue; + case (!isset($pointer[$item])): + return false; + } + $pointer = &$pointer[$item]; + } + return true; + } +} + +?> \ No newline at end of file -- cgit v1.2.3