I use following scheme.
It worth to say that most user permissions checks can be divided into two general cases:
- user access to controller action based on user role without checking
parameters action is called with,
- user access to model based on any logic or relations between
particular user and particular model.
Access to controller action without checking of attributes are usually implemented in MVC frameworks. This is simple at all: you define rules, your users have role. You simply check that user has permission to action lookup up its role in rules.
User access to particular model should be defined in model. (Actor is base user class. Suppose it can be either customer, seller or guest.)
interface ICheckAccess
{
public function checkAccess(Actor $actor, $role);
}
class SomeModel implements ICheckAccess
{
public function checkAccess(Actor $actor, $role)
{
// Your permissions logic can be as sophisticated as you want.
}
}
Placing that logic in model brings some profit. Access check method can be inherited, you don't need to create any extra classes, you can use general OOP advantages.
Next, to simplify access checking, we take some assumptions that are almost always implemented already for simplicity and good style:
- usually controllers are related to some model class;
- actions which are checked for access take single model id as parameter;
- this parameter can always be accessed uniformly from method of base controller class;
- action is placed in controller corresponding to model which id action takes.
With these assumptions, actions that use model id can be associated with particular model instance. In fact, most actions can easily be transformed and moved to fit assumptions stated above.
Then, some base abstract controller class should be defined and inherited.
abstract class ModelController
{
// Retrieve model from database using id from action parameter.
public abstract function loadModel($id);
// Returns rules for user role to pass to SomeModel::checkAccess()
// Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
public abstract function modelRules();
public abstract fucntion getIdParameter();
public function filterModelAccess()
{
$id = $this->getIdParameter();
if(!$this->checkModelAccess($id))
throw new HttpException(403);
}
public function checkModelAccess($id)
{
$model = $this->loadModel($id);
$actor = My::app()->getActor();
$rules = $this->modelRules();
$role = $rules[My::app()->getActionName()];
return $model->chechAccess($actor, $role);
}
}
You can call SomeController::checkModelAccess($id) method when you construct your menus and decide whether to show some link.