Yii2 - override checkAccess in ActiveController rest mode

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

Using the table structure above, the application uses ActiveController subclasses, with the overridden prepareDataProvider , to restrict the index list of each Product registered in User can see those that have the corresponding supplierID . Something like this in the actions() method of the 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 great, however I want to use checkAccess() to limit the actionView() for a single Product .

Currently, a registered User can access Product by changing the productID in the URL, regardless of whether he has a supplierID .

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

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

+5
source share
1 answer

How about a function that checks for a model:

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

If productID is the primary key of your product, the request for /products/1 will translate yii \ rest \ UrlRule to /products?productID=1 .

In this case, when the productID provided as a parameter, you can use beforeAction to do a quick check to see if such a model exists and allow the action to execute or throw an error if it isn'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 a question about Overriding the checkAccess method in ActiveController break mode I thought it would be useful to leave an example.

In the Yii2 REST design method, all delete , update and view actions will call the checkAccess method after loading the model instance:

 // 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 create and index actions, except that they will not pass it an instance of the model: call_user_func($this->checkAccess, $this->id) .

So, what are you trying to do (throwing a ForbiddenHttpException when a user tries to view, update or delete a product that he is not his supplier) can also be achieved as follows:

 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.'); } } } 
+4
source

Source: https://habr.com/ru/post/1233858/


All Articles