mirror of https://github.com/Oreolek/aacl.git
All functionality in README implemented and working.
parent
fb19751d51
commit
d05b6572bb
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2010 Paul Banks
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
32
README.md
32
README.md
|
@ -57,7 +57,7 @@ This means:
|
|||
### Sprig and other ORMs
|
||||
|
||||
AACL ships with a Sprig based Rule model and Sprig based class for easily turning Sprig models into Access Controlled Resources.
|
||||
It should be relatively trivial to override these with ORM (or other library) specific versions and be able to use all core functionality.
|
||||
It should be relatively trivial to modify the library to work with other ORMs but as this is only likely to be used by me for now, a flexible driver system seems unnecessary.
|
||||
|
||||
### Concept: ACL Resources
|
||||
|
||||
|
@ -68,22 +68,22 @@ against which access rules can be created.
|
|||
|
||||
#### AACL_Resource Interface
|
||||
|
||||
The `AACL_Resource` interface defines three methods:
|
||||
The `AACL_Resource` interface defines four methods:
|
||||
|
||||
- **acl_id()**
|
||||
- **public function acl_id()**
|
||||
|
||||
Must return a string that uniquely identifies the current object as a resource.
|
||||
Convention is for controllers to return as `c:controller_name` and models as `m:model_name.primary_key_value`
|
||||
Remember that dot in model identifier - it is significant!
|
||||
|
||||
- **acl_actions($return_current = FALSE)**
|
||||
- **public function acl_actions($return_current = FALSE)**
|
||||
|
||||
This method servers a dual purpose. When the argument `$return_current` is false, the method should return an array of string names, one for each action
|
||||
that can be carried out on the resource. For no specific actions, an empty array should be returned.
|
||||
- `Controller_AACL` returns an array containing the names of all public action methods automatically.
|
||||
- `Sprig_AACL` returns actions 'create', 'read', 'update', 'delete'. These can be changed by overriding this method in specific models.
|
||||
|
||||
- **acl_conditions(Model_User $user = NULL, $condition = NULL)**
|
||||
- **public function acl_conditions(Model_User $user = NULL, $condition = NULL)**
|
||||
|
||||
This method also servers a dual purpose: it both defines available conditions and checks them.
|
||||
|
||||
|
@ -91,7 +91,17 @@ The `AACL_Resource` interface defines three methods:
|
|||
The format of this array is `array('condition_id' => 'Nice description for UIs')`.
|
||||
|
||||
- When a user object and condition id are passed, the funtion should return a boolean indicating whether or not the condition has passed.
|
||||
|
||||
|
||||
- **public static function acl_instance($class_name)**
|
||||
|
||||
This method is used for auto-discovery of available resources. Since the resource ID, actions and conditions must be obtained from an object,
|
||||
we need a way to get and instance of the object given only the class name. Not that the object returned shoul not be used for anything except calling `acl_*` methods
|
||||
to discover resource properties.
|
||||
|
||||
Note that the `Model_AACL_Rule` itself extends `Sprig_AACL`. Instead of the default CRUD actions though it just specifies `grant` and `revoke` actions. This means you can
|
||||
create rules about whether a role can itsef grant or revoke access! Note that the checking is not automatic though. That would prevent installers from creating rules or similar
|
||||
due to not having a user logged in yet! It is still up to the developer to check the user has permission to grant or revoke using check().
|
||||
|
||||
### Resource Conditions
|
||||
|
||||
`AACL_Resource` objects can define conditions which allow rules to provide fine-grained control. Since conditions are resource specific, only conditions defined by the resource
|
||||
|
@ -198,13 +208,17 @@ One of the key requirements for this library is to make checking access rights a
|
|||
|
||||
All checking is done using `AACL::check()` described below:
|
||||
|
||||
**AACL::check($resource, $action = NULL)**
|
||||
**AACL::check(AACL_Resource $resource, $action = NULL)**
|
||||
|
||||
- **$resource**
|
||||
|
||||
Either a string resource ID or an AACL_Resource object. If an object is passed, `check()` will attempt to get the current action from the resource automatically
|
||||
The AACL_Resource being requested. `check()` will attempt to get the current action from the resource automatically
|
||||
using `$reource->acl_actions(TRUE)`. If this returns a string action then that action will be used for checking without having to specify the `$action` parameter.
|
||||
|
||||
Note that the string resource ID can't be specified since the `check()` function requires aaccess to the objects acl_* methods. Even if a method of mapping IDs to objects was
|
||||
implemented, there are issues creating instances of controllers and working out which URI to specify etc. This means that currently there is no way to check permisions on a
|
||||
controller resource other than the one in which the call to `AACL::check()` resides. In practice this is unlikely to be a real limitiation.
|
||||
|
||||
This means that, since a controller object knows the currently executing action, the current controller action can be checked simply with `AACL::check($this)`.
|
||||
Since models don't inherently know which action is being requested, `$action` parameter must be specified (or permission to access all actions will be required).
|
||||
|
||||
|
@ -269,4 +283,4 @@ for `m:post` rather than `m:post.1, m:post.2, ...`. It is left for the developer
|
|||
|
||||
### UI
|
||||
|
||||
A basic rule management UI will hoepfully be added to the module at some point to help get started. It will naturally be disabled in all but 'developement' environment.
|
||||
A basic rule management UI will hopefully be added to the module at some point to help get started. It will naturally be disabled in all but 'developement' environment.
|
|
@ -0,0 +1,292 @@
|
|||
<?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
|
|
@ -0,0 +1,14 @@
|
|||
<?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 {}
|
|
@ -0,0 +1,20 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||
|
||||
/**
|
||||
* AACL Resource interface
|
||||
*
|
||||
* @see http://github.com/banks/aacl
|
||||
* @package AACL
|
||||
* @uses Auth
|
||||
* @uses Sprig
|
||||
* @author Paul Banks
|
||||
* @copyright (c) Paul Banks 2010
|
||||
* @license MIT
|
||||
*/
|
||||
interface AACL_Resource
|
||||
{
|
||||
/**
|
||||
* Gets a unique ID string for this resource
|
||||
*
|
||||
* Convention for controllers is c:controller_name
|
||||
* Convention for models is m:model_name.primary_key_value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acl_id();
|
||||
|
||||
/**
|
||||
* Returns actions specific to this resource as an array
|
||||
*
|
||||
* For no actions return empty array
|
||||
*
|
||||
* Example: return array('create', 'read', 'update', 'delete')
|
||||
*
|
||||
* If $return_current is TRUE, return value should be the currently requested action or NULL if not known.
|
||||
*
|
||||
* @param bool $return_current [optional]
|
||||
* @return mixed
|
||||
*/
|
||||
public function acl_actions($return_current = FALSE);
|
||||
|
||||
/**
|
||||
* Defines any condition fro this resource
|
||||
*
|
||||
* If params are provided, returns a boolean indicating whether $user meets condition specified in $condition
|
||||
*
|
||||
* If no params are provided, returns an array or available conditions for this resource in form
|
||||
*
|
||||
* return array('condition_id' => 'User friendly description of condition');
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Returns an instance of the current object suitable for calling the above 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
|
||||
* @return Object
|
||||
*/
|
||||
public static function acl_instance($class_name);
|
||||
|
||||
} // End AACL_Resource
|
|
@ -0,0 +1,104 @@
|
|||
<?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
|
|
@ -0,0 +1,169 @@
|
|||
<?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
|
|
@ -0,0 +1,101 @@
|
|||
<?php defined('SYSPATH') or die ('No direct script access.');
|
||||
|
||||
/**
|
||||
* Base class for access controlled Sprig Models
|
||||
*
|
||||
* @see http://github.com/banks/aacl
|
||||
* @package AACL
|
||||
* @uses Auth
|
||||
* @uses Sprig
|
||||
* @author Paul Banks
|
||||
* @copyright (c) Paul Banks 2010
|
||||
* @license MIT
|
||||
*/
|
||||
abstract class Sprig_AACL extends Sprig implements AACL_Resource
|
||||
{
|
||||
/**
|
||||
* AACL_Resource::acl_id() implementation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acl_id()
|
||||
{
|
||||
// Create unique id from primary key if it is set
|
||||
if (is_array($this->_primary_key))
|
||||
{
|
||||
$id = '';
|
||||
|
||||
foreach ($this->_primary_key as $name)
|
||||
{
|
||||
$id .= (string) $this->$name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$id = (string) $this->{$this->_primary_key};
|
||||
}
|
||||
|
||||
if ( ! empty($id))
|
||||
{
|
||||
$id = '.'.$id;
|
||||
}
|
||||
|
||||
// Model namespace, model name, pk
|
||||
return 'm:'.strtolower($this->_model).$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* AACL_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('create', 'read', 'update', 'delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 - they will be model specific
|
||||
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)
|
||||
{
|
||||
$model_name = strtolower(substr($class_name, 6));
|
||||
|
||||
return Sprig::factory($model_name);
|
||||
}
|
||||
|
||||
} // End Sprig_AACL
|
Loading…
Reference in New Issue