7

我正在使用访问控制过滤器进行访问管理,但无法完成一件事 - 例如,我怎样才能只允许项目经理更新项目并禁止其他人使用?我通过 matchCallback 进行了尝试,但在这种情况下,所有项目经理都可以更新任何项目,因为返回 TRUE。

类似的更经常需要的规则 - 如何允许用户使用 ACF 更新/删除他是作者的帖子?

         'access' => [
            'class' => AccessControl::className(),
            'only' => ['index', 'view', 'create', 'update', 'delete'],
            'rules' => [
                [
                    'actions' => ['update'],
                    'allow' => true,
                    'roles' => ['@'],
                    'matchCallback' => function ($rule, $action) {

                        return Yii::$app->user->identity->getProjectParticipants()
                                    ->one()->isManager(Yii::$app->user->identity->id);
                    }
                ],
            ],
        ],
4

3 回答 3

7

它可以像这样实现:

use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;

class MyController extends Controller
{

...

    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'only' => ['update', 'delete'],
                'rules' => [
                    [
                        'actions' => ['update', 'delete'],
                        'allow' => true,
                        'roles' => ['@'],
                        'matchCallback' => function ($rule, $action) {
                            if (Yii::$app->user->can('admin') || $this->isUserAuthor()) {
                                return true;
                            }
                            return false;
                        }
                    ],
                ],
            ],
        ];
    }

    protected function findModel($id)
    {
        if (($model = MyModel::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }

    protected function isUserAuthor()
    {   
        return $this->findModel(Yii::$app->request->get('id'))->author->id == Yii::$app->user->id;
    }

...

}
于 2015-12-29T10:55:08.890 回答
3

这最好用自定义来解决AccessRule。必须填写代码来检查用户是否是项目的作者。

namespace app\filters;

class AuthorAccessRule extends \yii\filters\AccessRule
{
    public $allow = true;  // Allow access if this rule matches
    public $roles = ['@']; // Ensure user is logged in.

    public function allows($action, $user, $request)
    {
        $parentRes = parent::allows($action, $user, $request);
        // $parentRes can be `null`, `false` or `true`.
        // True means the parent rule matched and allows access.
        if ($parentRes !== true) {
            return $parentRes;
        }
        return ($this->getProjectAuthorId($request) == $user->id);
     }

     private function getProjectAuthorId($request)
     {
         // Fill in code to receive the right project.
         // assuming the project id is given à la `project/update?id=1`
         $projectId = $request->get('id');
         $project = \app\models\Project::findOne($projectId);
         return isset($project) ? $project->author_id : null;
     }
}

可以通过将其包含在行为中来使用该规则:

'authorAccess' => [
        'class' => AccessControl::className(),
        'only' => ['update'],
        'rules' => ['actions' => ['update']],
        'ruleConfig' => ['class' => '\app\filters\AuthorAccessRule'],
],
于 2015-06-25T13:06:47.600 回答
1

Following is how I do it with combination of ACF and RBAC. Please correct me if I am wrong or there is better way of doing it. It's based on Basic template.

  1. User's role is stored in the "role" column of the "user" table. Another table "country" is used in this example. Assume you have generated models and controllers using Gii.

  2. Customise PhpManager to use role from database table "user".

class PhpManager extends \yii\rbac\PhpManager
{
    public function init()
    {
        parent::init();
    }

    public function getAssignments($userId)
    {
        if (!Yii::$app->user->isGuest) {
            $assignment = new Assignment();
            $assignment->userId = $userId;
            # Assume the role is stored in User table "role" column
            $assignment->roleName = Yii::$app->user->identity->role;
            return [$assignment->roleName => $assignment];
        }
    }
}

3. Add authManager to web.app and console.app console file.

    'authManager' => [
        'class' => 'app\components\PhpManager',
        'defaultRoles' => ['user', 'manager', 'admin', 'master'],
    ],
  1. Create a customized AccessRule. Reference from speixoto's blog.

# Reference
# http://programming.peixoto.cf/2015/01/14/yii2-role-based-access-control-and-context-access-rule/#$$nmvkr0&&0SUmhOPVEeSW9grIhAgzZg$$

class ContextAccessRule extends AccessRule
{

    public $modelClass;
    public $primaryKey;

    protected function matchRole($user)
    {
        if (parent::matchRole($user))
            return true;

        $model = $this->findModel();

        foreach ($this->roles as $role) {
            # Call the CheckAccess() function which process rules
            if ($user->can($role, ['model' => $model])) {
                return true;
            }
        }
        return false;
    }

    protected function findModel()
    {
        if (!isset($this->modelClass))
            throw new InvalidConfigException(Yii::t('app', 'the "modelClass" must be set for "{class}".', ['class' => __CLASS__]));
        $primaryKey = $this->getPrimaryKey();
        # Get the request params
        $queryParams = \Yii::$app->getRequest()->getQueryParams();
        # Load the model
        $model = call_user_func([$this->modelClass, 'findOne'], $queryParams[join(',', $primaryKey)]);
        if ($model !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exists.'));
        }
    }

    # Get the primary key of the model
    protected function getPrimaryKey()
    {
        if (!isset($this->primaryKey)) {
            return call_user_func([$this->modelClass, 'primaryKey']);
        } else {
            return $this->primaryKey;
        }
    }

  1. Create a RbacController.php to generate RBAC files (assignments.php, items.php, rules.php) into rbac folder.

class RbacController extends Controller
{
    public function actionInit()
    {
        $auth = Yii::$app->authManager;
        $auth->removeAll();

        ### CREATE & ADD ROLES
        $user = $auth->createRole('user');
        $node = $auth->createRole('node');
        $manager = $auth->createRole('manager');
        $admin = $auth->createRole('admin');
        $master = $auth->createRole('master');

        $auth->add($user);
        $auth->add($node);
        $auth->add($manager);
        $auth->add($admin);
        $auth->add($master);

        $auth->addChild($manager, $user);
        $auth->addChild($manager, $node);
        $auth->addChild($admin, $manager);
        $auth->addChild($master, $admin);

        ### ADD RULES
        $ownerRule = new \app\components\OwnerRule();
        $auth->add($ownerRule);

        ### CREATE PERMISSIONS ###

        $pUpdateOwn = $auth->createPermission('updateOwn');
        $pUpdateOwn->description = 'update own';
        $pUpdateOwn->ruleName = $ownerRule->name;
        $auth->add($pUpdateOwn);
        $auth->addChild($pUpdateOwn, $pUpdate);

        $pDeleteOwn = $auth->createPermission('deleteOwn');
        $pDeleteOwn->description = 'delete own';
        $pDeleteOwn->ruleName = $ownerRule->name;
        $auth->add($pDeleteOwn);
        $auth->addChild($pDeleteOwn, $pDelete);

        ### ASSIGN PERMISSION TO ROLES

        $auth->addChild($user, $pUpdateOwn);
        $auth->addChild($user, $pDeleteOwn);
        $auth->addChild($manager, $pUpdateOwn);
        $auth->addChild($manager, $pDeleteOwn);

    }
}

  1. From console, navigate to your project root. Run ./yii rbac/init (for mac) to generate the 3 files into rbac folder.

  2. In CountryController.php, override following function to add "access" behaviors.

    public function behaviors()
    {
        $behaviors = parent::behaviors();

        $behaviors['verbs'] = [
            'class' => VerbFilter::className(),
            'actions' => [
                'delete' => ['post'],
            ],
        ];

        ['access'] = [
            'class' => AccessControl::className(),
//            'only' => ['view', 'index', 'create', 'update', 'delete'],
            'rules' => [
                [
                    'actions' => ['view', 'index'],
                    'allow' => true,
                    'roles' => ['?', '@'],
                ],
                [
                    'actions' => ['create'],
                    'allow' => true,
                    // Allow users, manager and admins to create
                    'roles' => ['user'],
                ],
                [
                    'class' => 'app\components\ContextAccessRule',
                    'modelClass' => 'app\models\Country',
                    'actions' => ['update'],
                    'allow' => true,
                    # allow owner and manager to udpate
                    'roles' => ['updateOwn', 'manager']
                ],
                [
                    'class' => 'app\components\ContextAccessRule',
                    'modelClass' => 'app\models\Country',
                    'actions' => ['delete'],
                    'allow' => true,
                    # allow owner and manager to delete
                    'roles' => ['deleteOwn', 'manager'],
                ],
            ],
            # if user not login, and not allowed for current action, return following exception
            'denyCallback' => function ($rule, $action) {
                throw new UnauthorizedHttpException('You are not authorized.');
            },
        ];

        return $behaviors;
    }
8. Test it out.

于 2015-04-16T09:28:00.693 回答