1

I'm trying to create rest api for my application to get the data in my android app. This is my controller

<?php

namespace api\modules\v1\controllers;

use yii\rest\ActiveController;
use yii\filters\auth\QueryParamAuth;

/**
 * Tk103 Controller API
 */
class Tk103Controller extends ActiveController
{
    public $modelClass = 'api\modules\v1\models\Tk103CurrentLocation';  

    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => QueryParamAuth::className(),
        ];
        return $behaviors;
    }  

}

I added access_token column in my user table, implemented findIdentityByAccessToken() in User Model and calling this URL http://localhost:7872/api/v1/tk103s?access-token=abcd

This is working great and returning data if and only if access_token matches with any single user in the table. I checked QueryParamAuth class and found that QueryParamAuth::authenticate() returns $identity after successful authentication.

Currently this url is returning whole data of my table.

What I want is(after authentication):

  1. Get user id/username of the requester.
  2. Based on that id/username, the data related to him as per relations of tables in db. (currently whole rows are being returned but I want only few that are matching with the current requester/user)

I tried but didn't getting any clue to catch returned $identity of user after authentication. And I know it is possible too to make this work. Help me out folks to create magic.

Choxx
  • 945
  • 1
  • 24
  • 46

1 Answers1

2
  1. Get user id/username of the requester.

That user instance you did return within the findIdentityByAccessToken method should be accessible any where inside your app within Yii::$app->user->identity. And should hold all the attributes retreived from DB. here is a quick example of using it to check access within the checkAccess method of the ActiveController class:

public function checkAccess($action, $model = null, $params = [])
{
    // only an image owner can request the related 'delete' or 'update' actions
    if ($action === 'update' or $action === 'delete') {
        if ($model->user_id !== \Yii::$app->user->identity->id)
            throw new \yii\web\ForbiddenHttpException('You can only '.$action.' images that you\'ve added.');
    }
}

Note that the checkAccess is by default an empty method that is manually called inside all the built-in actions in ActiveController. the Idea is to pass the action ID and the model instance to it just after retrieving it from DB and before modifying it so we can do extra checks. If you just need to perform checks by actions ID then yii\filters\AccessControl may be enough but inside checkAccess you are expecting to also get the model instance itself so it is important to note that when building your own actions or overriding existing onces. be sure to manually invoke it the same way it is done in UpdateAction.php or DeleteAction.php.


  1. whole rows are being returned but I want only few .. matching with .. current requester/user

It depends on how your data is structured. You can override ActiveController's actions to filter results before outputting them, it can be handled in the related SearchModel class if you are using one or it can be handled in model. A quick tip may be by simply overriding the find method inside your model:

public static function find()
{
    return parent::find()->where(['user_id' => Yii::$app->user->getId()]); // or Yii::$app->user->identity->id
}

Note that this works only when using ActiveRecord. Which means when using this:

$images = Image::find()->all();

The find method we just overriden will be filtered by default by always including that where condition before generating the DB query. Also note the default built-in actions in ActiveController are using ActiveRecords but if you are using actions where you are constructing the SQL queries using the Query Builder then you should manually do the filtering.

The same can be done if using ActiveQuery (maybe better explained here) by doing this:

public static function find()
{
    $query = new \app\models\Image(get_called_class());
    return $query->andWhere(['user_id' => Yii::$app->user->getId()]);
}
Community
  • 1
  • 1
Salem Ouerdani
  • 7,596
  • 3
  • 40
  • 52
  • Awesome dude.. Solution 1 worked for me as per my requirement. However, I'm not able to follow 2nd one. However I did find it's solution by creating a separate action and getting required data with activedataprovider. – Choxx Apr 04 '16 at 16:49
  • I also need one more suggestion, Is it a good approach to check user's access in checkAccess() in controller? If it passes test then I will provide him data as per request otherwise reject it. Is it right approach? If not let me know the better one please.. – Choxx Apr 04 '16 at 16:52
  • 1
    checkAccess is powerful if you are invoking it where it should be. [RBAC](http://www.yiiframework.com/doc-2.0/guide-security-authorization.html#rbac) may also be an alternative. It always depends on what you are building. I just updated both answer parts. I hope it is better explained now. – Salem Ouerdani Apr 05 '16 at 08:59
  • one more thing. if you don't need to do the filtering in too many actions or if somewhere you do expect to get all images by invoking `Image::find()->all()` then creating a separate action with a separate activedataprovider as you did is a much better solution that the one I gave. – Salem Ouerdani Apr 05 '16 at 09:16
  • 1
    @Samel thanks for the reply. Now I think I'm being a bit more compatible with yii. – Choxx Apr 05 '16 at 12:13