From 7a55ca9ff38ca9c6aabbac40cd1d0dfc2518f0b4 Mon Sep 17 00:00:00 2001 From: Roland Schneider Date: Mon, 4 Oct 2021 18:13:32 +0200 Subject: [PATCH] assign trainers to user; add email to jwt token --- backend/components/AdminMenuStructure.php | 115 +++++++- backend/controllers/UserController.php | 88 ++++--- backend/models/UserCreate.php | 31 ++- backend/models/UserUpdate.php | 55 +++- backend/views/user/_form.php | 54 +++- backend/views/user/create.php | 1 + backend/views/user/update.php | 3 +- common/components/RoleDefinition.php | 213 ++++++++------- common/models/Account.php | 247 ++++++++++-------- common/models/User.php | 36 +-- common/modules/event/models/EventSearch.php | 2 + customer/app/package-lock.json | 5 + customer/app/package.json | 1 + .../app/src/app/_helpers/jwt.interceptor.ts | 48 ++-- .../fit-navigation.component.ts | 4 +- .../secured-layout.component.html | 3 + .../secured-layout.component.ts | 4 + .../app/services/authentication.service.ts | 62 +++-- customer/app/src/app/state/app.actions.ts | 6 + customer/app/src/app/state/app.state.ts | 42 ++- customerapi/controllers/LoginController.php | 1 + 21 files changed, 671 insertions(+), 350 deletions(-) diff --git a/backend/components/AdminMenuStructure.php b/backend/components/AdminMenuStructure.php index a268768..93effef 100644 --- a/backend/components/AdminMenuStructure.php +++ b/backend/components/AdminMenuStructure.php @@ -172,19 +172,68 @@ class AdminMenuStructure ///////////////////////////// // Group Training ///////////////////////////// - if (RoleDefinition::isLoggedUser()) { - $items = []; - $items[] = ['label' => 'Felszerelés', 'url' => ['/event-equipment-type']]; - $items[] = ['label' => 'Edzők', 'url' => ['/trainer']]; - $items[] = ['label' => 'Termek', 'url' => ['/room']]; - $items[] = ['label' => 'Esemény típusok', 'url' => ['/event-type']]; - $items[] = ['label' => 'Események', 'url' => ['/event/event/index']]; - $items[] = ['label' => 'Órarend', 'url' => ['/event/event/timetable']]; - $items[] = ['label' => 'Hét másolása', 'url' => ['/event/event/copy-week']]; - $this->menuItems[] = ['label' => 'Csoportos edzés', 'url' => $this->emptyUrl, - 'items' => $items - ]; - } + $items = []; +// $items[] = ['label' => 'Felszerelés', 'url' => ['/event-equipment-type'], 'role' => [RoleDefinition::$ROLE_ADMIN]]; + $items[] = [ + 'label' => 'Edzők', + 'url' => ['/trainer'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + ] + ]; + $items[] = ['label' => 'Termek', + 'url' => ['/room'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + ] + ]; + $items[] = ['label' => 'Esemény típusok', + 'url' => ['/event-type'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + ] + ]; + $items[] = [ + 'label' => 'Események', + 'url' => ['/event/event/index'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + RoleDefinition::$ROLE_TRAINER + ] + ]; + $items[] = [ + 'label' => 'Órarend', + 'url' => ['/event/event/timetable'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + RoleDefinition::$ROLE_TRAINER + ] + ]; + $items[] = [ + 'label' => 'Hét másolása', + 'url' => ['/event/event/copy-week'], + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + RoleDefinition::$ROLE_TRAINER + ] + ]; + $this->menuItems[] = [ + 'label' => 'Csoportos edzés', + 'url' => $this->emptyUrl, + 'items' => $items, + 'role' => [ + RoleDefinition::$ROLE_ADMIN, + RoleDefinition::$ROLE_EMPLOYEE, + RoleDefinition::$ROLE_TRAINER + ] + ]; + ///////////////////////////// // Development @@ -198,10 +247,50 @@ class AdminMenuStructure ]; } + $this->menuItems = $this->filterMenu($this->menuItems); + } } + protected function filterMenu($menuItems) + { + $result = []; + foreach ($menuItems as $item) { + $filteredItem = $this->filterMenuItem($item); + if (isset($filteredItem)) { + $result[] = $filteredItem; + } + } + return $result; + } + + protected function filterMenuItem($menuItem) + { + $result = $menuItem; + if (isset($menuItem)) { + if (isset($menuItem['role'])) { + $roles = $menuItem['role']; + $canAny = RoleDefinition::canAny($roles); + if ($canAny === false) { + $result = null; + } else { + if (isset($menuItem['items'])) { + $result['items'] = []; + $items = $menuItem['items']; + foreach ($items as $subItem) { + $filteredItem = $this->filterMenuItem($subItem); + if (isset($filteredItem)) { + $result['items'][] = $filteredItem; + } + } + } + } + } + } + return $result; + } + protected function addLoginMainMenu() { if (Yii::$app->user->isGuest) { diff --git a/backend/controllers/UserController.php b/backend/controllers/UserController.php index 60c42f4..70892a2 100644 --- a/backend/controllers/UserController.php +++ b/backend/controllers/UserController.php @@ -2,10 +2,13 @@ namespace backend\controllers; +use common\models\Trainer; +use common\models\UserTrainerAssignment; use Yii; use common\models\User; use backend\models\UserSearch; use backend\models\UserCreate; +use yii\web\BadRequestHttpException; use yii\web\Controller; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; @@ -20,7 +23,7 @@ use common\components\RoleDefinition; */ class UserController extends \backend\controllers\BackendController { - + public function behaviors() { @@ -45,8 +48,8 @@ class UserController extends \backend\controllers\BackendController ], ]; } - - + + /** * Lists all User models. * @return mixed @@ -55,8 +58,8 @@ class UserController extends \backend\controllers\BackendController { $searchModel = new UserSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - + + return $this->render('index', [ 'searchModel' => $searchModel, @@ -84,35 +87,48 @@ class UserController extends \backend\controllers\BackendController public function actionCreate() { $model = new UserCreate(); - + $accounts = Account::readAccounts(); + $trainers = Trainer::find()->all(); if ($model->load(Yii::$app->request->post()) && $model->save()) { - + $this->updateAccountAssignments($model); - + $this->updateTrainerAssignments($model); + return $this->redirect(['index' ]); - - } - + + } + return $this->render('create', [ 'model' => $model, 'accounts' => $accounts, + 'trainers' => $trainers, ]); } - + public function updateAccountAssignments($model){ - - echo "saving accounts"; + UserAccountAssignment::deleteAll(['id_user' => $model->id]); foreach ( $model->selected_accounts as $id_account ){ - echo "saving account"; $uaa = new UserAccountAssignment(); $uaa->id_user = $model->id; $uaa->id_account = $id_account; $uaa->save(); } - + + } + + public function updateTrainerAssignments($model){ + + UserTrainerAssignment::deleteAll(['id_user' => $model->id]); + foreach ( $model->selected_trainers as $id_trainer ){ + $uaa = new UserTrainerAssignment(); + $uaa->id_user = $model->id; + $uaa->id_trainer = $id_trainer; + $uaa->save(); + } + } /** @@ -124,7 +140,7 @@ class UserController extends \backend\controllers\BackendController public function actionUpdate($id) { $model = UserUpdate::findOne(['id' => $id]); - + if ( Yii::$app->authManager->checkAccess($model->id, 'admin')){ $model->role = 'admin'; } else if ( Yii::$app->authManager->checkAccess($model->id, 'employee')){ @@ -132,28 +148,31 @@ class UserController extends \backend\controllers\BackendController }else if ( Yii::$app->authManager->checkAccess($model->id, 'reception')){ $model->role = 'reception'; } - + if ( $model == null ){ throw new NotFoundHttpException('The requested page does not exist.'); } - + $accounts = Account::readAccounts(); - $this->applyAccounts($model); - + $trainers = Trainer::find()->all(); + $this->applyTrainers($model); if ($model->load(Yii::$app->request->post()) && $model->save()) { + $this->updateAccountAssignments($model); + $this->updateTrainerAssignments($model); return $this->redirect(['view', 'id' => $model->id]); - } else { } return $this->render('update', [ 'model' => $model, 'accounts' => $accounts, + 'trainers' => $trainers, + ]); } - + private function applyAccounts($model ){ $assignedAccounts = $model->userAccountAssignments; foreach ($assignedAccounts as $acc ){ @@ -161,6 +180,13 @@ class UserController extends \backend\controllers\BackendController } } + private function applyTrainers($model ){ + $assignedTrainers = $model->userTrainerAssignments; + foreach ($assignedTrainers as $acc ){ + $model->selected_trainers[] = $acc->id_trainer; + } + } + /** * Deletes an existing User model. * If deletion is successful, the browser will be redirected to the 'index' page. @@ -171,12 +197,12 @@ class UserController extends \backend\controllers\BackendController { $user = $this->findModel($id); - + $user->updateAttributes(['status' => User::STATUS_DELETED]); return $this->redirect(['index']); } - + /** * Creates a new User model. * If creation is successful, the browser will be redirected to the 'view' page. @@ -185,17 +211,17 @@ class UserController extends \backend\controllers\BackendController public function actionRole() { $model = new \backend\models\RoleForm(); - + $model->availablePermissions = [ [ 'name' => "reception.transfers", 'description' => 'Tranzakciók' ] ]; - - - - + + + + if ($model->load(Yii::$app->request->post()) ) { if ( $model->validate() && $model->save()){ Yii::$app->session->setFlash('success', 'Jogosultságok elmentve'); @@ -209,7 +235,7 @@ class UserController extends \backend\controllers\BackendController $model->permissions[] = $child->name; } } - + return $this->render('role', [ 'model' => $model, ]); diff --git a/backend/models/UserCreate.php b/backend/models/UserCreate.php index 671cc14..f27d79e 100644 --- a/backend/models/UserCreate.php +++ b/backend/models/UserCreate.php @@ -9,9 +9,10 @@ class UserCreate extends User{ public $password_plain; public $password_repeat; public $selected_accounts = []; - + public $selected_trainers = []; + public $role; - + /** * @inheritdoc */ @@ -25,28 +26,34 @@ class UserCreate extends User{ } } ], + ['selected_trainers',function ($attribute, $params) { + if (!is_array($this->$attribute)) { + $this->addError($attribute, 'Invalid array'); + } + } + ], ['email' ,'email' ], ['email' ,'unique' ], ['username' ,'unique' ], [['password_plain' ,'password_repeat'] ,'string','min' =>6 ], [['password_repeat'] ,'validatePasswordRepeat' ], - + [['role'], 'required'], [['role'], 'string', 'max' => 20], ['status', 'default', 'value' => self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], ]; } - + public function validatePasswordRepeat($attribute,$params){ - + if ( !$this->hasErrors()){ if ( $this->password_plain != $this->password_repeat ){ $this->addError($attribute, Yii::t('app', 'Jelszó és jelszó újra nem egyezik!') ); } } } - + public function attributeLabels(){ return [ 'status' => 'Státusz', @@ -55,10 +62,10 @@ class UserCreate extends User{ 'created_at' =>'Létrehozás dátuma', 'password_plain' => Yii::t('app','Jelszó'), 'password_repeat' => Yii::t('app','Jelszó újra'), - + ]; } - + public function beforeSave($insert){ if ( parent::beforeSave($insert)){ if ( $insert ){ @@ -70,12 +77,12 @@ class UserCreate extends User{ return false; } } - + public function afterSave($insert, $changedAttributes){ - parent::afterSave($insert, $changedAttributes); + parent::afterSave($insert, $changedAttributes); $am = Yii::$app->authManager; $role = $am->getRole($this->role); Yii::$app->authManager->assign($role, $this->id); } - -} \ No newline at end of file + +} diff --git a/backend/models/UserUpdate.php b/backend/models/UserUpdate.php index 35f3694..721ccdc 100644 --- a/backend/models/UserUpdate.php +++ b/backend/models/UserUpdate.php @@ -9,9 +9,10 @@ class UserUpdate extends User { public $password_plain; public $password_repeat; public $selected_accounts = []; + public $selected_trainers = []; public $role; - + /** * @inheritdoc * @formatter:off @@ -21,25 +22,59 @@ class UserUpdate extends User { return [ [['username','email'], 'required' ], ['email' ,'email' ], - ['email' ,'unique' , 'targetClass' => User::className(), 'targetAttribute' => 'email'], - ['username' ,'unique', 'targetClass' => User::className(), 'targetAttribute' => 'username'], +// ['email' ,'unique' , 'targetClass' => User::className(), 'targetAttribute' => 'email'], +// ['username' ,'unique', 'targetClass' => User::className(), 'targetAttribute' => 'username'], [['password_plain' ,'password_repeat'] ,'string','min' =>6 ], [['password_repeat'] ,'validatePasswordRepeat' ], + [['username'] ,'validateUsername' ], + [['email'] ,'validateEmail' ], ['selected_accounts',function ($attribute, $params) { if (!is_array($this->$attribute)) { $this->addError($attribute, 'Invalid array'); } } ], + ['selected_trainers',function ($attribute, $params) { + if (!is_array($this->$attribute)) { + $this->addError($attribute, 'Invalid array'); + } + } + ], [['role'], 'required'], [['role'], 'string', 'max' => 20], - + ['status', 'default', 'value' => self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], - + ]; } - + + public function validateEmail($attribute, $params){ + /** @var User $user */ + $user = User::find() + ->andWhere(['email' => $this->email])->one(); + + if (isset($user)){ + if ( $user->id != $this->id ){ + $this->addError($attribute,'Az email cím már használatban van!'); + } + } + + } + + public function validateUsername($attribute, $params){ + /** @var User $user */ + $user = User::find() + ->andWhere(['username' => $this->username])->one(); + + if (isset($user)){ + if ( $user->id != $this->id ){ + $this->addError($attribute,'A felhasználónév már használatban van!'); + } + } + + } + /** * @formatter:on */ @@ -53,14 +88,14 @@ class UserUpdate extends User { } } public function attributeLabels() { - return [ - + return [ + 'status' => 'Státusz', 'email' => 'E-mail', 'username' => 'Felhasználónév', 'created_at' => 'Létrehozás dátuma', 'password_plain' => Yii::t ( 'app', 'Jelszó' ), - 'password_repeat' => Yii::t ( 'app', 'Jelszó újra' ) + 'password_repeat' => Yii::t ( 'app', 'Jelszó újra' ) ] ; } @@ -78,7 +113,7 @@ class UserUpdate extends User { } } public function afterSave($insert, $changedAttributes){ - parent::afterSave($insert, $changedAttributes); + parent::afterSave($insert, $changedAttributes); $am = Yii::$app->authManager; $am->revokeAll($this->id); $role = $am->getRole($this->role); diff --git a/backend/views/user/_form.php b/backend/views/user/_form.php index 70a6870..1ad0728 100644 --- a/backend/views/user/_form.php +++ b/backend/views/user/_form.php @@ -1,5 +1,8 @@ -field($model, 'password_repeat')->passwordInput() ?> field($model, 'role')->dropDownList($roleOptions) ?> - selected_accounts; - + $selectedTrainers = $model->selected_trainers; + ?>

