diff --git a/common/models/User.php b/common/models/User.php index d706022..1658563 100755 --- a/common/models/User.php +++ b/common/models/User.php @@ -3,10 +3,10 @@ namespace common\models; use Yii; -use yii\base\NotSupportedException; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; use yii\web\IdentityInterface; +use yii\web\UnauthorizedHttpException; /** * User model @@ -64,14 +64,25 @@ class User extends ActiveRecord implements IdentityInterface return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); } - /** - * {@inheritdoc} - */ - public static function findIdentityByAccessToken($token, $type = null) + public function generateAccessToken() { - throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); + $this->access_token = Yii::$app->security->generateRandomString(); + return $this->access_token; } + public static function findIdentityByAccessToken($token, $type = null) + { + $user = static::find()->where(['access_token' => $token, 'status' => self::STATUS_ACTIVE])->one(); + if (!$user) { + return false; + } + if (strtotime($user->access_token_expired_at) < time()) { + throw new UnauthorizedHttpException('the access - token expired ', -1); + } else { + return $user; + } + + } /** * Finds user by username * @@ -186,4 +197,16 @@ class User extends ActiveRecord implements IdentityInterface { $this->password_reset_token = null; } + + public function beforeSave($insert) + { + if (parent::beforeSave($insert)) { + if ($this->isNewRecord) { + $this->auth_key = Yii::$app->security->generateRandomString(); + } + return true; + } + return false; + } + } diff --git a/console/migrations/m210708_141557_add_access_columns_to_user_table.php b/console/migrations/m210708_141557_add_access_columns_to_user_table.php new file mode 100644 index 0000000..142cde5 --- /dev/null +++ b/console/migrations/m210708_141557_add_access_columns_to_user_table.php @@ -0,0 +1,21 @@ +addColumn('user', 'access_token', $this->string()); + $this->addColumn('user', 'access_token_expired_at', $this->dateTime()); + } + + public function safeDown() + { + $this->dropColumn('user', 'access_token'); + $this->dropColumn('user', 'access_token_expired_at'); + } +} diff --git a/frontend/config/main.php b/frontend/config/main.php index 7b2da72..7899e29 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -15,6 +15,15 @@ return [ 'modules' => [ 'api' => [ + 'components' => [ + 'user' => [ + 'identityClass' => 'frontend\modules\api\models\User', + 'enableAutoLogin' => true, + 'enableSession' => false, + 'class' => 'frontend\modules\api\models\User', + //'identityCookie' => ['name' => '_identity-api', 'httpOnly' => true], + ], + ], 'class' => 'frontend\modules\api\Api', ], 'access' => [ @@ -32,6 +41,9 @@ return [ 'request' => [ 'csrfParam' => '_csrf-frontend', 'baseUrl' => '', + 'parsers' => [ + 'application/json' => 'yii\web\JsonParser', + ], ], 'user' => [ 'identityClass' => 'common\models\User', diff --git a/frontend/modules/api/controllers/ReportsController.php b/frontend/modules/api/controllers/ReportsController.php index 42ffdb0..40884d1 100644 --- a/frontend/modules/api/controllers/ReportsController.php +++ b/frontend/modules/api/controllers/ReportsController.php @@ -3,16 +3,32 @@ namespace frontend\modules\api\controllers; use common\behaviors\GsCors; +use common\models\Reports; +use frontend\modules\api\models\ReportSearchForm; +use JsonException; +use Yii; +use yii\filters\auth\CompositeAuth; +use yii\filters\auth\HttpBearerAuth; +use yii\filters\ContentNegotiator; +use yii\rest\Controller; +use yii\web\BadRequestHttpException; +use yii\web\NotFoundHttpException; +use yii\web\Response; -class ReportsController extends \yii\rest\Controller +class ReportsController extends Controller { + public function init() + { + parent::init(); // TODO: Change the autogenerated stub + } + public function behaviors() { return [ [ - 'class' => \yii\filters\ContentNegotiator::className(), + 'class' => ContentNegotiator::className(), 'formats' => [ - 'application/json' => \yii\web\Response::FORMAT_JSON, + 'application/json' => Response::FORMAT_JSON, ], ], 'corsFilter' => [ @@ -27,13 +43,84 @@ class ReportsController extends \yii\rest\Controller 'X-Requested-With' ], ] + ], + 'authenticator' => [ + 'class' => CompositeAuth::class, + 'authMethods' => [ + HttpBearerAuth::class, + ], ] ]; } - public function actionIndex() + public function actionIndex(): array { + $reportsModel = new ReportSearchForm(); + $params = Yii::$app->request->get(); + $reportsModel->attributes = $params; + + if(!$reportsModel->validate()){ + return $reportsModel->errors; + } + return $reportsModel->byParams(); + } + + public function actionCreate() + { + $reportsModel = new Reports(); + + $params = Yii::$app->request->get(); + $reportsModel->attributes = $params; + + if(!$reportsModel->validate()){ + throw new BadRequestHttpException(json_encode($reportsModel->errors)); + } + + $reportsModel->save(); + + return $reportsModel->toArray(); + } + + public function actionDelete() + { + $id = Yii::$app->request->get('id'); + + $report = Reports::findOne($id); + + if(null === $report) { + throw new NotFoundHttpException('Report not found'); + } + + if(false === ($report->delete())) { + throw new JsonException('Report not deleted'); + } + + return true; + } + + public function actionUpdate(): array + { + $params = Yii::$app->request->get(); + + $reportsModel = Reports::findone($params['id']); + if(!isset($reportsModel)) { + throw new NotFoundHttpException('report not found'); + } + + if(isset($params['user_card_id'])) { + throw new JsonException('constraint by user_card_id'); + } + + $reportsModel->attributes = $params; + + if(!$reportsModel->validate()){ + throw new BadRequestHttpException(json_encode($reportsModel->errors)); + } + + $reportsModel->save(); + + return $reportsModel->toArray(); } } diff --git a/frontend/modules/api/controllers/UserController.php b/frontend/modules/api/controllers/UserController.php new file mode 100644 index 0000000..7a6c2a4 --- /dev/null +++ b/frontend/modules/api/controllers/UserController.php @@ -0,0 +1,57 @@ + ContentNegotiator::class, + 'formats' => [ + 'application/json' => Response::FORMAT_JSON, + ], + ], + 'authenticatior' => [ + 'class' => QueryParamAuth::class, //implement access token authentication + 'except' => ['login'], // no need to verify the access token method, pay attention to distinguish between $noAclLogin + ] + ]); + } + + public function actions() + { + $action = parent::actions(); // TODO: Change the autogenerated stub + unset($action['index']); + unset($action['create']); + unset($action['update']); + unset($action['delete']); + } + + + public function actionLogin() + { + $model = new LoginForm(); + if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) { + return [ + 'access_token' => $model->login(), + ]; + } else { + throw new BadRequestHttpException(json_encode($model->errors)); + } + } +} diff --git a/frontend/modules/api/models/LoginForm.php b/frontend/modules/api/models/LoginForm.php new file mode 100644 index 0000000..30a75ec --- /dev/null +++ b/frontend/modules/api/models/LoginForm.php @@ -0,0 +1,70 @@ +hasErrors()) { + $user = $this->getUser(); + if (!$user || !$user->validatePassword($this->password)) { + $this->addError($attribute, 'Incorrect username or password.'); + } + } + } + + public function login() + { + if ($this->validate()) { + //return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); + if ($this->getUser()) { + $access_token = $this->_user->generateAccessToken(); + $this->_user->access_token_expired_at = date('Y-m-d', time() + static::EXPIRE_TIME); + $this->_user->save(); + Yii::$app->user->login($this->_user, static::EXPIRE_TIME); + return $access_token; + } + } + return false; + } + + protected function getUser() + { + if ($this->_user === null) { + $this->_user = User::findByUsername($this->username); + } + + return $this->_user; + } +} diff --git a/frontend/modules/api/models/ReportSearchForm.php b/frontend/modules/api/models/ReportSearchForm.php new file mode 100644 index 0000000..40642b9 --- /dev/null +++ b/frontend/modules/api/models/ReportSearchForm.php @@ -0,0 +1,54 @@ +limit = 10; + $this->offset = 0; + $this->user_id = null; + + $this->toDate = date('Y-m-d', time()); + $this->fromDate = date('Y-m-01', time()); + + parent::__construct($config); + } + + public function rules(): array + { + return [ + [['fromDate', 'toDate'], 'date', 'format' => 'php:Y-m-d'], + [['limit', 'offset', 'user_id'], 'integer', 'min' => 0], + ]; + } + + public function byParams() + { + $queryBuilder = Reports::find() + ->andWhere(['between', 'created_at', $this->fromDate, $this->toDate, $this->user_id]) + ->limit($this->limit) + ->offset($this->offset); + + if(isset($this->user_id)) { + $userCardId = UserCard::findByUserId($this->user_id)->id; + $queryBuilder->andWhere(['user_card_id' => $userCardId]); + } + + $data = $queryBuilder->all(); + + return $data; + } +} \ No newline at end of file diff --git a/frontend/modules/card/models/UserCard.php b/frontend/modules/card/models/UserCard.php index 8f736f0..923904e 100755 --- a/frontend/modules/card/models/UserCard.php +++ b/frontend/modules/card/models/UserCard.php @@ -3,6 +3,7 @@ namespace frontend\modules\card\models; use common\models\CardSkill; +use Yii; use yii\helpers\ArrayHelper; class UserCard extends \common\models\UserCard @@ -15,7 +16,7 @@ class UserCard extends \common\models\UserCard parent::init(); $skill = ArrayHelper::getColumn( - CardSkill::find()->where(['card_id' => \Yii::$app->request->get('id')])->all(), + CardSkill::find()->where(['card_id' => Yii::$app->request->get('id')])->all(), 'skill_id' ); @@ -26,7 +27,7 @@ class UserCard extends \common\models\UserCard public function afterSave($insert, $changedAttributes) { - $post = \Yii::$app->request->post('UserCard'); + $post = Yii::$app->request->post('UserCard'); if ($post['skill']) { CardSkill::deleteAll(['card_id' => $this->id]); @@ -42,4 +43,9 @@ class UserCard extends \common\models\UserCard parent::afterSave($insert, $changedAttributes); // TODO: Change the autogenerated stub } + + public static function findByUserId($userId): ?UserCard + { + return self::findOne(['id_user' => $userId]); + } }