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

Utilizzando la struttura della tabella di cui sopra, l'applicazione utilizza sottoclassi di ActiveController, con override prepareDataProvider per limitare l'elenco di ciascuna Product un registra nel Userindex può vedere a quelli con corrispondenti supplierID valori. Qualcosa di simile nel metodo actions() di ProductController.Yii2 - Override CheckAccess nel resto 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]); 
}; 

Questo funziona bene, ma sto cercando di usare checkAccess() per limitare actionView() per un singolo Product.

Al momento, un loggato User può accedere a una Product cambiando il productID nell'URL, se l'hanno l'appropriato supplierID.

sembra che io non posso accedere al caso particolare di Product, per verificare la supplierID, fino a quando è tornato actionView() che è quando voglio che il controllo avvenga.

Posso ignorare checkAccess() per limitare l'accesso e lanciare l'appropriato ForbiddenHttpException?

+0

così , si desidera disabilitare l'accesso 'ID prodotto' dall'URL. –

+0

hmm, forse? non ci avevo pensato da quella direzione. pensavo di non restituire dati non consentiti ma potrebbe esserci una soluzione nel non avere il 'productID' come parametro' GET'. qualcosa su cui riflettere sicuramente ... –

risposta

4

Che dire di una funzione che controlla se un modello esiste:

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

Se productID è il vostro prodotto chiave primaria, quindi una richiesta di /products/1 verrà tradotto da Yii \ resto \ UrlRule-/products?productID=1.

In tal caso, quando productID è fornito come param, è possibile utilizzare beforeAction per fare un rapido controllo, se tale modello esiste & essere eseguita l'azione o lanciare un errore se non lo fa:

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

aggiornamento

Poiché la domanda è circa Override del metodo CheckAccess in riposo ActiveController Ho pensato che sarebbe stato utile lasciare un esempio.

Nel modo come Yii2 RIPOSO creata, tutti delete, update e view azioni richiamerà il metodo checkAccess volta l'istanza modello caricato:

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

Lo stesso vale per la create e index azioni tranne che non passeranno alcuna istanza di modello ad esso: call_user_func($this->checkAccess, $this->id).

Quindi, ciò che si sta cercando di fare (gettando un ForbiddenHttpException quando un utente sta cercando di visualizzare, aggiornare o eliminare un prodotto che non è il suo fornitore) può essere ottenuta anche in questo modo:

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; questo è simile a quello che ho al momento, usando un metodo helper (non 'checkAccess') che getta appropriatamente' actionMethods 'e chiamando il metodo all'inizio di ogni metodo di "azione" sottoposto a override. la tua soluzione sembra un po 'più idiota Yii. ho intenzione di dare la tua idea e farò rapporto. grazie –

+0

Prego. Ho sempre visto il metodo [checkAccess] (http://www.yiiframework.com/doc-2.0/guide-rest-controllers.html#performing-access-check) come il posto giusto per l'autorizzazione dell'autorizzazione prima dell'esecuzione o dell'utilizzo qualsiasi azione –

+0

Questa soluzione ha funzionato con me durante la creazione di percorsi di risorse nidificate come descritto [qui] (https://github.com/yiisoft/yii2/issues/9474) e dopo l'implementazione del filtro all'interno di IndexAction come illustrato [qui] (http: // stackoverflow .com/domande/25522462/yii2-resto-query # risposta-30.560.912). Il mio codice completo è [questo] (https://github.com/tunecino/Yii2_foundation-apps/blob/master/backend/api/modules/v1/controllers/ImageController.php) e funziona bene, ma sto anche seguendo la tua domanda perché sono anche interessato a diversi modi per risolverlo, specialmente ora dopo aver menzionato il metodo checkAccess. –

Problemi correlati