Domanda

I'm working on a custom framework strictly for fun and educational purposes. I've read over this question on how to implement permissions and I like the answers; either using a decorator pattern and / or checking permissions based on the URL from a dispatcher.

My question is how the white list of permissions should be generated? I don't want every method in my controllers to require permission to execute, so I could, for example, use a special naming convention such as preceding method names with an "x":

class CalendarController
{
    public function index($year = null, $month = null, $day = null)
    {
        // display calendar (no permission needed)
    }

    public function xAddEvent()
    {
        // display form to add event (permission required)
    }

    public function xAddEventSubmit()
    {
        // submit form to add event (permission required)
    }
}

I could then write a script to iterate through all of my controllers and return the x-methods, giving me my list of permissions to assign to different roles.

Another option could be to hard-code permissions as a property of each controller, for example:

class CalendarController
{
    public $permissions = array('addEvent',
                                'addEventSubmit');

    public function index($year = null, $month = null, $day = null)
    {
        // display calendar (no permission needed)
    }


    public function addEvent()
    {
        // display form to add event (permission required)
    }

    public function addEventSubmit()
    {
        // submit form to add event (permission required)
    }
}

Are there any better alternatives or am I on the right track?

È stato utile?

Soluzione

Note: this will be mostly an expansion on the solution that i provided in the linked article. I will not comment on answers from hakre.

As I understand your question, the issue basically is that you do not want to set access rights for each method separately.

Option 1: don't decorate

In the solution, that involves Decorator, one of the benefits is that, when you use the secured class (for example the Controller, though it can be any part of application), you do not need to know that it has been decorated. Therefore, if you have some controllers that should be fully accessible to anyone, then you can just not decorate those.

This approach would most likely require the factory, that is responsible for instantiation of controller, to have some list of controller that should or should-not be wrapped in a decorator. Since this would always require an if statement to consult the list, I personally would consider this only for instances on which you call more the one method (which, in my case, would exclude controllers).

Option 2: wildcards and whitelists

A different way tackle this would be take advantage of how you actually check for authorization.

$command = [ get_class($this->target), $method ];

This was the token, that get checked against. This means that the ACL receives not only the name of method, but also the full class name (including namespace, btw). It gives you an opportunity to create a list of rules that include both name of the class and method. Something along the lines of:

Controllers\Identification::*  anonymous
Controllers\*::*               admin
Controllers\Users::view        authenticated
Controllers\Users::remove      manager
Controllers\Users::add         manager

The idea is that you save some configuration where you define all the allowed interactions. The ACL goes down the list, checking the user's group, and on first match returns the result (in the example the admins can access everything except login page, which is allowed only for unauthenticated users). Then again, this particular example would depend on you implementing at least partial groups-contain-groups functionality.

I would also reiterate, that you should only use white-lists for this. There is no significant risk added, if you forget to allow managers to remove users, but, if you forget to deny users to remove other users, it can be a critical mistake when using blacklist based authorization.

my two cents

Altri suggerimenti

Have you thought of method overloading? This is a very simple example. Basically, if a function is not found, the __call() function is used to catch it. Then, you can do your permission checks and call the correct private or protected method.

class CalendarController
{
    public function index($year = null, $month = null, $day = null)
    {
        // display calendar (no permission needed)
    }

    public function __call($name, $arguments)
    {
        // do your permission checks here
        if ($name == 'addEvent' && $this->hasPermission()) {
            return $this->_addEvent($arguments);
        }

        return false;
    }

    protected function _addEvent($params) {

    }
}

I would recommend looking at other frameworks to get an idea of how they implement restrictions. For example, yii implements access restrictions based on the specific action requested and whether or not the user has a specific level of permissions. I would highly recommend RBAC (http://en.wikipedia.org/wiki/Role-based_access_control)

Yii example:

public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'list' and 'show' actions
            'actions'=>array('list','show'),
            'users'=>array('*'),
        ),
        array('allow', // allow authenticated user to perform 'create' and 'update' actions
            'actions'=>array('create','update'),
            'users'=>array('@'),
        ),
        array('allow', // allow admin user to perform 'admin' and 'delete' actions
            'actions'=>array('admin','delete'),
            'users'=>array('admin'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

They also implement filters which tell us that certain actions must be requested with certain types of HTTP requests. the following example states that in order to run a create action, the HTTP request must be a POST.

public function filters()
{
    return array(
        'accessControl', // perform access control for CRUD operations
        'postOnly + create', // we only allow create via POST request
    );
}

Resource: http://www.larryullman.com/2010/01/14/yii-framework-access-control-lists/

Also see: http://www.yiiframework.com/wiki/169/configuring-controller-access-rules-to-default-deny/

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top