RBAC in yii 2.o

Hi guys,

I am new to the yii framework. I have a problem implementing a simple RBAC in a yii application i am developing.

the app has an uploads model and controller that handles user uploads. The only problem is users can view,edit & delete all uploads even if they didn’t create them like in the image attached below.

I have tried following the RBAC guide in the Yii 2.0 guide but i cant seem to get thing working.

Here is what I did;

  1. Enabled AuthManager in console\config\main.php and \frontend\config\main.php

  2. Ran RBAC database migrations and the yii rbac/init. They all succeeded.

here is my rbacController file


<?php

namespace console\controllers;


use Yii;

use yii\console\Controller;


class RbacController extends Controller

{

    public function actionInit()

    {

        $auth = Yii::$app->authManager;


       //add "createUpload" permission

		$createUpload=$auth->createPermission ('createUpload');

		$createUpload->description='Create Post';

		$auth->add($createUpload);

		

		//add "updateUpload" permission

		$updateUpload=$auth->createPermission ('updateUpload');

		$updateUpload->description='Update Upload';

		$auth->add($updateUpload);

		

                //add deleteUpload permission

		$deleteUpload=$auth->createPermission ('deleteUpload');

		$deleteUpload->description='Delete Upload';

		$auth->add($deleteUpload);

                

                //add "viewUpload" permission

		$viewUpload=$auth->createPermission ('viewUpload');

		$viewUpload->description='view Upload';

		$auth->add($viewUpload);

                

		 // add "author" role and give this role the "createUpload" permission

        $author = $auth->createRole('author');

        $auth->add($author);

        $auth->addChild($author, $createUpload);


        // add "admin" role and give this role the "updateUpload" permission

        // as well as the permissions of the "author" role

        $admin = $auth->createRole('admin');

        $auth->add($admin);

        $auth->addChild($admin, $updateUpload);

        $auth->addChild($admin, $author);


        

        // add the rule

$rule = new \console\rbac\AuthorRule;

$auth->add($rule);

        // add the "$updateOwnUpload" permission and associate the rule with it.

$updateOwnUpload = $auth->createPermission('updateOwnUpload');

$updateOwnUpload->description = 'Update own upload';

$updateOwnUpload->ruleName = $rule->name;

$auth->add($updateOwnUpload);

        

    }

}

and the accessrule file




<?php


/* 

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */


namespace console\rbac;


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->getUser_id($request) == $user->id);

     }


     private function getUser_id($request)

     {

         // Fill in code to receive the right project.

         // assuming the project id is given à la `project/update?id=1`

         $upload_id = $request->get('id');

         $uploads = frontend\models\Uploads::findOne($upload_id);

         return isset($uploads) ? $uploads->user_id : null;

     }

}



It looks like you have not set any role with deleteUpload and viewUpload permissions.

Yes. But shouldnt the viewpost permission still work?

I’m not sure, I haven’t analyse the rbac code before. Anyway - do you have the rbac added in your access filters?

I couldnt post sooner coz i had to waith for 12 hours after i signed up to post more than 2 posts. Anyway I managed to get around the problem but now i cant access the uploads page at all. it throws a #403 forbidden error.

Here is my rbac controller file which creates permissions and roles assigns users to roles. I have some users with id 12-15.




<?php

namespace console\controllers;


use Yii;

use yii\console\Controller;


class RbacController extends Controller

{

    public function actionInit()

