2015-10-16 14 views
5
Product.supplierID = Supplier.supplierID 
---------   ---------- 
|Product|---------|Supplier| 
---------   ---------- 
         | 
         | Supplier.supplierID = User.supplierID 
         | 
        --------- 
        | User | 
        --------- 

Stosując powyższą strukturę tabeli, aplikacja wykorzystuje podklas ActiveController, z zastąpionej prepareDataProvider aby ograniczyć listę index każdego Product zalogowanego User widać do tych z pasującymi supplierID wartości. Coś takiego w metodzie actions() z ProductController.Yii2 - Przestawianie checkAccess w spoczynku ActiveController

$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]); 
}; 

Działa to dobrze, ale szukam w użyciu checkAccess() ograniczyć actionView() dla pojedynczego Product.

Obecnie zalogowanego User może uzyskać dostęp do Product zmieniając productID w URL, czy mają odpowiedni supplierID.

Wygląda na to, że nie mogę uzyskać dostępu do konkretnej instancji Product, aby sprawdzić supplierID, aż zwróci się actionView(), kiedy chcę, aby to się stało.

Czy mogę zastąpić checkAccess(), aby ograniczyć dostęp i rzucić odpowiednią ForbiddenHttpException?

+0

tak , chcesz wyłączyć dostęp do identyfikatora "identyfikatora produktu" z adresu URL. –

+0

hmm, być może? nie myślałem o tym z tego kierunku. bardziej myślał o niezwracaniu danych, które są niedozwolone, ale może istnieć rozwiązanie polegające na braku parametru "identyfikator produktu" jako parametru "GET". coś do przemyślenia na pewno ... –

Odpowiedz

4

Co o funkcji, która sprawdza, czy istnieje model:

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

Jeśli productID jest podstawowy klucz produktu, a następnie wniosek do /products/1 będą tłumaczone przez Yii \ reszty \ UrlRule do /products?productID=1.

W takim przypadku, gdy productID jest jako param, można użyć beforeAction do szybkiego sprawdzenia, czy taki model istnieje & niech działanie realizowane lub wygeneruje błąd, jeśli nie:

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

aktualizacja

w tej kwestii jest o przesłanianie metody checkAccess w spoczynku ActiveController Pomyślałem, że warto zostawić przykład.

W sposób jak Yii2 REST został zaprojektowany, wszystkie delete, update i view działań wywoła metodę checkAccess raz instancja modelu jest załadowany:

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

To samo jest prawdą dla create i index działania z wyjątkiem tego, że nie przekazują do niego żadnej instancji modelu: call_user_func($this->checkAccess, $this->id).

Więc co próbujesz zrobić (rzucanie ForbiddenHttpException gdy użytkownik próbuje przeglądać, aktualizować lub usunąć produkt nie jest jej dostawca) można również uzyskać w ten sposób:

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

    } 
} 
+0

@SalemOerdany; jest to podobne do tego, co mam w tej chwili, używając metody pomocniczej (nie "checkAccess"), która rzuca odpowiednio niestandardowe 'actionMethods' i wywołując metodę na górze każdej przesłoniętej metody" działania ". twoje rozwiązanie wydaje się nieco bardziej idiomatyczne Yii. Zamierzam dać ci pomysł i złożyć raport. dzięki –

+0

Nie ma za co. Zawsze widziałem metodę [checkAccess] (http://www.yiiframework.com/doc-2.0/guide-rest-controllers.html#performing-access-check) jako właściwe miejsce autoryzacji uchwytów przed wykonaniem lub użyciem Wszelkie działania. –

+0

To rozwiązanie działało ze mną podczas budowania zagnieżdżonych tras zasobów zgodnie z opisem [tutaj] (https://github.com/yiisoft/yii2/issues/9474) i po wdrożeniu filtrowania w ramach IndexAction, jak pokazano [tutaj] (http: // stackoverflow .com/questions/25522462/yii2-rest-query # answer-30560912). Mój pełny kod to [to] (https://github.com/tunecino/Yii2_foundation-apps/blob/master/backend/api/modules/v1/controllers/ImageController.php) i działa dobrze, ale śledzę też Twoje pytanie, ponieważ jestem również zainteresowany różnymi sposobami rozwiązania tego problemu, szczególnie teraz, gdy wspomnisz o metodzie checkAccess. –