简体   繁体   中英

Yii2 - Override checkAccess in rest ActiveController

Product.supplierID = Supplier.supplierID
---------         ----------
|Product|---------|Supplier|
---------         ----------
                       |
                       |  Supplier.supplierID = User.supplierID
                       |
                   ---------
                   |  User |
                   ---------

Using the above table structure, the application uses sub-classes of ActiveController , with overridden prepareDataProvider to limit the index list of each Product a logged in User can see to those with matching supplierID values. Something like this in the actions() method of ProductController .

$actions['index']['prepareDataProvider'] = function($action)
{
    $query = Product::find();
    if (Yii::$app->user->can('supplier') &&
        Yii::$app->user->identity->supplierID) {
        $query->andWhere(['supplierID' => Yii::$app->user->identity->supplierID]);
    }
    return new ActiveDataProvider(['query' => $query]);
};

This works fine, however I'm looking to use checkAccess() to limit actionView() for a single Product .

At the moment, a logged in User can access a Product by changing the productID in the URL, whether or not the have the appropriate supplierID .

It looks like I can't access the particular instance of Product , to check the supplierID , until actionView() has returned which is when I want the check to happen.

Can I override checkAccess() to restrict access and throw the appropriate ForbiddenHttpException ?

What about a function that checks if a model exist :

protected function modelExist($id)
{
    return Product::find()
    ->where([ 'productID' => $id ])
    ->andWhere(['supplierID' => Yii::$app->user->identity->supplierID ])
    ->exists(); 
}

If productID is your Product Primary Key, then a request to /products/1 will be translated by yii\\rest\\UrlRule to /products?productID=1 .

In that case, when productID is provided as a param , you can use beforeAction to make a quick check if such model exist & let the action be executed or throw an error if it doesn't :

// this array will hold actions to which you want to perform a check
public $checkAccessToActions   = ['view','update','delete'];

public function beforeAction($action) {
    if (!parent::beforeAction($action)) return false;

        $params = Yii::$app->request->queryParams;

        if (isset($params['productID']) {
           foreach ($this->checkAccessToActions as $action) {
              if ($this->action->id === $action) {
                  if ($this->modelExist($params['productID']) === false)
                       throw new NotFoundHttpException("Object not found");
              }
           }
    }
    return true;
}

update

As the question is about Overriding the checkAccess method in rest ActiveController I thought it would be useful to leave an example.

In the way how Yii2 REST was designed, all of delete , update and view actions will invoke the checkAccess method once the model instance is loaded:

// code snippet from yii\rest\ViewAction
$model = $this->findModel($id);
if ($this->checkAccess) {
    call_user_func($this->checkAccess, $this->id, $model);
}

The same is true for the create and index actions except that they won't pass any model instance to it: call_user_func($this->checkAccess, $this->id) .

So what you are trying to do ( throwing a ForbiddenHttpException when a user is trying to view, update or delete a product he is not its supplier ) may also be achieved this way:

public function checkAccess($action, $model = null, $params = [])
{
    if ($action === 'view' or $action === 'update' or $action === 'delete') 
    {
        if ( Yii::$app->user->can('supplier') === false
             or Yii::$app->user->identity->supplierID === null
             or $model->supplierID !== \Yii::$app->user->identity->supplierID ) 
        {
             throw new \yii\web\ForbiddenHttpException('You can\'t '.$action.' this product.');
        }

    }
}

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