7.8 所有权访问

对于一个应用来说,不仅仅有控制器和动作方法。有时候,你会想允许某个用户访问某个控制器和动作,但是又想让他仅仅只能操作系统中属于他的内容对象。例如,我们允许用户修改博文,但是只能修改他自己的博文。在这些应用中,我们需要知道当前用户正在操作的博文的所有者是否是他。

7.8.1 准备内容对象

首先,你必须在内容对象中创建特定的方法。在此示例中,我们将创建accessIsOwner()方法,当然方法名可以随便取。

这个内容对象中的特殊的所有权方法按顺序接受两个参数:一个是Solar_Auth_Adapter的实例,另一个是Solar_Role_Adapter的实例。那么你刚才写的这个方法就可以当前检验用户的身份认证和/或角色,最后如果该用户被认定为该内容对象的所有者,则返回真值。(否则,返回假值。)

作为一个示例,假设我们有一个博文的模型记录,并且对应于此模型的数据表有一个author_handle字段(也即,该博文的创建者的用户名)。我们可以在博文记录类中写下这个方法:

<?php
class Vendor_Model_Blogs_Record
{
    public function accessIsOwner(Solar_Auth_Adapter $auth, Solar_Role_Adapter $role)
    {
        // check the record's 'author_handle' column to see
        // if it matches the current authenticated user.
        if ($this->author_handle == $auth->handle) {
            return true;
        }
        
        // not the original author, so don't treat as an owner.
        return false;
    }
}

7.8.2 配置访问适配器以识别所有权

对于系统中的每一种内容对象,你必须告诉Solar_Access_Adapter适配器在决定当前用户是否是所有者时使用何种方法。在上面的示例中,我们写了一个Vendor_Model_Blogs_Record::accessIsOwner()方法。接下来我们在配置文件中为Solar_Access_Adapter写一条配置项:

<?php
$config['Solar_Access_Adapter']['owner_method'] = array(
    'Vendor_Model_Blogs_Record' => 'accessIsOwner',
);

'owner_method'配置项的值是是一个键-值对关联数组,键代表被检验的内容对象的类名,值代表该类中的检验方法。之后当我们调用$user->access->isOwner()方法的时候,适配器将遍历数组中的这些键来匹配当前控制器对象,并且调用相关的方法来检验所有权。

7.8.3 访问控制列表

在之前的某一节当中提到过,在控制器和动作中检验权限时,你可以使用'owner'记录类型指定当前用户必须为该对象的所有者。

# flag      type        name        class                   action
# ----      ----        ----        -----                   ------
# allow users to edit their own posts
allow       owner       -           Vendor_App_Blog         edit

7.8.4 与控制器共舞

现在,我们在内容对象中已经有一个特有的所有权方法了,并且访问适配器知道使用何种方法去检验内容对象。我们可以在控制器动作中使用所有权访问验证了。

使用前面提到的“动作内部的访问控制”方法,在动作中加所有权验证的代码如下:

<?php
abstract class Vendor_Controller_Page extends Solar_Controller_Page
{
    public $user;
    
    protected function _setup()
    {
        parent::_setup();
        $this->user = Solar_Registry::get('user');
    }
    
    protected function _isUserAllowed($object = null)
    {
        if ($this->user->access->isAllowed($this, $this->_action, $object)) {
            return true;
        } else {
            $this->_error('Access denied.');
            $this->_response->setStatusCode(403);
            return false;
        }
    }
}

class Vendor_App_Blog extends Vendor_Controller_Page
{  
    public function actionEdit($id = null)
    {
        // ... perform preliminary logic ...
        
        // get the blog item for editing
        $item = $this->_model->blogs->fetch($id);
        
        // check access
        if (! $this->_isUserAllowed($item)) {
            return;
        }
        
        // ... rest of the action here ...
    }
}

  • _isUserAllowed()方法中,我们加了一个参数用于传递需要进行所有权验证的内容对象。类似的,isAllowed()方法也要传递需要验证的内容对象。

  • actionEdit()方法在验证访问控制权限之前获取博文记录。在博文获取之后,我们才验证在当前控制器和动作中用户是否允许访问内容对象。