Engedélyezett kasszák

@@ -52,7 +58,7 @@ asort($roleOptions); 'checkboxOptions' => function ($model, $key, $index, $column) use ($selectedAccounts){ $result = []; $result['value'] = $model->id_account ; - + if ( isset($selectedAccounts) ){ if ( is_array($selectedAccounts) ){ if ( array_search($model->id_account , $selectedAccounts ) !== false){ @@ -60,15 +66,47 @@ asort($roleOptions); } } } - + return $result; } ], [ 'attribute' => 'name' ], ], ])?> + + +

Engedélyezett edzők

+ new ArrayDataProvider( [ + 'allModels' => $trainers, + 'sort' => false, + 'pagination' => false, + ]), + 'columns' => [ + [ + 'class' => 'yii\grid\CheckboxColumn', + 'name' => (new ReflectionClass( $model->classname() ))->getShortName() . '[selected_trainers]', + 'checkboxOptions' => function ($model, $key, $index, $column) use ($selectedTrainers){ + $result = []; + $result['value'] = $model->id ; + + if ( isset($selectedTrainers) ){ + if ( is_array($selectedTrainers) ){ + if ( array_search($model->id , $selectedTrainers ) !== false){ + $result['checked'] = 'checked' ; + } + } + } + + return $result; + } + ], + [ 'attribute' => 'name' ], + ], + ])?> +
- isNewRecord ? Yii::t('app', 'Mentés') : Yii::t('app', 'Mentés'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> + $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
diff --git a/backend/views/user/create.php b/backend/views/user/create.php index e84dfba..3507975 100644 --- a/backend/views/user/create.php +++ b/backend/views/user/create.php @@ -17,6 +17,7 @@ $this->params['breadcrumbs'][] = $this->title; render('_form', [ 'model' => $model, 'accounts' => $accounts, + 'trainers' => $trainers, ]) ?> diff --git a/backend/views/user/update.php b/backend/views/user/update.php index 3508fd2..d5694f3 100644 --- a/backend/views/user/update.php +++ b/backend/views/user/update.php @@ -16,7 +16,8 @@ $this->params['breadcrumbs'][] = Yii::t('backend/user', 'Update'); render('_form', [ 'model' => $model, - 'accounts' => $accounts + 'accounts' => $accounts, + 'trainers' => $trainers, ]) ?> diff --git a/common/components/RoleDefinition.php b/common/components/RoleDefinition.php index e5e4468..3a8d95a 100644 --- a/common/components/RoleDefinition.php +++ b/common/components/RoleDefinition.php @@ -1,118 +1,147 @@ Yii::t('common/role', 'Reception'), + 'admin' => Yii::t('common/role', 'Administrator'), + 'employee' => Yii::t('common/role', 'Employee'), + 'Trainer' => Yii::t('common/role', 'Edző'), + ]; + } - public static function roleLabels(){ - return [ - 'reception' => Yii::t('common/role' ,'Reception'), - 'admin' => Yii::t('common/role' ,'Administrator'), - 'employee' => Yii::t('common/role' ,'Employee'), - 'Trainer' => Yii::t('common/role' ,'Edző'), - ]; - } - - public static function getRoleLabel($role){ - $result = null; - $roleLabels = self::roleLabels(); - if ( array_key_exists($role, $roleLabels)){ - $result = $roleLabels[$role]; - } - return $result; - } + public static function getRoleLabel($role) + { + $result = null; + $roleLabels = self::roleLabels(); + if (array_key_exists($role, $roleLabels)) { + $result = $roleLabels[$role]; + } + return $result; + } - public static function roleDefinitions(){ - return [ - 'employee' => [ - 'canAllow' => [ 'employee'], - ], - 'admin' => [ - 'canAllow' => ['admin','reception','employee'], - ], - 'reception' => [ - 'canAllow' => [ ], - ], - ]; - } + public static function roleDefinitions() + { + return [ + 'employee' => [ + 'canAllow' => ['employee'], + ], + 'admin' => [ + 'canAllow' => ['admin', 'reception', 'employee'], + ], + 'reception' => [ + 'canAllow' => [], + ], + ]; + } - public static function getRoleDefinition($role){ - $defs = self::roleDefinitions(); - $result = null; - if ( array_key_exists($role, $defs)){ - $result = $defs[$role]; - } - $result = $defs[$role]; - return $result; - } + public static function getRoleDefinition($role) + { + $defs = self::roleDefinitions(); + $result = null; + if (array_key_exists($role, $defs)) { + $result = $defs[$role]; + } + $result = $defs[$role]; + return $result; + } - public static function getRolesCanAllow($role){ - $result = []; - $def = self::getRoleDefinition($role); - if ( isset($def)){ - $result = $def['canAllow']; - } + public static function getRolesCanAllow($role) + { + $result = []; + $def = self::getRoleDefinition($role); + if (isset($def)) { + $result = $def['canAllow']; + } - return $result; - } + return $result; + } - public static function can($role){ - $result = false; - if ( !Yii::$app->user->isGuest ){ - if ( isset( $role)){ - if ( is_array($role)){ - foreach ($role as $r){ - $result |= Yii::$app->user->can($r); - } - }else if ( is_string($role)){ - $result = Yii::$app->user->can($role); - } - } - } - return $result; - } + public static function can($role) + { + $result = false; + if (!Yii::$app->user->isGuest) { + if (isset($role)) { + if (is_array($role)) { + foreach ($role as $r) { + $result |= Yii::$app->user->can($r); + } + } else if (is_string($role)) { + $result = Yii::$app->user->can($role); + } + } + } + return $result; + } - public static function isAdmin(){ - return self::can('admin'); - } + public static function canAny($roles) + { + foreach ($roles as $role) { + if (self::can($role)) { + return true; + } + } + return false; + } - public static function isReception(){ - return self::can('reception'); - } + public static function isAdmin() + { + return self::can('admin'); + } - public static function isEmployee(){ - return self::can('employee'); - } + public static function isReception() + { + return self::can('reception'); + } - public static function isTrainer(){ - return self::can('trainer'); - } + public static function isEmployee() + { + return self::can('employee'); + } + + public static function isTrainer() + { + return self::can('trainer'); + } - public static function isLoggedUser(){ + public static function isLoggedUser() + { return self::isTrainer() || self::isAdmin() || self::isEmployee() || self::isReception(); } - /* - * [ - * 'role1' => 'template1', - * 'role2' => 'template2, - * ] - * */ - public static function getRoleTemplate($templates){ - $result = ""; - foreach ($templates as $role => $template ){ - if ( Yii::$app->user->can($role)){ - $result = $template; - break; - } - } - return $result; - } + + /* + * [ + * 'role1' => 'template1', + * 'role2' => 'template2, + * ] + * */ + public static function getRoleTemplate($templates) + { + $result = ""; + foreach ($templates as $role => $template) { + if (Yii::$app->user->can($role)) { + $result = $template; + break; + } + } + return $result; + } } diff --git a/common/models/Account.php b/common/models/Account.php index 4e98310..bd347b8 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -12,22 +12,22 @@ use yii\helpers\ArrayHelper; * This is the model class for table "account". * * @property integer $id_account - * @property string $name + * @property string $name * @property integer $status * @property integer $type - * @property string $created_at - * @property string $updated_at + * @property string $created_at + * @property string $updated_at * @property integer $log_card_read_in_reception */ class Account extends \yii\db\ActiveRecord { - - const STATUS_DELETED = 0; - const STATUS_ACTIVE = 10; - - const TYPE_ALL = 0; - const TYPE_VALUE_HIDDEN = 10; - + + const STATUS_DELETED = 0; + const STATUS_ACTIVE = 10; + + const TYPE_ALL = 0; + const TYPE_VALUE_HIDDEN = 10; + /** * @inheritdoc */ @@ -41,13 +41,15 @@ class Account extends \yii\db\ActiveRecord */ public function behaviors() { - return [ - [ 'class' => TimestampBehavior::className(), - 'value' => function(){ return date('Y-m-d H:i:s' ); } - ] - ]; + return [ + ['class' => TimestampBehavior::className(), + 'value' => function () { + return date('Y-m-d H:i:s'); + } + ] + ]; } - + /** * @inheritdoc */ @@ -55,8 +57,8 @@ class Account extends \yii\db\ActiveRecord { return [ [['name', 'type'], 'required'], - [['name', ], 'unique'], - [['status', 'type','log_card_read_in_reception'], 'integer'], + [['name',], 'unique'], + [['status', 'type', 'log_card_read_in_reception'], 'integer'], [['name'], 'string', 'max' => 64] ]; } @@ -76,45 +78,51 @@ class Account extends \yii\db\ActiveRecord 'log_card_read_in_reception' => Yii::t('common/account', 'Log Card Read in Reception'), ]; } - - public function getUserAccountAssignments(){ - return $this->hasMany(UserAccountAssignment::className(), ['id_account' => 'id_account']); + + public function getUserAccountAssignments() + { + return $this->hasMany(UserAccountAssignment::className(), ['id_account' => 'id_account']); } - - static function statuses() { - return [ - self::STATUS_ACTIVE => Yii::t('common/account', 'Active'), - self::STATUS_DELETED => Yii::t('common/account', 'Inactive'), - ]; + + static function statuses() + { + return [ + self::STATUS_ACTIVE => Yii::t('common/account', 'Active'), + self::STATUS_DELETED => Yii::t('common/account', 'Inactive'), + ]; } - - public function getStatusHuman(){ - $result = null; - $s = self::statuses(); - if ( array_key_exists($this->status, $s)){ - $result = $s[$this->status]; - } - return $result; + + public function getStatusHuman() + { + $result = null; + $s = self::statuses(); + if (array_key_exists($this->status, $s)) { + $result = $s[$this->status]; + } + return $result; } - - static function types() { - return [ - self::TYPE_ALL => Yii::t('common/account', 'Account'), - self::TYPE_VALUE_HIDDEN => Yii::t('common/account', 'Only the name is visible'), - ]; + + static function types() + { + return [ + self::TYPE_ALL => Yii::t('common/account', 'Account'), + self::TYPE_VALUE_HIDDEN => Yii::t('common/account', 'Only the name is visible'), + ]; } - - public function getTypeHuman(){ - $result = null; - $s = self::types(); - if ( array_key_exists($this->type, $s)){ - $result = $s[$this->type]; - } - return $result; + + public function getTypeHuman() + { + $result = null; + $s = self::types(); + if (array_key_exists($this->type, $s)) { + $result = $s[$this->type]; + } + return $result; } - - public function isInactive(){ - return $this->status == self::STATUS_DELETED; + + public function isInactive() + { + return $this->status == self::STATUS_DELETED; } /** @@ -126,76 +134,82 @@ class Account extends \yii\db\ActiveRecord * three arm gate, but we want to track the customers. * @return bool */ - public function isLogCardReadInReceptionOn(){ + public function isLogCardReadInReceptionOn() + { return $this->log_card_read_in_reception == 1; } /** * $param int $forceIncludeAccount id account, that should be included in list, even if it is inactive - * @param null $forceIncludeAccount the next account should be included too, even if it is not + * @param null $forceIncludeAccount the next account should be included too, even if it is not * allowed for user * @return array|null|\yii\db\ActiveRecord[] */ - public static function readAccounts($forceIncludeAccount = null){ - $accounts = null; - - if ( $forceIncludeAccount == null) { - $accounts = Account::find()->andWhere(['status' => Account::STATUS_ACTIVE])->all(); - }else{ - $accounts = Account::find()->andWhere( ['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount ] ])->all(); - } - - return $accounts; - } - - public static function read($forceIncludeAccount = null){ - $accounts = null; - $query = Account::find(); - - $query->innerJoinWith('userAccountAssignments'); - $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]); + public static function readAccounts($forceIncludeAccount = null) + { + $accounts = null; - if ( $forceIncludeAccount == null){ - $query->andWhere(['status' => Account::STATUS_ACTIVE])->all(); - }else{ - $query->andWhere( ['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount ] ])->all(); - } + if ($forceIncludeAccount == null) { + $accounts = Account::find()->andWhere(['status' => Account::STATUS_ACTIVE])->all(); + } else { + $accounts = Account::find()->andWhere(['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount]])->all(); + } - $query->orderBy( ['name' => SORT_ASC]); - - $accounts = $query->all(); - return $accounts; + return $accounts; } - - public static function writeDefault($account){ - $session = Yii::$app->session; - $session->set('id_account', $account->id_account); - + + public static function read($forceIncludeAccount = null) + { + $accounts = null; + $query = Account::find(); + + $query->innerJoinWith('userAccountAssignments'); + $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]); + + if ($forceIncludeAccount == null) { + $query->andWhere(['status' => Account::STATUS_ACTIVE])->all(); + } else { + $query->andWhere(['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount]])->all(); + } + + $query->orderBy(['name' => SORT_ASC]); + + $accounts = $query->all(); + return $accounts; } - + + public static function writeDefault($account) + { + $session = Yii::$app->session; + $session->set('id_account', $account->id_account); + + } + /** read id_transfer from session (default account ) - * + * * @return int id_transfer * */ - public static function readDefault( ){ - $session = Yii::$app->session; - $result = $session->get('id_account'); - return $result; + public static function readDefault() + { + $session = Yii::$app->session; + $result = $session->get('id_account'); + return $result; } - - + + /** * read default transfer object * return the default account or null, if not found * @return \common\models\Account * */ - public static function readDefaultObject( ){ - $account = null; - $id_account = self::readDefault(); - if ( isset($id_account)){ - $account = Account::findOne($id_account); - } - return $account; + public static function readDefaultObject() + { + $account = null; + $id_account = self::readDefault(); + if (isset($id_account)) { + $account = Account::findOne($id_account); + } + return $account; } @@ -204,23 +218,24 @@ class Account extends \yii\db\ActiveRecord * @param $idAccount integer The id of the account to read * @return array|null|\yii\db\ActiveRecord */ - public static function readOne($idAccount){ - $accounts = null; - - $query = Account::find(); - $query->innerJoinWith('userAccountAssignments'); - $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]); - $query->andWhere(['status' => Account::STATUS_ACTIVE]); - $query->andWhere(['account.id_account' => $idAccount]); - $accounts = $query->one(); - - return $accounts; - } - - public static function toAccaountMap($accounts){ - return ArrayHelper::map( $accounts,'id_account','name' ); + public static function readOne($idAccount) + { + $accounts = null; + + $query = Account::find(); + $query->innerJoinWith('userAccountAssignments'); + $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]); + $query->andWhere(['status' => Account::STATUS_ACTIVE]); + $query->andWhere(['account.id_account' => $idAccount]); + $accounts = $query->one(); + + return $accounts; } + public static function toAccaountMap($accounts) + { + return ArrayHelper::map($accounts, 'id_account', 'name'); + } } diff --git a/common/models/User.php b/common/models/User.php index 9962081..e29d28c 100644 --- a/common/models/User.php +++ b/common/models/User.php @@ -26,7 +26,7 @@ class User extends ActiveRecord implements IdentityInterface { const STATUS_DELETED = 0; const STATUS_ACTIVE = 10; - + const ROLE_RECEPTION = 'receptionist'; /** @@ -192,18 +192,22 @@ class User extends ActiveRecord implements IdentityInterface { $this->password_reset_token = null; } - + public function getUserAccountAssignments(){ return $this->hasMany(UserAccountAssignment::className(), ['id_user' =>'id']); } - + + public function getUserTrainerAssignments(){ + return $this->hasMany(UserTrainerAssignment::className(), ['id_user' =>'id']); + } + static function statuses() { return [ self::STATUS_ACTIVE => Yii::t('app', 'Aktív'), self::STATUS_DELETED => Yii::t('app', 'Inaktív'), ] ; } - + public function getStatusHuman(){ $result = null; $s = self::statuses($this->status); @@ -212,8 +216,8 @@ class User extends ActiveRecord implements IdentityInterface } return $result; } - - + + public function attributeLabels(){ return [ 'status' => 'Státusz', @@ -224,14 +228,14 @@ class User extends ActiveRecord implements IdentityInterface 'statusHuman' => Yii::t('backend/user', 'Status'), ]; } - + /** - * - * + * + * * @return \yii\rbac\Role[]*/ public function getRoles(){ $roles = \Yii::$app->authManager->getRolesByUser($this->id ); - return $roles; + return $roles; } /** @@ -239,28 +243,28 @@ class User extends ActiveRecord implements IdentityInterface * */ public function getRoleString(){ $roles = \Yii::$app->authManager->getRolesByUser($this->id ); - + return implode(', ', array_map(function ($role) { return sprintf("%s", RoleDefinition::getRoleLabel($role->name)); }, $roles )); } - + /** * $param int $forceIncludeAccount id warehouse, that should be included in list, even if it is inactive * */ public static function read($forceIncludeObjectWithId = null){ $users = null; $query = User::find(); - + if ( RoleDefinition::isReception()){ $query->andWhere(['id' => Yii::$app->user->id ]); } - + if ( $forceIncludeObjectWithId == null){ $users = $query->andWhere(['status' => User::STATUS_ACTIVE])->all(); }else{ $users = $query->andWhere( ['or', ['status' => User::STATUS_ACTIVE], ['id' => $forceIncludeObjectWithId ] ])->all(); } - + return $users; } - + } diff --git a/common/modules/event/models/EventSearch.php b/common/modules/event/models/EventSearch.php index 6e6b80b..bd20550 100644 --- a/common/modules/event/models/EventSearch.php +++ b/common/modules/event/models/EventSearch.php @@ -3,6 +3,7 @@ namespace common\modules\event\models; use common\components\Helper; +use common\components\RoleDefinition; use Yii; use yii\base\Model; use yii\data\ActiveDataProvider; @@ -86,6 +87,7 @@ class EventSearch extends Event ] ); + $dataProvider = new ActiveDataProvider([ 'query' => $query, 'sort' => [ diff --git a/customer/app/package-lock.json b/customer/app/package-lock.json index 520f1ca..bc36f1e 100644 --- a/customer/app/package-lock.json +++ b/customer/app/package-lock.json @@ -6823,6 +6823,11 @@ "set-immediate-shim": "~1.0.1" } }, + "jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "karma": { "version": "6.3.4", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz", diff --git a/customer/app/package.json b/customer/app/package.json index 7872653..d155010 100644 --- a/customer/app/package.json +++ b/customer/app/package.json @@ -28,6 +28,7 @@ "@ngxs/logger-plugin": "^3.7.2", "@ngxs/store": "^3.7.2", "bootstrap": "^5.1.1", + "jwt-decode": "^3.1.2", "moment": "^2.24.0", "ngx-bootstrap": "^5.0.0", "ngx-toastr": "^14.1.3", diff --git a/customer/app/src/app/_helpers/jwt.interceptor.ts b/customer/app/src/app/_helpers/jwt.interceptor.ts index f21380d..639c871 100644 --- a/customer/app/src/app/_helpers/jwt.interceptor.ts +++ b/customer/app/src/app/_helpers/jwt.interceptor.ts @@ -1,31 +1,37 @@ -import { Injectable } from "@angular/core"; -import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http"; -import { Observable } from "rxjs"; +import {Injectable} from "@angular/core"; +import {HttpInterceptor, HttpRequest, HttpHandler, HttpEvent} from "@angular/common/http"; +import {Observable} from "rxjs"; +import {Store} from "@ngxs/store"; +import {AppState} from "../state/app.state"; /* The JWT interceptor intercepts the incoming requests from the application/user and adds JWT token to the request's Authorization header, only if the user is logged in. -This JWT token in the request header is required to access the SECURE END API POINTS on the server +This JWT token in the request header is required to access the SECURE END API POINTS on the server */ @Injectable() -export class JwtInterceptor implements HttpInterceptor{ - constructor(){} +export class JwtInterceptor implements HttpInterceptor { + constructor(private store: Store) { + } - intercept(request: HttpRequest, next: HttpHandler): Observable>{ - // check if the current user is logged in - // if the user making the request is logged in, he will have JWT token in it's local storage, which is set by Authorization Service during login process - let currentUser = JSON.parse(localStorage.getItem('currentUser')); - if(currentUser && currentUser.token){ - // clone the incoming request and add JWT token in the cloned request's Authorization Header - request = request.clone({ - setHeaders: { - Authorization: `Bearer ${currentUser.token}` - } - }); + intercept(request: HttpRequest, next: HttpHandler): Observable> { + // check if the current user is logged in + // if the user making the request is logged in, he will have JWT token in it's local storage, which is set by Authorization Service during login process + // let currentUser = JSON.parse(localStorage.getItem('currentUser')); + + const token = this.store.selectSnapshot(AppState.getToken); + + if (token) { + // clone the incoming request and add JWT token in the cloned request's Authorization Header + request = request.clone({ + setHeaders: { + Authorization: `Bearer ${token}` } - - // handle any other requests which went unhandled - return next.handle(request); + }); } -} \ No newline at end of file + + // handle any other requests which went unhandled + return next.handle(request); + } +} diff --git a/customer/app/src/app/components/fit-navigation/fit-navigation.component.ts b/customer/app/src/app/components/fit-navigation/fit-navigation.component.ts index 7d3611e..77d5cc4 100644 --- a/customer/app/src/app/components/fit-navigation/fit-navigation.component.ts +++ b/customer/app/src/app/components/fit-navigation/fit-navigation.component.ts @@ -67,11 +67,11 @@ export class FitNavigationComponent implements OnInit { if ( item.roles && item.roles.length ){ let firstRole = item.roles[0]; if ( firstRole == '!'){ - return !this.authenticationService.user.value; + return !this.authenticationService.isLoggedIn(); }else if ( firstRole == '*'){ return true; }else if ( firstRole == '@'){ - return this.authenticationService.user.value; + return this.authenticationService.isLoggedIn(); } } diff --git a/customer/app/src/app/layout/secured-layout/secured-layout.component.html b/customer/app/src/app/layout/secured-layout/secured-layout.component.html index 684864e..026f6f6 100644 --- a/customer/app/src/app/layout/secured-layout/secured-layout.component.html +++ b/customer/app/src/app/layout/secured-layout/secured-layout.component.html @@ -1,4 +1,7 @@
+
+
Bejelentkezve: {{username | async}}
+
diff --git a/customer/app/src/app/layout/secured-layout/secured-layout.component.ts b/customer/app/src/app/layout/secured-layout/secured-layout.component.ts index e97891d..104b7c8 100644 --- a/customer/app/src/app/layout/secured-layout/secured-layout.component.ts +++ b/customer/app/src/app/layout/secured-layout/secured-layout.component.ts @@ -1,4 +1,7 @@ import { Component, OnInit } from '@angular/core'; +import {Select} from "@ngxs/store"; +import {AppState} from "../../state/app.state"; +import {Observable} from "rxjs"; @Component({ selector: 'app-secured-layout', @@ -6,6 +9,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./secured-layout.component.scss'] }) export class SecuredLayoutComponent implements OnInit { + @Select(AppState.getUsername) public username: Observable; constructor() { } diff --git a/customer/app/src/app/services/authentication.service.ts b/customer/app/src/app/services/authentication.service.ts index aa5a072..c4d0518 100644 --- a/customer/app/src/app/services/authentication.service.ts +++ b/customer/app/src/app/services/authentication.service.ts @@ -1,57 +1,71 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from "@angular/common/http"; -import { map } from 'rxjs/operators'; +import {Injectable} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {catchError, map} from 'rxjs/operators'; import {Endpoints} from "./endpoints"; -import {BehaviorSubject} from "rxjs"; +import { throwError} from "rxjs"; import {PasswordChangeRequest} from "../app.types"; +import jwtDecode, {JwtPayload} from "jwt-decode"; +import {Store} from "@ngxs/store"; +import {LoginAction} from "../state/app.actions"; +import {AppState} from "../state/app.state"; @Injectable({ providedIn: 'root' }) export class AuthenticationService { - private _user: BehaviorSubject = new BehaviorSubject(null); - constructor(private http: HttpClient){ - let user = localStorage.getItem('currentUser' ); - if ( user ){ - this.user.next( JSON.stringify(user)); - } + constructor(private http: HttpClient, private store: Store) { + store.dispatch(new LoginAction(this.getToken())) + } + + public getToken() { + let currentUser = JSON.parse(localStorage.getItem('currentUser')); + return currentUser?.token; } // login - login(username: string, password:string){ - return this.http.post(Endpoints.POST_USERS_AUTHENTICATE(), {username:username,password:password}) + login(username: string, password: string) { + return this.http.post(Endpoints.POST_USERS_AUTHENTICATE(), {username: username, password: password}) .pipe( // the backend service sends an instance of the user // user: any (because .post) map(user => { // login successful if the response has jwt token - if(user && user.token){ + if (user && user.token) { // store user details and jwt token in the local storage to keep the user logged in between page refreshes localStorage.setItem('currentUser', JSON.stringify(user)); - this.user.next(user); + const decoded = jwtDecode(user.token); + console.info("decoded", decoded); + // this.user.next(user); + this.store.dispatch(new LoginAction(user.token)); } return user; - }) + }), + catchError(err => { + localStorage.removeItem('currentUser'); + this.store.dispatch(new LoginAction(null)); + return throwError(err); + }), ); } - passwordChange(passwordChangeRequest: PasswordChangeRequest){ + passwordChange(passwordChangeRequest: PasswordChangeRequest) { return this.http.post(Endpoints.POST_USER_PASSWORD_CHANGE(), passwordChangeRequest) } - get user() { - return this._user; - } - - public isLoggedIn(){ - return this.user.value; + // get user() { + // return this._user; + // } + // + public isLoggedIn() { + return this.store.selectSnapshot(AppState.getToken); } // logout - logout(){ + logout() { // remove user from local storage localStorage.removeItem('currentUser'); - this.user.next(null); + this.store.dispatch(new LoginAction(null)); + // this.user.next(null); } } diff --git a/customer/app/src/app/state/app.actions.ts b/customer/app/src/app/state/app.actions.ts index 686e610..d3c14fc 100644 --- a/customer/app/src/app/state/app.actions.ts +++ b/customer/app/src/app/state/app.actions.ts @@ -6,3 +6,9 @@ export class FilterTimeTableAction { constructor(public idTrainer: number, public idEventType: number) {} } +export class LoginAction { + // For debug / console output upon dev environment + static readonly type = "[App] LoginAction"; + constructor(public token: string) {} +} + diff --git a/customer/app/src/app/state/app.state.ts b/customer/app/src/app/state/app.state.ts index b7d37c6..be7bc9c 100644 --- a/customer/app/src/app/state/app.state.ts +++ b/customer/app/src/app/state/app.state.ts @@ -1,12 +1,15 @@ -import {Action, Selector, State, StateContext, Store} from "@ngxs/store"; +import {Action, Selector, State, StateContext} from "@ngxs/store"; import {Injectable} from "@angular/core"; import { - FilterTimeTableAction, + FilterTimeTableAction, LoginAction, } from "./app.actions"; import {TimeTableFilter} from "../app.types"; +import jwtDecode, {JwtPayload} from "jwt-decode"; export interface AppStateModel { + username: string; + token: string; filterTimeTable: FilterTimeTableAction; } @@ -14,6 +17,8 @@ export interface AppStateModel { @State({ name: "app", defaults: { + username: null, + token: null, filterTimeTable: { idTrainer: -1, idEventType: -1 @@ -23,8 +28,7 @@ export interface AppStateModel { export class AppState { - constructor( - ) { + constructor() { } @Selector() @@ -32,6 +36,17 @@ export class AppState { return state.filterTimeTable; } + @Selector() + public static getUsername(state: AppStateModel): string { + return state.username; + } + + @Selector() + public static getToken(state: AppStateModel): string { + return state.token; + } + + @Action(FilterTimeTableAction) dispatchFilterTimeTable(ctx: StateContext, {idEventType, idTrainer}: FilterTimeTableAction): void { ctx.patchState({ @@ -42,4 +57,23 @@ export class AppState { }); } + @Action(LoginAction) + dispatchLogin(ctx: StateContext, {token}: LoginAction): void { + let username = null; + + try { + const decoded = jwtDecode(token); + username = decoded?.username; + } catch (e) { + // user not logged in + token = null; + } + + + ctx.patchState({ + username: username, + token: token + }); + } + } diff --git a/customerapi/controllers/LoginController.php b/customerapi/controllers/LoginController.php index 56436fa..904fb22 100644 --- a/customerapi/controllers/LoginController.php +++ b/customerapi/controllers/LoginController.php @@ -54,6 +54,7 @@ class LoginController extends CustomerApiController ->issuedAt($time)// Configures the time that the token was issue (iat claim) ->expiresAt($time + 3600)// Configures the expiration time of the token (exp claim) ->withClaim('uid', $form->getCustomer()->getId())// Configures a new claim, called "uid" + ->withClaim('username', $form->getCustomer()->email)// Configures a new claim, called "username" ->getToken($signer, $key); // Retrieves the generated token return $this->asJson([