mirror of
https://github.com/Oreolek/aacl.git
synced 2024-06-29 03:14:57 +03:00
Numerous improvements.
- ORM implementation - Correct conroller name resolving (not via routes) - as in docs - renamed to ACL as in docs - SQL schema
This commit is contained in:
parent
2cef0e84af
commit
6ab2e7479d
9
acl.sql
Normal file
9
acl.sql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS `acl` (
|
||||||
|
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`role_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`resource` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`action` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`condition` VARCHAR(255) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `role_id` (`role_id`)
|
||||||
|
) ENGINE=INNODB;
|
292
classes/aacl.php
292
classes/aacl.php
|
@ -1,292 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Another ACL
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class AACL
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* All rules that apply to the currently logged in user
|
|
||||||
*
|
|
||||||
* @var array contains Model_AACL_Rule objects
|
|
||||||
*/
|
|
||||||
protected static $_rules;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grant access to $role for resource
|
|
||||||
*
|
|
||||||
* @param mixed string role name or Model_Role object
|
|
||||||
* @param string resource identifier
|
|
||||||
* @param string action [optional]
|
|
||||||
* @param string condition [optional]
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function grant($role, $resource, $action = NULL, $condition = NULL)
|
|
||||||
{
|
|
||||||
// Normalise $role
|
|
||||||
if ( ! $role instanceof Model_Role)
|
|
||||||
{
|
|
||||||
$role = Sprig::factory('role', array('name' => $role))->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check role exists
|
|
||||||
if ( ! $role->loaded())
|
|
||||||
{
|
|
||||||
throw new AACL_Exception('Unknown role :role passed to AACL::grant()',
|
|
||||||
array(':role' => $role->name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create rule
|
|
||||||
Sprig::factory('aacl_rule', array(
|
|
||||||
'role' => $role->id,
|
|
||||||
'resource' => $resource,
|
|
||||||
'action' => $action,
|
|
||||||
'condition' => $condition,
|
|
||||||
))->create();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Revoke access to $role for resource
|
|
||||||
*
|
|
||||||
* @param mixed string role name or Model_Role object
|
|
||||||
* @param string resource identifier
|
|
||||||
* @param string action [optional]
|
|
||||||
* @param string condition [optional]
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function revoke($role, $resource, $action = NULL, $condition = NULL)
|
|
||||||
{
|
|
||||||
// Normalise $role
|
|
||||||
if ( ! $role instanceof Model_Role)
|
|
||||||
{
|
|
||||||
$role = Sprig::factory('role', array('name' => $role))->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check role exists
|
|
||||||
if ( ! $role->loaded())
|
|
||||||
{
|
|
||||||
// Just return without deleting anything
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$model = Sprig::factory('aacl_rule', array(
|
|
||||||
'role' => $role->id,
|
|
||||||
));
|
|
||||||
|
|
||||||
if ($resource !== '*')
|
|
||||||
{
|
|
||||||
// Add normal reources, resource '*' will delete all rules for this role
|
|
||||||
$model->resource = $resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($resource !== '*' AND ! is_null($action))
|
|
||||||
{
|
|
||||||
$model->action = $action;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($resource !== '*' AND ! is_null($condition))
|
|
||||||
{
|
|
||||||
$model->condition = $condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete rule
|
|
||||||
$model->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks user has permission to access resource
|
|
||||||
*
|
|
||||||
* @param AACL_Resource AACL_Resource object being requested
|
|
||||||
* @param string action identifier [optional]
|
|
||||||
* @throw AACL_Exception To identify permission or authentication failure
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function check(AACL_Resource $resource, $action = NULL)
|
|
||||||
{
|
|
||||||
if ($user = Auth::instance()->get_user())
|
|
||||||
{
|
|
||||||
// User is logged in, check rules
|
|
||||||
$rules = self::_get_rules($user);
|
|
||||||
|
|
||||||
foreach ($rules as $rule)
|
|
||||||
{
|
|
||||||
if ($rule->allows_access_to($resource, $action))
|
|
||||||
{
|
|
||||||
// Access granted, just return
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No access rule matched
|
|
||||||
throw new AACL_Exception_403;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// User is not logged in and the need to be
|
|
||||||
throw new AACL_Exception_401;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all rules that apply to user
|
|
||||||
*
|
|
||||||
* @param Model_User $user
|
|
||||||
* @param bool [optional] Force reload from DB default FALSE
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected static function _get_rules(Model_User $user, $force_load = FALSE)
|
|
||||||
{
|
|
||||||
if ( ! isset(self::$_rules) OR $force_load)
|
|
||||||
{
|
|
||||||
// Get rule model instance
|
|
||||||
$model = Sprig::factory('aacl_rule');
|
|
||||||
|
|
||||||
self::$_rules = Sprig::factory('aacl_rule')
|
|
||||||
->load(DB::select()
|
|
||||||
// Select all rules that apply to any of the user's roles
|
|
||||||
->where($model->field('role')->column, 'IN', $user->roles->as_array(NULL, 'id'))
|
|
||||||
// Order by resource length as this will mostly mean that
|
|
||||||
// Less specific rules come first making the checking quicker
|
|
||||||
->order_by('LENGTH("'.$model->field('resource')->column.'")', 'ASC')
|
|
||||||
, FALSE)->as_array();
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$_rules;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static $_resources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all valid resource objects based on the filesstem adn reflection
|
|
||||||
*
|
|
||||||
* @param mixed string resource_id [optional] if provided, the info for that specific resource ID is returned,
|
|
||||||
* if TRUE a flat array of just the ids is returned
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function list_resources($resource_id = FALSE)
|
|
||||||
{
|
|
||||||
if ( ! isset(self::$_resources))
|
|
||||||
{
|
|
||||||
// Find all classes in the application and modules
|
|
||||||
$classes = self::_list_classes();
|
|
||||||
|
|
||||||
// Loop throuch classes and see if they implement AACL_Resource
|
|
||||||
foreach ($classes as $i => $class_name)
|
|
||||||
{
|
|
||||||
$class = new ReflectionClass($class_name);
|
|
||||||
|
|
||||||
if ($class->implementsInterface('AACL_Resource'))
|
|
||||||
{
|
|
||||||
// Ignore interfaces
|
|
||||||
if ($class->isInterface())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore abstract classes
|
|
||||||
if ($class->isAbstract())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an instance of the class
|
|
||||||
$resource = $class->getMethod('acl_instance')->invoke($class_name, $class_name);
|
|
||||||
|
|
||||||
// Get resource info
|
|
||||||
self::$_resources[$resource->acl_id()] = array(
|
|
||||||
'actions' => $resource->acl_actions(),
|
|
||||||
'conditions' => $resource->acl_conditions(),
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($resource_id === TRUE)
|
|
||||||
{
|
|
||||||
return array_keys(self::$_resources);
|
|
||||||
}
|
|
||||||
elseif ($resource_id)
|
|
||||||
{
|
|
||||||
return isset(self::$_resources[$resource_id]) ? self::$_resources[$resource_id] : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$_resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function _list_classes($files = NULL)
|
|
||||||
{
|
|
||||||
if (is_null($files))
|
|
||||||
{
|
|
||||||
// Remove core module paths form search
|
|
||||||
$loaded_modules = Kohana::modules();
|
|
||||||
|
|
||||||
$exclude_modules = array('database', 'orm', 'sprig', 'auth', 'sprig-auth',
|
|
||||||
'userguide', 'image', 'codebench', 'unittest', 'pagination');
|
|
||||||
|
|
||||||
$paths = Kohana::include_paths();
|
|
||||||
|
|
||||||
// Remove known core module paths
|
|
||||||
foreach ($loaded_modules as $module => $path)
|
|
||||||
{
|
|
||||||
if (in_array($module, $exclude_modules))
|
|
||||||
{
|
|
||||||
unset($paths[array_search($path.DIRECTORY_SEPARATOR, $paths)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove system path
|
|
||||||
unset($paths[array_search(SYSPATH, $paths)]);
|
|
||||||
|
|
||||||
$files = Kohana::list_files('classes', $paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classes = array();
|
|
||||||
|
|
||||||
foreach ($files as $name => $path)
|
|
||||||
{
|
|
||||||
if (is_array($path))
|
|
||||||
{
|
|
||||||
$classes = array_merge($classes, self::_list_classes($path));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Strip 'classes/' off start
|
|
||||||
$name = substr($name, 8);
|
|
||||||
|
|
||||||
// Strip '.php' off end
|
|
||||||
$name = substr($name, 0, 0 - strlen(EXT));
|
|
||||||
|
|
||||||
// Convert to class name
|
|
||||||
$classes[] = str_replace(DIRECTORY_SEPARATOR, '_', $name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force static access
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function __construct() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force static access
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function __clone() {}
|
|
||||||
|
|
||||||
} // End AACL
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base AACL exception
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class AACL_Exception extends Kohana_Exception {}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 401 "User requires authentication" exception
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class AACL_Exception_401 extends AACL_Exception
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct('Authentication Required');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 403 "Permission denied" exception
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class AACL_Exception_403 extends AACL_Exception
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct('Permission Denied');
|
|
||||||
}
|
|
||||||
}
|
|
417
classes/acl.php
Normal file
417
classes/acl.php
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Another ACL
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses ORM
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class ACL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All rules that apply to the currently logged in user
|
||||||
|
*
|
||||||
|
* @var array contains Model_ACL_Rule objects
|
||||||
|
*/
|
||||||
|
protected static $_rules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $_resources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently logged in user
|
||||||
|
*
|
||||||
|
* @return Model_User logged in user's instance or NULL pointer
|
||||||
|
* @return NULL
|
||||||
|
*/
|
||||||
|
public static function get_loggedin_user()
|
||||||
|
{
|
||||||
|
return Auth::instance()->get_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grant access to $role for resource
|
||||||
|
*
|
||||||
|
* @param string|Model_Role $role string role name or Model_Role object [optional]
|
||||||
|
* @param string $resource resource identifier [optional]
|
||||||
|
* @param string $action action [optional]
|
||||||
|
* @param string $condition condition [optional]
|
||||||
|
* @throws ACL_Exception
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function grant($role = NULL, $resource = NULL, $action = NULL, $condition = NULL)
|
||||||
|
{
|
||||||
|
// if $role is null — we grant this to everyone
|
||||||
|
if ( ! is_null($role))
|
||||||
|
{
|
||||||
|
// Normalize $role
|
||||||
|
$role = ACL::normalize_role($role);
|
||||||
|
|
||||||
|
// Check role exists
|
||||||
|
if ( ! $role->loaded())
|
||||||
|
{
|
||||||
|
throw new ACL_Exception('Unknown role :role passed to ACL::grant()',
|
||||||
|
array(':role' => $role->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create rule
|
||||||
|
ACL::create_rule(
|
||||||
|
array(
|
||||||
|
'role_id' => $role,
|
||||||
|
'resource' => $resource,
|
||||||
|
'action' => $action,
|
||||||
|
'condition' => $condition,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke access to $role for resource
|
||||||
|
* CHANGED: now accepts NULL role
|
||||||
|
*
|
||||||
|
* @param string|Model_Role $role role name or Model_Role object [optional]
|
||||||
|
* @param string $resource resource identifier [optional]
|
||||||
|
* @param string $action action [optional]
|
||||||
|
* @param string $condition condition [optional]
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function revoke($role = NULL, $resource = NULL, $action = NULL, $condition = NULL)
|
||||||
|
{
|
||||||
|
$model = ORM::factory('ACL_Rule');
|
||||||
|
|
||||||
|
if (is_null($role))
|
||||||
|
{
|
||||||
|
$model->where('role_id', 'IS', NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normalize $role
|
||||||
|
$role = ACL::normalize_role($role);
|
||||||
|
|
||||||
|
// Check role exists
|
||||||
|
if ( ! $role->loaded())
|
||||||
|
{
|
||||||
|
// Just return without deleting anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model->where('role_id', '=', $role->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_null($resource))
|
||||||
|
{
|
||||||
|
// Add normal resources, resource NULL will delete all rules
|
||||||
|
$model->and_where('resource', '=', $resource);
|
||||||
|
|
||||||
|
if ( ! is_null($action))
|
||||||
|
{
|
||||||
|
$model->and_where('action', '=', $action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_null($condition))
|
||||||
|
{
|
||||||
|
$model->and_where('condition', '=', $condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = $model->find_all();
|
||||||
|
|
||||||
|
foreach ($rules as $rule)
|
||||||
|
{
|
||||||
|
$rule->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method, that allows to check any rule from database in any place of project.
|
||||||
|
* Works with string presentations of resources, actions, roles and conditions
|
||||||
|
*
|
||||||
|
* @param ACL_Resource $resource
|
||||||
|
* @param string $action
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function access(ACL_Resource $resource, $action = NULL)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ACL::check($resource, $action);
|
||||||
|
}
|
||||||
|
catch (ACL_Exception $e)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks user has permission to access resource
|
||||||
|
* works with unauthenticated users (role_id = NULL)
|
||||||
|
*
|
||||||
|
* @param ACL_Resource $resource ACL_Resource object being requested
|
||||||
|
* @param string $action action identifier [optional]
|
||||||
|
* @throws ACL_Exception_401 To identify permission or authentication failure
|
||||||
|
* @throws ACL_Exception_403 To identify permission or authentication failure
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function check(ACL_Resource $resource, $action = NULL)
|
||||||
|
{
|
||||||
|
$user = ACL::get_loggedin_user();
|
||||||
|
|
||||||
|
// User is logged in, check rules
|
||||||
|
$rules = ACL::_get_rules($user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Model_ACL_Rule $rule
|
||||||
|
*/
|
||||||
|
foreach ($rules as $rule)
|
||||||
|
{
|
||||||
|
if ($rule->allows_access_to($resource, $action, $user))
|
||||||
|
{
|
||||||
|
// Access granted, just return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No access rule matched
|
||||||
|
if ($user)
|
||||||
|
{
|
||||||
|
throw new ACL_Exception_403;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ACL_Exception_401;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Almost the same as check() but doesn't throw exceptions and answer is boolean
|
||||||
|
*
|
||||||
|
* @param ACL_Resource $resource ACL_Resource object being requested
|
||||||
|
* @param string $action action identifier [optional]
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function check_if(ACL_Resource $resource, $action = NULL)
|
||||||
|
{
|
||||||
|
$user = ACL::get_loggedin_user();
|
||||||
|
|
||||||
|
// User is logged in, check rules
|
||||||
|
$rules = ACL::_get_rules($user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Model_ACL_Rule $rule
|
||||||
|
*/
|
||||||
|
foreach ($rules as $rule)
|
||||||
|
{
|
||||||
|
if ($rule->allows_access_to($resource, $action, $user))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an ACL rule
|
||||||
|
*
|
||||||
|
* @param array $fields optional fields' values
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function create_rule(array $fields = array())
|
||||||
|
{
|
||||||
|
ORM::factory('ACL_Rule')->values($fields)->create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all rules that apply to user
|
||||||
|
*
|
||||||
|
* CHANGED
|
||||||
|
*
|
||||||
|
* @param mixed $user Model_User|Model_Role|bool User, role or everyone
|
||||||
|
* @param bool $force_load [optional] Force reload from DB default FALSE
|
||||||
|
* @return Database_Result
|
||||||
|
*/
|
||||||
|
public static function _get_rules($user = FALSE, $force_load = FALSE)
|
||||||
|
{
|
||||||
|
if ( ! isset(ACL::$_rules) || $force_load)
|
||||||
|
{
|
||||||
|
$select_query = ORM::factory('ACL_Rule')
|
||||||
|
// User is guest
|
||||||
|
->where('role_id', '=', NULL);
|
||||||
|
|
||||||
|
// Get rules for user
|
||||||
|
if ($user instanceof Model_User and $user->loaded())
|
||||||
|
{
|
||||||
|
ACL::$_rules = $select_query->or_where('role_id', 'IN', $user->roles->find_all()->as_array());
|
||||||
|
}
|
||||||
|
// Get rules for role
|
||||||
|
elseif ($user instanceof Model_Role and $user->loaded())
|
||||||
|
{
|
||||||
|
ACL::$_rules = $select_query->or_where('role_id', '=', $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ACL::$_rules = $select_query
|
||||||
|
->order_by('LENGTH("resource")', 'ASC')
|
||||||
|
->find_all()->as_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ACL::$_rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all valid resource objects based on the filesstem adn
|
||||||
|
* FIXED
|
||||||
|
*
|
||||||
|
* @param string|bool string resource_id [optional] if provided, the info for that specific resource ID is returned,
|
||||||
|
* if TRUE a flat array of just the ids is returned
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function list_resources($resource_id = FALSE)
|
||||||
|
{
|
||||||
|
if ( ! isset(ACL::$_resources))
|
||||||
|
{
|
||||||
|
// Find all classes in the application and modules
|
||||||
|
$classes = ACL::_list_classes();
|
||||||
|
|
||||||
|
// Loop through classes and see if they implement ACL_Resource
|
||||||
|
foreach ($classes as $class_name)
|
||||||
|
{
|
||||||
|
$class = new ReflectionClass($class_name);
|
||||||
|
|
||||||
|
if ($class->implementsInterface('ACL_Resource'))
|
||||||
|
{
|
||||||
|
// Ignore interfaces and abstract classes
|
||||||
|
if ($class->isInterface() || $class->isAbstract())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an instance of the class
|
||||||
|
$resource = $class->getMethod('acl_instance')->invoke($class_name, $class_name);
|
||||||
|
|
||||||
|
// Get resource info
|
||||||
|
ACL::$_resources[$resource->acl_id()] = array(
|
||||||
|
'actions' => $resource->acl_actions(),
|
||||||
|
'conditions' => $resource->acl_conditions(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($resource_id === TRUE)
|
||||||
|
{
|
||||||
|
return array_keys(ACL::$_resources);
|
||||||
|
}
|
||||||
|
elseif ($resource_id)
|
||||||
|
{
|
||||||
|
return isset(ACL::$_resources[$resource_id]) ? ACL::$_resources[$resource_id] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ACL::$_resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _list_classes($files = NULL)
|
||||||
|
{
|
||||||
|
if (is_null($files))
|
||||||
|
{
|
||||||
|
// Remove core module paths form search
|
||||||
|
$loaded_modules = Kohana::modules();
|
||||||
|
|
||||||
|
$exclude_modules = array(
|
||||||
|
'database',
|
||||||
|
'orm',
|
||||||
|
'auth',
|
||||||
|
'userguide',
|
||||||
|
'image',
|
||||||
|
'codebench',
|
||||||
|
'unittest',
|
||||||
|
'pagination',
|
||||||
|
'cache',
|
||||||
|
);
|
||||||
|
|
||||||
|
$paths = Kohana::include_paths();
|
||||||
|
|
||||||
|
// Remove known core module paths
|
||||||
|
foreach ($loaded_modules as $module => $path)
|
||||||
|
{
|
||||||
|
if (in_array($module, $exclude_modules))
|
||||||
|
{
|
||||||
|
// Doesn't works properly — double slash on the end
|
||||||
|
// unset($paths[array_search($path.DIRECTORY_SEPARATOR, $paths)]);
|
||||||
|
unset($paths[array_search($path, $paths)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove system path
|
||||||
|
unset($paths[array_search(SYSPATH, $paths)]);
|
||||||
|
$files = array_merge(Kohana::list_files('classes'.DIRECTORY_SEPARATOR.'controller', $paths), Kohana::list_files('classes'.DIRECTORY_SEPARATOR.'model', $paths));
|
||||||
|
}
|
||||||
|
|
||||||
|
$classes = array();
|
||||||
|
|
||||||
|
foreach ($files as $name => $path)
|
||||||
|
{
|
||||||
|
if (is_array($path))
|
||||||
|
{
|
||||||
|
$classes = array_merge($classes, ACL::_list_classes($path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Strip 'classes/' off start
|
||||||
|
$name = substr($name, 8);
|
||||||
|
|
||||||
|
// Strip '.php' off end
|
||||||
|
$name = substr($name, 0, 0 - strlen(EXT));
|
||||||
|
|
||||||
|
// Convert to class name
|
||||||
|
$classes[] = str_replace(DIRECTORY_SEPARATOR, '_', $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize role
|
||||||
|
*
|
||||||
|
* @param Model_Role|string $role role instance or role identifier
|
||||||
|
*
|
||||||
|
* @return Model_Role role instance
|
||||||
|
*/
|
||||||
|
protected static function normalize_role($role)
|
||||||
|
{
|
||||||
|
if ( ! $role instanceof Model_Role)
|
||||||
|
{
|
||||||
|
return ORM::factory('role')->where('name', '=', $role)->find();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force static access
|
||||||
|
*/
|
||||||
|
protected function __construct() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force static access
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function __clone() {}
|
||||||
|
|
||||||
|
} // End ACL_Core
|
14
classes/acl/exception.php
Normal file
14
classes/acl/exception.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base ACL exception
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses Sprig
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class ACL_Exception extends HTTP_Exception {}
|
21
classes/acl/exception/401.php
Normal file
21
classes/acl/exception/401.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 401 "User requires authentication" exception
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses Sprig
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class ACL_Exception_401 extends ACL_Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer HTTP 401 Unauthorized
|
||||||
|
*/
|
||||||
|
protected $_code = 401;
|
||||||
|
|
||||||
|
}
|
21
classes/acl/exception/403.php
Normal file
21
classes/acl/exception/403.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 403 "Permission denied" exception
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses Sprig
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class ACL_Exception_403 extends ACL_Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer HTTP 401 Unauthorized
|
||||||
|
*/
|
||||||
|
protected $_code = 403;
|
||||||
|
|
||||||
|
}
|
|
@ -1,25 +1,25 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AACL Resource interface
|
* ACL Resource interface
|
||||||
*
|
*
|
||||||
* @see http://github.com/banks/aacl
|
* @see http://github.com/banks/aacl
|
||||||
* @package AACL
|
* @package ACL
|
||||||
* @uses Auth
|
* @uses Auth
|
||||||
* @uses Sprig
|
* @uses Sprig
|
||||||
* @author Paul Banks
|
* @author Paul Banks
|
||||||
* @copyright (c) Paul Banks 2010
|
* @copyright (c) Paul Banks 2010
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
interface AACL_Resource
|
interface ACL_Resource {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Gets a unique ID string for this resource
|
* Gets a unique ID string for this resource
|
||||||
*
|
*
|
||||||
* Convention for controllers is c:controller_name
|
* Convention for controllers is c:controller_name
|
||||||
* Convention for models is m:model_name.primary_key_value
|
* Convention for models is m:model_name.primary_key_value
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function acl_id();
|
public function acl_id();
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ interface AACL_Resource
|
||||||
*
|
*
|
||||||
* If $return_current is TRUE, return value should be the currently requested action or NULL if not known.
|
* If $return_current is TRUE, return value should be the currently requested action or NULL if not known.
|
||||||
*
|
*
|
||||||
* @param bool $return_current [optional]
|
* @param bool $return_current [optional]
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function acl_actions($return_current = FALSE);
|
public function acl_actions($return_current = FALSE);
|
||||||
|
|
||||||
|
@ -46,9 +46,11 @@ interface AACL_Resource
|
||||||
*
|
*
|
||||||
* return array('condition_id' => 'User friendly description of condition');
|
* return array('condition_id' => 'User friendly description of condition');
|
||||||
*
|
*
|
||||||
* @param Model_User $user [optional] logged in user model
|
* Objects which implement this interface should check the first parameter if an instance of Model_User
|
||||||
* @param object $condition [optional] condition to test
|
*
|
||||||
* @return mixed
|
* @param Model_User $user [optional] logged in user model
|
||||||
|
* @param object $condition [optional] condition to test
|
||||||
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function acl_conditions(Model_User $user = NULL, $condition = NULL);
|
public function acl_conditions(Model_User $user = NULL, $condition = NULL);
|
||||||
|
|
||||||
|
@ -57,9 +59,9 @@ interface AACL_Resource
|
||||||
*
|
*
|
||||||
* Note that the object instance returned should not be used for anything except querying the acl_* methods
|
* Note that the object instance returned should not be used for anything except querying the acl_* methods
|
||||||
*
|
*
|
||||||
* @param string Class name of object required
|
* @param string Class name of object required
|
||||||
* @return Object
|
* @return Object
|
||||||
*/
|
*/
|
||||||
public static function acl_instance($class_name);
|
public static function acl_instance($class_name);
|
||||||
|
|
||||||
} // End AACL_Resource
|
} // End ACL_Resource
|
|
@ -1,104 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for access controlled controllers
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
abstract class Controller_AACL extends Controller_Template implements AACL_Resource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* AACL_Resource::acl_id() implementation
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function acl_id()
|
|
||||||
{
|
|
||||||
// Controller namespace, controller name
|
|
||||||
return 'c:'.strtolower($this->request->controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AACL_Resource::acl_actions() implementation
|
|
||||||
*
|
|
||||||
* @param bool $return_current [optional]
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function acl_actions($return_current = FALSE)
|
|
||||||
{
|
|
||||||
if ($return_current)
|
|
||||||
{
|
|
||||||
return $this->request->action;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all actions in this class
|
|
||||||
$reflection = new ReflectionClass($this);
|
|
||||||
|
|
||||||
$actions = array();
|
|
||||||
|
|
||||||
// Add all public methods that start with 'action_'
|
|
||||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
|
|
||||||
{
|
|
||||||
if (substr($method->name, 0, 7) === 'action_')
|
|
||||||
{
|
|
||||||
$actions[] = substr($method->name, 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AACL_Resource::acl_conditions() implementation
|
|
||||||
*
|
|
||||||
* @param Model_User $user [optional] logged in user model
|
|
||||||
* @param object $condition [optional] condition to test
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function acl_conditions(Model_User $user = NULL, $condition = NULL)
|
|
||||||
{
|
|
||||||
if (is_null($user) AND is_null($condition))
|
|
||||||
{
|
|
||||||
// We have no conditions
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We have no conditions so this test should fail!
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AACL_Resource::acl_instance() implementation
|
|
||||||
*
|
|
||||||
* Note that the object instance returned should not be used for anything except querying the acl_* methods
|
|
||||||
*
|
|
||||||
* @param string Class name of object required
|
|
||||||
* @return Object
|
|
||||||
*/
|
|
||||||
public static function acl_instance($class_name)
|
|
||||||
{
|
|
||||||
// Return controller instance populated with manipulated request details
|
|
||||||
$instance = new $class_name(Request::instance());
|
|
||||||
|
|
||||||
$controller_name = strtolower(substr($class_name, 11));
|
|
||||||
|
|
||||||
if ($controller_name !== Request::instance()->controller)
|
|
||||||
{
|
|
||||||
// Manually override controller name and action
|
|
||||||
$instance->request->controller = strtolower(substr(get_class($this), 11));
|
|
||||||
|
|
||||||
$instance->request->action = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End Controller_AACL
|
|
105
classes/controller/acl.php
Normal file
105
classes/controller/acl.php
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for access controlled controllers
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses Sprig
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class Controller_ACL extends Controller implements ACL_Resource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_id() implementation
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function acl_id()
|
||||||
|
{
|
||||||
|
// Controller namespace, controller name
|
||||||
|
$class_name = get_class($this);
|
||||||
|
$class_name = str_replace('Controller_', '', $class_name);
|
||||||
|
return 'c:'.strtolower($class_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_actions() implementation
|
||||||
|
*
|
||||||
|
* @param bool $return_current [optional]
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function acl_actions($return_current = FALSE)
|
||||||
|
{
|
||||||
|
if ($return_current)
|
||||||
|
{
|
||||||
|
return $this->request->action();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all actions in this class
|
||||||
|
$reflection = new ReflectionClass($this);
|
||||||
|
|
||||||
|
$actions = array();
|
||||||
|
|
||||||
|
// Add all public methods that start with 'action_'
|
||||||
|
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
|
||||||
|
{
|
||||||
|
if (substr($method->name, 0, 7) === 'action_')
|
||||||
|
{
|
||||||
|
$actions[] = substr($method->name, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_conditions() implementation
|
||||||
|
*
|
||||||
|
* @param Model_User $user [optional] logged in user model
|
||||||
|
* @param string $condition [optional] condition to test
|
||||||
|
* @throws ACL_Exception
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function acl_conditions(Model_User $user = NULL, $condition = NULL)
|
||||||
|
{
|
||||||
|
if (is_null($user) AND is_null($condition))
|
||||||
|
{
|
||||||
|
// We have no conditions
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have no conditions so this test should fail!
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_instance() implementation
|
||||||
|
*
|
||||||
|
* Note that the object instance returned should not be used for anything except querying the acl_* methods
|
||||||
|
*
|
||||||
|
* @param string $class_name Class name of object required
|
||||||
|
* @return Object
|
||||||
|
*/
|
||||||
|
public static function acl_instance($class_name)
|
||||||
|
{
|
||||||
|
// Return controller instance populated with manipulated request details
|
||||||
|
$instance = new $class_name(Request::current(), Response::factory());
|
||||||
|
// Remove "controller_" part from name
|
||||||
|
$controller_name = strtolower(substr($class_name, 11));
|
||||||
|
|
||||||
|
if ($controller_name !== Request::current()->controller())
|
||||||
|
{
|
||||||
|
// Manually override controller name and action
|
||||||
|
$instance->request->controller(strtolower($controller_name));
|
||||||
|
|
||||||
|
$instance->request->action('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Controller_ACL_Core
|
|
@ -1,169 +0,0 @@
|
||||||
<?php defined('SYSPATH') or die ('No direct script access.');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access rule model
|
|
||||||
*
|
|
||||||
* @see http://github.com/banks/aacl
|
|
||||||
* @package AACL
|
|
||||||
* @uses Auth
|
|
||||||
* @uses Sprig
|
|
||||||
* @author Paul Banks
|
|
||||||
* @copyright (c) Paul Banks 2010
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class Model_AACL_Rule extends Sprig_AACL
|
|
||||||
{
|
|
||||||
protected function _init()
|
|
||||||
{
|
|
||||||
$this->_fields += array(
|
|
||||||
'id' => new Sprig_Field_Auto,
|
|
||||||
'role' => new Sprig_Field_BelongsTo(array(
|
|
||||||
'model' => 'role',
|
|
||||||
)),
|
|
||||||
'resource' => new Sprig_Field_Char(array(
|
|
||||||
'max_length' => 45,
|
|
||||||
'null' => FALSE,
|
|
||||||
)),
|
|
||||||
'action' => new Sprig_Field_Char(array(
|
|
||||||
'max_length' => 25,
|
|
||||||
'null' => TRUE,
|
|
||||||
)),
|
|
||||||
'condition' => new Sprig_Field_Char(array(
|
|
||||||
'max_length' => 25,
|
|
||||||
'null' => TRUE,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if rule matches current request
|
|
||||||
*
|
|
||||||
* @param AACL_Resource AACL_Resource object that user requested access to
|
|
||||||
* @param string action requested [optional]
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public function allows_access_to(AACL_Resource $resource, $action = NULL)
|
|
||||||
{
|
|
||||||
if ($this->resource === '*')
|
|
||||||
{
|
|
||||||
// No point checking anything else!
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($action))
|
|
||||||
{
|
|
||||||
// Check to see if Resource whats to define it's own action
|
|
||||||
$action = $resource->acl_actions(TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get string id
|
|
||||||
$resource_id = $resource->acl_id();
|
|
||||||
|
|
||||||
// Make sure action matches
|
|
||||||
if ( ! is_null($action) AND ! is_null($this->action) AND $action !== $this->action)
|
|
||||||
{
|
|
||||||
// This rule has a specific action and it doesn't match the specific one passed
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$matches = FALSE;
|
|
||||||
|
|
||||||
// Make sure rule resource is the same as requested resource, or is an ancestor
|
|
||||||
while( ! $matches)
|
|
||||||
{
|
|
||||||
// Attempt match
|
|
||||||
if ($this->resource === $resource_id)
|
|
||||||
{
|
|
||||||
// Stop loop
|
|
||||||
$matches = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Find last occurence of '.' separator
|
|
||||||
$last_dot_pos = strrpos($resource_id, '.');
|
|
||||||
|
|
||||||
if ($last_dot_pos !== FALSE)
|
|
||||||
{
|
|
||||||
// This rule might match more generally, try the next level of specificity
|
|
||||||
$resource_id = substr($resource_id, 0, $last_dot_pos);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We can't make this any more general as there are no more dots
|
|
||||||
// And we haven't managed to match the resource requested
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we know this rule matches the resource, check any match condition
|
|
||||||
if ( ! is_null($this->condition) AND ! $resource->acl_conditions(Auth::instance()->get_user(), $this->condition))
|
|
||||||
{
|
|
||||||
// Condition wasn't met (or doesn't exist)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All looks rosy!
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override create to remove less specific rules when creating a rule
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function create()
|
|
||||||
{
|
|
||||||
// Delete all more specifc rules for this role
|
|
||||||
$delete = DB::delete($this->_table)
|
|
||||||
->where($this->_fields['role']->column, '=', $this->_changed['role']);
|
|
||||||
|
|
||||||
// If resource is '*' we don't need any more rules - we just delete every rule for this role
|
|
||||||
|
|
||||||
if ($this->resource !== '*')
|
|
||||||
{
|
|
||||||
// Need to restrict to roles with equal or more specific resource id
|
|
||||||
$delete->where_open()
|
|
||||||
->where($this->_fields['resource']->column, '=', $this->resource)
|
|
||||||
->or_where($this->_fields['resource']->column, 'LIKE', $this->resource.'.%')
|
|
||||||
->where_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! is_null($this->action))
|
|
||||||
{
|
|
||||||
// If this rule has an action, only remove other rules with the same action
|
|
||||||
$delete->where($this->_fields['action']->column, '=', $this->action);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! is_null($this->condition))
|
|
||||||
{
|
|
||||||
// If this rule has a condition, only remove other rules with the same condition
|
|
||||||
$delete->where($this->_fields['condition']->column, '=', $this->condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the delete
|
|
||||||
$delete->execute($this->_db);
|
|
||||||
|
|
||||||
// Create new rule
|
|
||||||
parent::create();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override Default model actions
|
|
||||||
*
|
|
||||||
* @param bool $return_current [optional]
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function acl_actions($return_current = FALSE)
|
|
||||||
{
|
|
||||||
if ($return_current)
|
|
||||||
{
|
|
||||||
// We don't know anything about what the user intends to do with us!
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return default model actions
|
|
||||||
return array('grant', 'revoke');
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End Model_AACL_Rule
|
|
209
classes/model/acl/rule.php
Normal file
209
classes/model/acl/rule.php
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access rule model
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses ORM
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
class Model_ACL_Rule extends ORM_ACL {
|
||||||
|
|
||||||
|
protected static $_acl_actions = array(
|
||||||
|
'grant',
|
||||||
|
'revoke',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override Default model actions
|
||||||
|
*/
|
||||||
|
protected static $_acl_orm_actions = array();
|
||||||
|
|
||||||
|
protected $_table_name = 'acl';
|
||||||
|
|
||||||
|
protected $_primary_key = 'id';
|
||||||
|
|
||||||
|
protected $_table_columns = array(
|
||||||
|
'id' => array('type' => 'int'),
|
||||||
|
'role_id' => array('type' => 'int', 'null' => TRUE),
|
||||||
|
'resource' => array('type' => 'varchar'),
|
||||||
|
'action' => array('type' => 'varchar'),
|
||||||
|
'condition' => array('type' => 'varchar'),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected $_belongs_to = array(
|
||||||
|
'role' => array(
|
||||||
|
'model' => 'Role',
|
||||||
|
'foreign_key' => 'role_id',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: validation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL action
|
||||||
|
* grant access / create rule
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return $this
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function grant(array $data)
|
||||||
|
{
|
||||||
|
if ($this->loaded())
|
||||||
|
{
|
||||||
|
throw new Exception('called grant on loaded rule');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->values($data);
|
||||||
|
$this->check();
|
||||||
|
ACL::grant($this->role, $this->resource, $this->action, $this->condition);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL action
|
||||||
|
* revoke access / delete rule
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function revoke()
|
||||||
|
{
|
||||||
|
if ( ! $this->loaded())
|
||||||
|
{
|
||||||
|
throw new Exception('rule doesn\'t exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
ACL::revoke($this->role, $this->resource, $this->action, $this->condition);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if rule matches current request
|
||||||
|
*
|
||||||
|
* @param ACL_Resource $resource ACL_Resource object or it's id that user requested access to
|
||||||
|
* @param string $action action requested [optional]
|
||||||
|
* @param Model_User $user ACL instance
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function allows_access_to(ACL_Resource $resource, $action = NULL, Model_User $user = NULL)
|
||||||
|
{
|
||||||
|
if (empty($this->resource))
|
||||||
|
{
|
||||||
|
// No point checking anything else!
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($action))
|
||||||
|
{
|
||||||
|
// Check to see if Resource wants to define it's own action
|
||||||
|
$action = $resource->acl_actions(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure action matches
|
||||||
|
if ( ! is_null($action) AND ! empty($this->action) AND $action !== $this->action)
|
||||||
|
{
|
||||||
|
// This rule has a specific action and it doesn't match the specific one passed
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resource_id = $resource->acl_id();
|
||||||
|
|
||||||
|
$matches = FALSE;
|
||||||
|
|
||||||
|
// Make sure rule resource is the same as requested resource, or is an ancestor
|
||||||
|
while ( ! $matches)
|
||||||
|
{
|
||||||
|
// Attempt match
|
||||||
|
if ($this->resource === $resource_id)
|
||||||
|
{
|
||||||
|
// Stop loop
|
||||||
|
$matches = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find last occurence of '.' separator
|
||||||
|
$last_dot_pos = strrpos($resource_id, '.');
|
||||||
|
|
||||||
|
if ($last_dot_pos !== FALSE)
|
||||||
|
{
|
||||||
|
// This rule might match more generally, try the next level of specificity
|
||||||
|
$resource_id = substr($resource_id, 0, $last_dot_pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We can't make this any more general as there are no more dots
|
||||||
|
// And we haven't managed to match the resource requested
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know this rule matches the resource, check any match condition
|
||||||
|
if ( ! empty($this->condition) AND ! $resource->acl_conditions($user, $this->condition))
|
||||||
|
{
|
||||||
|
// Condition wasn't met (or doesn't exist)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All looks rosy!
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override create to remove less specific rules when creating a rule
|
||||||
|
*
|
||||||
|
* @param Validation $validation
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function create(Validation $validation = NULL)
|
||||||
|
{
|
||||||
|
// Delete all more specific rules for this role
|
||||||
|
$delete = DB::delete($this->_table_name);
|
||||||
|
if (isset($this->_changed['role']))
|
||||||
|
{
|
||||||
|
$delete->where('role_id', '=', $this->_changed['role']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$delete->where('role_id', 'IS', NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If resource is NULL we don't need any more rules - we just delete every rule for this role
|
||||||
|
|
||||||
|
// Otherwise
|
||||||
|
if ( ! is_null($this->resource))
|
||||||
|
{
|
||||||
|
// Need to restrict to roles with equal or more specific resource id
|
||||||
|
$delete->where_open()
|
||||||
|
->where('resource', '=', $this->resource)
|
||||||
|
->or_where('resource', 'LIKE', $this->resource.'.%')
|
||||||
|
->where_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_null($this->action))
|
||||||
|
{
|
||||||
|
// If this rule has an action, only remove other rules with the same action
|
||||||
|
$delete->where('action', '=', $this->action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_null($this->condition))
|
||||||
|
{
|
||||||
|
// If this rule has a condition, only remove other rules with the same condition
|
||||||
|
$delete->where('condition', '=', $this->condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the delete
|
||||||
|
$delete->execute();
|
||||||
|
|
||||||
|
// Create new rule
|
||||||
|
return parent::create($validation);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Model_ACL_Core_Rule
|
111
classes/orm/acl.php
Normal file
111
classes/orm/acl.php
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for access controlled ORM Models
|
||||||
|
*
|
||||||
|
* @see http://github.com/banks/aacl
|
||||||
|
* @package ACL
|
||||||
|
* @uses Auth
|
||||||
|
* @uses ORM
|
||||||
|
* @author Paul Banks
|
||||||
|
* @copyright (c) Paul Banks 2010
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
abstract class ORM_ACL extends ORM implements ACL_Resource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
* model specific overrides
|
||||||
|
*/
|
||||||
|
protected static $_acl_actions = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $_acl_orm_actions = array('create', 'read', 'update', 'delete');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_acl_id = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_id() implementation
|
||||||
|
*
|
||||||
|
* Note: keeps a cache of the acl_id and returns it if the model hasn't changed
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function acl_id()
|
||||||
|
{
|
||||||
|
if ( ! empty($this->_acl_id) and ! $this->changed())
|
||||||
|
{
|
||||||
|
return $this->_acl_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create unique id from primary key if it is set
|
||||||
|
$id = (string) $this->pk();
|
||||||
|
|
||||||
|
if ( ! empty($id))
|
||||||
|
{
|
||||||
|
$id = '.'.$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model namespace, model name, pk
|
||||||
|
$this->_acl_id = 'm:'.strtolower($this->object_name()).$id;
|
||||||
|
return $this->_acl_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_actions() implementation
|
||||||
|
*
|
||||||
|
* @param bool $return_current [optional]
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function acl_actions($return_current = FALSE)
|
||||||
|
{
|
||||||
|
if ($return_current)
|
||||||
|
{
|
||||||
|
// We don't know anything about what the user intends to do with us!
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return default model actions
|
||||||
|
return array_merge(static::$_acl_actions, static::$_acl_orm_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_conditions() implementation
|
||||||
|
*
|
||||||
|
* @param Model_User $user [optional] logged in user model
|
||||||
|
* @param string $condition [optional] condition to test
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function acl_conditions(Model_User $user = NULL, $condition = NULL)
|
||||||
|
{
|
||||||
|
if (is_null($user) AND is_null($condition))
|
||||||
|
{
|
||||||
|
// We have no conditions - they will be model specific
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have no conditions so this test should fail!
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL_Resource::acl_instance() implementation
|
||||||
|
*
|
||||||
|
* Note that the object instance returned should not be used for anything except querying the acl_* methods
|
||||||
|
*
|
||||||
|
* @param string $class_name Class name of object required
|
||||||
|
* @return Object
|
||||||
|
*/
|
||||||
|
public static function acl_instance($class_name)
|
||||||
|
{
|
||||||
|
$model_name = strtolower(substr($class_name, 6));
|
||||||
|
|
||||||
|
return ORM::factory($model_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End ORM_ACL
|
Loading…
Reference in a new issue