    {

        $auth = Yii::$app->authManager;

        

                //index permission

		$index=$auth->createPermission ('uploads/index');

		$index->description='View Upload index';

		$auth->add($index);

                

               //add "createUpload" permission

		$create=$auth->createPermission ('uploads/create');

		$create->description='Create Upload';

		$auth->add($create);

		

		//add "updateUpload" permission

		$update=$auth->createPermission ('uploads/update');

		$update->description='Update Upload';

		$auth->add($update);

		

                //add deleteUpload permission

		$delete=$auth->createPermission ('uploads/delete');

		$delete->description='Delete Upload';

		$auth->add($delete);

                //add "viewUpload" permission

		$view=$auth->createPermission ('uploads/view');

		$view->description='view Upload';

		$auth->add($view);

                

		// add "client" role and give this role the "create,index,view" permission

                $client = $auth->createRole('client');

                $auth->add($client);

                $auth->addChild($client, $index);

                $auth->addChild($client, $create);

                $auth->addChild($client, $view);

                

                

                

                //add admin role and give them delete and update permissions as well as clients permissions

                $admin = $auth->createRole('admin');

                $auth->add($admin);

                $auth->addChild($admin, $client);

                $auth->addChild($admin, $delete);

                $auth->addChild($admin, $update);




                //assign roles

                $auth->assign($admin,12);

                $auth->assign($client,13);

                $auth->assign($client,14);

                $auth->assign($client,15);

       

                // add the rule

       

                $rule = new \console\rbac\ClientRule;

                $auth->add($rule);

                        // add the "$updateOwnUpload" permission and associate the rule with it.

                $updateOwnUpload = $auth->createPermission('updateOwnUpload');

                $updateOwnUpload->description = 'Update own upload';

                $updateOwnUpload->ruleName = $rule->name;

                $auth->add($updateOwnUpload);


                //$update=$auth->createPermission('auth/post/update');

                // "updateOwnUpload" will be used from "updatePost"

                $auth->addChild($updateOwnUpload, $update);

                // allow "clients" to update their own posts

                $auth->addChild($client, $updateOwnUpload); 


    }

}



here is my uploads controller




<?php


namespace frontend\controllers;


use Yii;

use frontend\models\Uploads;

use frontend\models\UploadsSearch;

use yii\web\Controller;

use yii\web\NotFoundHttpException;

use yii\filters\VerbFilter;

use yii\web\UploadedFile;

use yii\filters\AccessControl;

/**

 * UploadsController implements the CRUD actions for Uploads model.

 */

class UploadsController extends Controller

{

  /* 

    public function behaviors()

    {

        return[

            'verbs'=>[

                'class'=>VerbFilter::className(),

                'actions'=>[

                    'delete'=>['post'],

                ],

            ],

        ];

    */

    

    public function behaviors()

    {

        $behaviors ['access']= [

         'class'=>AccessControl::className(),

	 'rules'=>[

	    [

		'allow'=>true,

		'roles'=>['@'],

		'matchCallback'=>function ($rule, $action) {

            

			$module =Yii::$app->controller->module->id;

			$action =Yii::$app->controller->action->id;

			$controller=Yii::$app->controller->id;

			$route="$controller/$action";

			$post =Yii::$app->request->post();

			if(\Yii::$app->user->can($route)){

				return true;

			}

		}

		],

	   ],

	 ];

	 return $behaviors;

    }


    /**

     * Lists all Uploads models.

     * @return mixed

     */

    public function actionIndex()

    {

        $searchModel = new UploadsSearch();

        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);


