简体   繁体   中英

Allow only author to edit his post in yii2 using ACF

I am using Access Control Filter for access managing, but can't get one thing done - for example, how can I allow just project manager to update project and forbid it to others? I tried it via matchCallback, but in this case all project managers can update any project because TRUE is returned.

Similar more often required rules - how to allow user to update/delete posts where he is author using 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);
                    }
                ],
            ],
        ],

It could be implemented something like this:

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;
    }

...

}

This is bested solved with a custom AccessRule . One would have to fill in the code to check if a user is the author of a project.

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;
     }
}

The rule can be used by including this in the behaviors:

'authorAccess' => [
        'class' => AccessControl::className(),
        'only' => ['update'],
        'rules' => ['actions' => ['update']],
        'ruleConfig' => ['class' => '\app\filters\AuthorAccessRule'],
],

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM