CakePHP权限:ACL过度杀伤

时间:2013-01-12 13:59:47

标签: cakephp cakephp-2.2 cakephp-acl

我有两个主要表:

  1. 书籍(id,author,isbn,...)
  2. 用户(ID,用户名,密码,...)
  3. 我正在寻找构建一个应用程序,其中User1登录并可以:

    1. 查看所有图书的清单(例如,仅标题)
    2. 查看他应该有权访问的某些书籍的详细信息(作者,isbn,...)
    3. 每个用户都可以访问某组图书。我不需要担任各种角色。

      我为上面设置了MVC和关系(habtm)。我现在正在研究开发权限。你认为CakePHP ACL解决了这个问题还是过度杀伤?

      如果它是矫枉过正的,是否有其他组件或更简单的方法来构建所需的功能?

2 个答案:

答案 0 :(得分:1)

是的,ACL过度

ACL是一个非常强大且灵活的系统 - 但它并不是免费的,它带来了复杂性。除非你有一个绝对需要细粒度权限的用例(你所描述的两个规则不适合这个) - 不要使用ACL。

限制他添加的用户的书籍

此规则易于实施 - 例如添加到相关的查找呼叫:

$results = $BookModelInstance->find('all', array(
    'conditions' => array(
        'created_by' => AuthComponent::user('id')
    )
));

限制他购买的用户的书籍

此规则also easy to implement,稍微考虑一下:

$BookModelInstance->bindModel(array(
    'hasOne' => array( // Yes, hasOne not hasMany
        'MyPurchase' => array(
            'className' => 'Purchase',
            'foriegnKey' => 'user_id'
        ) 
    )
));
$results = $BookModelInstance->find('all', array(
    'recursive' => 0, // to join hasOne+belongsTo associations into the query
    'conditions' => array(
        'MyPurchase.user_id' = AuthComponent::user('id'),
    )
));

bindModel调用达到了SELECT .. FROM books LEFT JOIN book_users的等价物。因此,查找调用中的条件会将结果限制为有购买图书的用户记录的图书。

将它们放在一起

自动应用这两个规则的简单实现是:

model Book extends AppModel {

    public $actsAs = array('Containable');

    public $restrictToUser = true;


    public function beforeSave($options = array()) {
        if (!$this->id) {
           // Store who created this book
           $this->data[$this->alias]['created_by'] = AuthComponent::user('id');
        }
        return true;
    }

    public function beforeFind($queryData) {
        if (!$this->restrictToUser) {
            // we don't want to apply user-level restrictions
            return true;
        }

        $userId = AuthComponent::user('id');
        if (!$userId) {
            // we want to restrict to the current user - there isn't one.
            return false;
        }

        // define the association to the purchase table
        $this->bindModel(array(
            'hasOne' => array(
                'MyPurchase' => array(
                    'className' => 'Purchase',
                    'foriegnKey' => 'user_id'
                ) 
            )
        ));

        //ensure the purchase table is included in the current query
        $queryData['contain']['MyPurchase'] = array();

        // restrict to rows created by the current user, OR purchased by the current user
        $queryData['conditions']['OR'] = array(
            $this->alias '.created_by' => $userId,
            'MyPurchase.user_id' => $userId
        );
        return $queryData;
    }
}

这要求字段created_by(或等效字段)位于books表中,并使用containsable来确保purchases表(或等效表)包含在所有相关查询中。

答案 1 :(得分:0)

最简单的解决方案:在控制器中添加条件,所以:

$this->set('books', $this->Book->find(
    'all',
    array('conditions' => array('Book.user_id' => $user['User']['id']))
);

缺点:您可能会在此处创建重复的代码,因为此检查也必须在其他地方进行。此外,当您开始测试模型时,您只能测试它返回书籍,您无法测试模型方法,如:getMyBooks($ userId)。所以不,不是首选的解决方案。

下一个解决方案:检入模型

可以通过签入例如您的书籍模型来完成。您可以检查afterfind()方法是否允许返回的记录。在您之前的查找中,您还可以为所有查询添加其他条件。

一般来说模型应该很胖所以我建议在那里实现清晰的方法:getAllBooks,getBooksOfUser($ User),getLatestBooksOfUser($ User)等。

为什么这是一个很好的实现?因为您现在可以在中心位置管理访问级别。您可以测试模型,但您确定它只返回该用户的书籍。

使用beforeSave等,你可以介入每次保存尝试并首先检查:嘿,你想保存这个,但这真的是你的书吗?

ACL解决方案

但总的来说,实现一些ACL解决方案(最好是内置的解决方案)可能是明智之举,因为这会使您的应用程序更具未来性。它允许灵活性,例如:

  

每个用户都可以访问某组图书。我不需要担任各种角色。

现在这是真的,但未来可以改变它。因此,如果您需要快速解决方案,只需自定义过滤记录。但想想未来。