        return $this->render('index', [

            'searchModel' => $searchModel,

            'dataProvider' => $dataProvider,

        ]);

    }


    /**

     * Displays a single Uploads model.

     * @param integer $id

     * @return mixed

     */

    public function actionView($id)

    {

        return $this->render('view', [

            'model' => $this->findModel($id),

        ]);

    }


    /**

     * Creates a new Uploads model.

     * If creation is successful, the browser will be redirected to the 'view' page.

     * @return mixed

     */

    public function actionCreate()

    {

        $model = new Uploads();


        if ($model->load(Yii::$app->request->post()))

                     

            {

            $model->user_id = Yii::$app->user->getId();

            //get file instance

			$imageName=$model->upload_name;

			 $model->file= UploadedFile::getInstance($model,'file');

			 $model->file->saveAs('uploads/'.$imageName.'.'.$model->file->extension);

			 

			//save path in db column

			$model->upload_path='uploads/'.$imageName.'.'.$model->file->extension;

			$model->upload_date=date('Y-m-d h:m:s');

			$model->save();

                        

            return $this->redirect(['view', 'id' => $model->upload_id]);

        } else {

            return $this->render('create', [

                'model' => $model,

            ]);

        }

    }


    /**

     * Updates an existing Uploads model.

     * If update is successful, the browser will be redirected to the 'view' page.

     * @param integer $id

     * @return mixed

     */

    public function actionUpdate($id)

    {

        $model = $this->findModel($id);


        if ($model->load(Yii::$app->request->post())

                && $model->save()) {

            return $this->redirect(['view', 'id' => $model->upload_id]);

        } else {

            return $this->render('update', [

                'model' => $model,

            ]);

        }

    }


    /**

     * Deletes an existing Uploads model.

     * If deletion is successful, the browser will be redirected to the 'index' page.

     * @param integer $id

     * @return mixed

     */

    public function actionDelete($id)

    {

        $this->findModel($id)->delete();


        return $this->redirect(['index']);

    }


    /**

     * Finds the Uploads model based on its primary key value.

     * If the model is not found, a 404 HTTP exception will be thrown.

     * @param integer $id

     * @return Uploads the loaded model

     * @throws NotFoundHttpException if the model cannot be found

     */

    protected function findModel($id)

    {

        if (($model = Uploads::findOne($id)) !== null) {

            return $model;

        } else {

            throw new NotFoundHttpException('The requested page does not exist.');

        }

    }

    

}




here is my access rule file




<?php


/* 

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */


namespace console\rbac;


class ClientsAccessRule 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->getUser_id($request) == $user->id);

     }


     public function getUser_id($request)

     {

         // Fill in code to receive the right project.

         // assuming the project id is given à la `project/update?id=1`

         $upload_id = $request->get('id');

         $uploads = frontend\models\Uploads::findOne($upload_id);

         return isset($uploads) ? $uploads->user_id : null;

     }

}




and finally here is my rule file




<?php


/* 

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */


namespace console\rbac;


use yii\rbac\Rule;


/**

 * Checks if authorID matches user passed via params

 */

class ClientRule extends Rule

{

    public $name = 'isClient';


    /**

     * @param string|integer $user the user ID.

     * @param Item $item the role or permission that this rule is associated with

     * @param array $params parameters passed to ManagerInterface::checkAccess().

     * @return boolean a value indicating whether the rule permits the role or permission it is associated with.

     */

    public function execute($user, $item, $params)

    {

        if(isset($params['model'])){ //Directly specify the model you plan to use via param

            $model=$params['model'];

            }else {//use the controller findModel method to get the model-this is what executes via the behavior rule

                $id=\Yii::$app->request->get('id'); //note this is an assumption on you url structure

                $model=\Yii::$app->controller->findUserModel($id); //note this only works if you change findModel to be public

            }

            return $model->user_id==$user;

    }

}




If you are using rbac add it in accessfilter. I.e.




[

    'allow' => true,

    'actions' => ['delete']

    'roles' => ['admin'],

],



I have added it but still i cant view my uploads page :mellow:

however when I dont use the matchcallback option i can view and do all other operations. I think its a bug in my match callback section though i cant seem to figure it out




public function behaviors()

    {

        $behaviors ['access']= [

         'class'=>AccessControl::className(),

	 'rules'=>[

	    [

		'allow'=>true,

                'actions'=>['delete','index'],

		'roles'=>['admin','client'],

		'matchCallback'=>function ($rule, $action) {

            

			$module =Yii::$app->controller->module->id;

			$action =Yii::$app->controller->action->id;

			$controller=Yii::$app->controller->id;

			$route="$controller/$action";

			$post =Yii::$app->request->post();

			if(\Yii::$app->user->can($route)){

				return true;

			}

		}

		],

	   ],

	 ];

	 return $behaviors;

    }

From AccessControl - "If no rule matches, the access will be denied". Check if this is the case here.