assign trainers to user; add email to jwt token

This commit is contained in:
Roland Schneider 2021-10-04 18:13:32 +02:00
parent 439fb12b6e
commit 7a55ca9ff3
21 changed files with 671 additions and 350 deletions

View File

@ -172,19 +172,68 @@ class AdminMenuStructure
///////////////////////////// /////////////////////////////
// Group Training // Group Training
///////////////////////////// /////////////////////////////
if (RoleDefinition::isLoggedUser()) {
$items = []; $items = [];
$items[] = ['label' => 'Felszerelés', 'url' => ['/event-equipment-type']]; // $items[] = ['label' => 'Felszerelés', 'url' => ['/event-equipment-type'], 'role' => [RoleDefinition::$ROLE_ADMIN]];
$items[] = ['label' => 'Edzők', 'url' => ['/trainer']]; $items[] = [
$items[] = ['label' => 'Termek', 'url' => ['/room']]; 'label' => 'Edzők',
$items[] = ['label' => 'Esemény típusok', 'url' => ['/event-type']]; 'url' => ['/trainer'],
$items[] = ['label' => 'Események', 'url' => ['/event/event/index']]; 'role' => [
$items[] = ['label' => 'Órarend', 'url' => ['/event/event/timetable']]; RoleDefinition::$ROLE_ADMIN,
$items[] = ['label' => 'Hét másolása', 'url' => ['/event/event/copy-week']]; RoleDefinition::$ROLE_EMPLOYEE,
$this->menuItems[] = ['label' => 'Csoportos edzés', 'url' => $this->emptyUrl, ]
'items' => $items
]; ];
} $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 // 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() protected function addLoginMainMenu()
{ {
if (Yii::$app->user->isGuest) { if (Yii::$app->user->isGuest) {

View File

@ -2,10 +2,13 @@
namespace backend\controllers; namespace backend\controllers;
use common\models\Trainer;
use common\models\UserTrainerAssignment;
use Yii; use Yii;
use common\models\User; use common\models\User;
use backend\models\UserSearch; use backend\models\UserSearch;
use backend\models\UserCreate; use backend\models\UserCreate;
use yii\web\BadRequestHttpException;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
@ -86,10 +89,12 @@ class UserController extends \backend\controllers\BackendController
$model = new UserCreate(); $model = new UserCreate();
$accounts = Account::readAccounts(); $accounts = Account::readAccounts();
$trainers = Trainer::find()->all();
if ($model->load(Yii::$app->request->post()) && $model->save()) { if ($model->load(Yii::$app->request->post()) && $model->save()) {
$this->updateAccountAssignments($model); $this->updateAccountAssignments($model);
$this->updateTrainerAssignments($model);
return $this->redirect(['index' ]); return $this->redirect(['index' ]);
@ -98,15 +103,14 @@ class UserController extends \backend\controllers\BackendController
return $this->render('create', [ return $this->render('create', [
'model' => $model, 'model' => $model,
'accounts' => $accounts, 'accounts' => $accounts,
'trainers' => $trainers,
]); ]);
} }
public function updateAccountAssignments($model){ public function updateAccountAssignments($model){
echo "saving accounts";
UserAccountAssignment::deleteAll(['id_user' => $model->id]); UserAccountAssignment::deleteAll(['id_user' => $model->id]);
foreach ( $model->selected_accounts as $id_account ){ foreach ( $model->selected_accounts as $id_account ){
echo "saving account";
$uaa = new UserAccountAssignment(); $uaa = new UserAccountAssignment();
$uaa->id_user = $model->id; $uaa->id_user = $model->id;
$uaa->id_account = $id_account; $uaa->id_account = $id_account;
@ -115,6 +119,18 @@ class UserController extends \backend\controllers\BackendController
} }
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();
}
}
/** /**
* Updates an existing User model. * Updates an existing User model.
* If update is successful, the browser will be redirected to the 'view' page. * If update is successful, the browser will be redirected to the 'view' page.
@ -138,19 +154,22 @@ class UserController extends \backend\controllers\BackendController
} }
$accounts = Account::readAccounts(); $accounts = Account::readAccounts();
$this->applyAccounts($model); $this->applyAccounts($model);
$trainers = Trainer::find()->all();
$this->applyTrainers($model);
if ($model->load(Yii::$app->request->post()) && $model->save()) { if ($model->load(Yii::$app->request->post()) && $model->save()) {
$this->updateAccountAssignments($model); $this->updateAccountAssignments($model);
$this->updateTrainerAssignments($model);
return $this->redirect(['view', 'id' => $model->id]); return $this->redirect(['view', 'id' => $model->id]);
} else {
} }
return $this->render('update', [ return $this->render('update', [
'model' => $model, 'model' => $model,
'accounts' => $accounts, 'accounts' => $accounts,
'trainers' => $trainers,
]); ]);
} }
@ -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. * Deletes an existing User model.
* If deletion is successful, the browser will be redirected to the 'index' page. * If deletion is successful, the browser will be redirected to the 'index' page.

View File

@ -9,6 +9,7 @@ class UserCreate extends User{
public $password_plain; public $password_plain;
public $password_repeat; public $password_repeat;
public $selected_accounts = []; public $selected_accounts = [];
public $selected_trainers = [];
public $role; public $role;
@ -25,6 +26,12 @@ class UserCreate extends User{
} }
} }
], ],
['selected_trainers',function ($attribute, $params) {
if (!is_array($this->$attribute)) {
$this->addError($attribute, 'Invalid array');
}
}
],
['email' ,'email' ], ['email' ,'email' ],
['email' ,'unique' ], ['email' ,'unique' ],
['username' ,'unique' ], ['username' ,'unique' ],

View File

@ -9,6 +9,7 @@ class UserUpdate extends User {
public $password_plain; public $password_plain;
public $password_repeat; public $password_repeat;
public $selected_accounts = []; public $selected_accounts = [];
public $selected_trainers = [];
public $role; public $role;
@ -21,16 +22,24 @@ class UserUpdate extends User {
return [ return [
[['username','email'], 'required' ], [['username','email'], 'required' ],
['email' ,'email' ], ['email' ,'email' ],
['email' ,'unique' , 'targetClass' => User::className(), 'targetAttribute' => 'email'], // ['email' ,'unique' , 'targetClass' => User::className(), 'targetAttribute' => 'email'],
['username' ,'unique', 'targetClass' => User::className(), 'targetAttribute' => 'username'], // ['username' ,'unique', 'targetClass' => User::className(), 'targetAttribute' => 'username'],
[['password_plain' ,'password_repeat'] ,'string','min' =>6 ], [['password_plain' ,'password_repeat'] ,'string','min' =>6 ],
[['password_repeat'] ,'validatePasswordRepeat' ], [['password_repeat'] ,'validatePasswordRepeat' ],
[['username'] ,'validateUsername' ],
[['email'] ,'validateEmail' ],
['selected_accounts',function ($attribute, $params) { ['selected_accounts',function ($attribute, $params) {
if (!is_array($this->$attribute)) { if (!is_array($this->$attribute)) {
$this->addError($attribute, 'Invalid array'); $this->addError($attribute, 'Invalid array');
} }
} }
], ],
['selected_trainers',function ($attribute, $params) {
if (!is_array($this->$attribute)) {
$this->addError($attribute, 'Invalid array');
}
}
],
[['role'], 'required'], [['role'], 'required'],
[['role'], 'string', 'max' => 20], [['role'], 'string', 'max' => 20],
@ -40,6 +49,32 @@ class UserUpdate extends User {
]; ];
} }
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 * @formatter:on
*/ */

View File

@ -1,5 +1,8 @@
<?php <?php
use backend\models\UserUpdate;
use common\models\Account;
use common\models\Trainer;
use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\ActiveForm; use yii\widgets\ActiveForm;
use yii\grid\GridView; use yii\grid\GridView;
@ -8,8 +11,10 @@ use common\components\RoleDefinition;
use common\models\User; use common\models\User;
/* @var $this yii\web\View */ /* @var $this yii\web\View */
/* @var $model common\models\User */ /* @var $model UserUpdate */
/* @var $form yii\widgets\ActiveForm */ /* @var $form yii\widgets\ActiveForm */
/* @var $trainers Trainer[] */
/* @var $accounts Account[] */
?> ?>
<?php <?php
@ -35,6 +40,7 @@ asort($roleOptions);
<?php <?php
$selectedAccounts = $model->selected_accounts; $selectedAccounts = $model->selected_accounts;
$selectedTrainers = $model->selected_trainers;
?> ?>
@ -67,8 +73,40 @@ asort($roleOptions);
[ 'attribute' => 'name' ], [ 'attribute' => 'name' ],
], ],
])?> ])?>
<h3>Engedélyezett edzők</h3>
<?php echo GridView::widget([
'dataProvider' => 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' ],
],
])?>
<div class="form-group"> <div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Mentés') : Yii::t('app', 'Mentés'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> <?= Html::submitButton( Yii::t('app', 'Mentés'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

View File

@ -17,6 +17,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?= $this->render('_form', [ <?= $this->render('_form', [
'model' => $model, 'model' => $model,
'accounts' => $accounts, 'accounts' => $accounts,
'trainers' => $trainers,
]) ?> ]) ?>
</div> </div>

View File

@ -16,7 +16,8 @@ $this->params['breadcrumbs'][] = Yii::t('backend/user', 'Update');
<?= $this->render('_form', [ <?= $this->render('_form', [
'model' => $model, 'model' => $model,
'accounts' => $accounts 'accounts' => $accounts,
'trainers' => $trainers,
]) ?> ]) ?>
</div> </div>

View File

@ -1,75 +1,87 @@
<?php <?php
namespace common\components; namespace common\components;
use \Yii; use \Yii;
class RoleDefinition{ class RoleDefinition
{
public static $ROLE_ADMIN = "admin";
public static $ROLE_RECEPTION = "reception";
public static $ROLE_EMPLOYEE = "employee";
public static $ROLE_TRAINER = "trainer";
public static function roleLabels()
public static function roleLabels(){ {
return [ return [
'reception' => Yii::t('common/role' ,'Reception'), 'reception' => Yii::t('common/role', 'Reception'),
'admin' => Yii::t('common/role' ,'Administrator'), 'admin' => Yii::t('common/role', 'Administrator'),
'employee' => Yii::t('common/role' ,'Employee'), 'employee' => Yii::t('common/role', 'Employee'),
'Trainer' => Yii::t('common/role' ,'Edző'), 'Trainer' => Yii::t('common/role', 'Edző'),
]; ];
} }
public static function getRoleLabel($role){ public static function getRoleLabel($role)
{
$result = null; $result = null;
$roleLabels = self::roleLabels(); $roleLabels = self::roleLabels();
if ( array_key_exists($role, $roleLabels)){ if (array_key_exists($role, $roleLabels)) {
$result = $roleLabels[$role]; $result = $roleLabels[$role];
} }
return $result; return $result;
} }
public static function roleDefinitions(){ public static function roleDefinitions()
{
return [ return [
'employee' => [ 'employee' => [
'canAllow' => [ 'employee'], 'canAllow' => ['employee'],
], ],
'admin' => [ 'admin' => [
'canAllow' => ['admin','reception','employee'], 'canAllow' => ['admin', 'reception', 'employee'],
], ],
'reception' => [ 'reception' => [
'canAllow' => [ ], 'canAllow' => [],
], ],
]; ];
} }
public static function getRoleDefinition($role){ public static function getRoleDefinition($role)
{
$defs = self::roleDefinitions(); $defs = self::roleDefinitions();
$result = null; $result = null;
if ( array_key_exists($role, $defs)){ if (array_key_exists($role, $defs)) {
$result = $defs[$role]; $result = $defs[$role];
} }
$result = $defs[$role]; $result = $defs[$role];
return $result; return $result;
} }
public static function getRolesCanAllow($role){ public static function getRolesCanAllow($role)
{
$result = []; $result = [];
$def = self::getRoleDefinition($role); $def = self::getRoleDefinition($role);
if ( isset($def)){ if (isset($def)) {
$result = $def['canAllow']; $result = $def['canAllow'];
} }
return $result; return $result;
} }
public static function can($role){ public static function can($role)
{
$result = false; $result = false;
if ( !Yii::$app->user->isGuest ){ if (!Yii::$app->user->isGuest) {
if ( isset( $role)){ if (isset($role)) {
if ( is_array($role)){ if (is_array($role)) {
foreach ($role as $r){ foreach ($role as $r) {
$result |= Yii::$app->user->can($r); $result |= Yii::$app->user->can($r);
} }
}else if ( is_string($role)){ } else if (is_string($role)) {
$result = Yii::$app->user->can($role); $result = Yii::$app->user->can($role);
} }
} }
@ -77,37 +89,54 @@ class RoleDefinition{
return $result; return $result;
} }
public static function isAdmin(){ public static function canAny($roles)
{
foreach ($roles as $role) {
if (self::can($role)) {
return true;
}
}
return false;
}
public static function isAdmin()
{
return self::can('admin'); return self::can('admin');
} }
public static function isReception(){ public static function isReception()
{
return self::can('reception'); return self::can('reception');
} }
public static function isEmployee(){ public static function isEmployee()
{
return self::can('employee'); return self::can('employee');
} }
public static function isTrainer(){ public static function isTrainer()
{
return self::can('trainer'); return self::can('trainer');
} }
public static function isLoggedUser(){ public static function isLoggedUser()
{
return self::isTrainer() || self::isAdmin() || self::isEmployee() return self::isTrainer() || self::isAdmin() || self::isEmployee()
|| self::isReception(); || self::isReception();
} }
/* /*
* [ * [
* 'role1' => 'template1', * 'role1' => 'template1',
* 'role2' => 'template2, * 'role2' => 'template2,
* ] * ]
* */ * */
public static function getRoleTemplate($templates){ public static function getRoleTemplate($templates)
{
$result = ""; $result = "";
foreach ($templates as $role => $template ){ foreach ($templates as $role => $template) {
if ( Yii::$app->user->can($role)){ if (Yii::$app->user->can($role)) {
$result = $template; $result = $template;
break; break;
} }

View File

@ -42,8 +42,10 @@ class Account extends \yii\db\ActiveRecord
public function behaviors() public function behaviors()
{ {
return [ return [
[ 'class' => TimestampBehavior::className(), ['class' => TimestampBehavior::className(),
'value' => function(){ return date('Y-m-d H:i:s' ); } 'value' => function () {
return date('Y-m-d H:i:s');
}
] ]
]; ];
} }
@ -55,8 +57,8 @@ class Account extends \yii\db\ActiveRecord
{ {
return [ return [
[['name', 'type'], 'required'], [['name', 'type'], 'required'],
[['name', ], 'unique'], [['name',], 'unique'],
[['status', 'type','log_card_read_in_reception'], 'integer'], [['status', 'type', 'log_card_read_in_reception'], 'integer'],
[['name'], 'string', 'max' => 64] [['name'], 'string', 'max' => 64]
]; ];
} }
@ -77,43 +79,49 @@ class Account extends \yii\db\ActiveRecord
]; ];
} }
public function getUserAccountAssignments(){ public function getUserAccountAssignments()
{
return $this->hasMany(UserAccountAssignment::className(), ['id_account' => 'id_account']); return $this->hasMany(UserAccountAssignment::className(), ['id_account' => 'id_account']);
} }
static function statuses() { static function statuses()
{
return [ return [
self::STATUS_ACTIVE => Yii::t('common/account', 'Active'), self::STATUS_ACTIVE => Yii::t('common/account', 'Active'),
self::STATUS_DELETED => Yii::t('common/account', 'Inactive'), self::STATUS_DELETED => Yii::t('common/account', 'Inactive'),
]; ];
} }
public function getStatusHuman(){ public function getStatusHuman()
{
$result = null; $result = null;
$s = self::statuses(); $s = self::statuses();
if ( array_key_exists($this->status, $s)){ if (array_key_exists($this->status, $s)) {
$result = $s[$this->status]; $result = $s[$this->status];
} }
return $result; return $result;
} }
static function types() { static function types()
{
return [ return [
self::TYPE_ALL => Yii::t('common/account', 'Account'), self::TYPE_ALL => Yii::t('common/account', 'Account'),
self::TYPE_VALUE_HIDDEN => Yii::t('common/account', 'Only the name is visible'), self::TYPE_VALUE_HIDDEN => Yii::t('common/account', 'Only the name is visible'),
]; ];
} }
public function getTypeHuman(){ public function getTypeHuman()
{
$result = null; $result = null;
$s = self::types(); $s = self::types();
if ( array_key_exists($this->type, $s)){ if (array_key_exists($this->type, $s)) {
$result = $s[$this->type]; $result = $s[$this->type];
} }
return $result; return $result;
} }
public function isInactive(){ public function isInactive()
{
return $this->status == self::STATUS_DELETED; return $this->status == self::STATUS_DELETED;
} }
@ -126,7 +134,8 @@ class Account extends \yii\db\ActiveRecord
* three arm gate, but we want to track the customers. * three arm gate, but we want to track the customers.
* @return bool * @return bool
*/ */
public function isLogCardReadInReceptionOn(){ public function isLogCardReadInReceptionOn()
{
return $this->log_card_read_in_reception == 1; return $this->log_card_read_in_reception == 1;
} }
@ -136,38 +145,41 @@ class Account extends \yii\db\ActiveRecord
* allowed for user * allowed for user
* @return array|null|\yii\db\ActiveRecord[] * @return array|null|\yii\db\ActiveRecord[]
*/ */
public static function readAccounts($forceIncludeAccount = null){ public static function readAccounts($forceIncludeAccount = null)
{
$accounts = null; $accounts = null;
if ( $forceIncludeAccount == null) { if ($forceIncludeAccount == null) {
$accounts = Account::find()->andWhere(['status' => Account::STATUS_ACTIVE])->all(); $accounts = Account::find()->andWhere(['status' => Account::STATUS_ACTIVE])->all();
}else{ } else {
$accounts = Account::find()->andWhere( ['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount ] ])->all(); $accounts = Account::find()->andWhere(['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount]])->all();
} }
return $accounts; return $accounts;
} }
public static function read($forceIncludeAccount = null){ public static function read($forceIncludeAccount = null)
{
$accounts = null; $accounts = null;
$query = Account::find(); $query = Account::find();
$query->innerJoinWith('userAccountAssignments'); $query->innerJoinWith('userAccountAssignments');
$query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]); $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id]);
if ( $forceIncludeAccount == null){ if ($forceIncludeAccount == null) {
$query->andWhere(['status' => Account::STATUS_ACTIVE])->all(); $query->andWhere(['status' => Account::STATUS_ACTIVE])->all();
}else{ } else {
$query->andWhere( ['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount ] ])->all(); $query->andWhere(['or', ['status' => Account::STATUS_ACTIVE], ['id_account' => $forceIncludeAccount]])->all();
} }
$query->orderBy( ['name' => SORT_ASC]); $query->orderBy(['name' => SORT_ASC]);
$accounts = $query->all(); $accounts = $query->all();
return $accounts; return $accounts;
} }
public static function writeDefault($account){ public static function writeDefault($account)
{
$session = Yii::$app->session; $session = Yii::$app->session;
$session->set('id_account', $account->id_account); $session->set('id_account', $account->id_account);
@ -177,7 +189,8 @@ class Account extends \yii\db\ActiveRecord
* *
* @return int id_transfer * @return int id_transfer
* */ * */
public static function readDefault( ){ public static function readDefault()
{
$session = Yii::$app->session; $session = Yii::$app->session;
$result = $session->get('id_account'); $result = $session->get('id_account');
return $result; return $result;
@ -189,10 +202,11 @@ class Account extends \yii\db\ActiveRecord
* return the default account or null, if not found * return the default account or null, if not found
* @return \common\models\Account * @return \common\models\Account
* */ * */
public static function readDefaultObject( ){ public static function readDefaultObject()
{
$account = null; $account = null;
$id_account = self::readDefault(); $id_account = self::readDefault();
if ( isset($id_account)){ if (isset($id_account)) {
$account = Account::findOne($id_account); $account = Account::findOne($id_account);
} }
return $account; return $account;
@ -204,7 +218,8 @@ class Account extends \yii\db\ActiveRecord
* @param $idAccount integer The id of the account to read * @param $idAccount integer The id of the account to read
* @return array|null|\yii\db\ActiveRecord * @return array|null|\yii\db\ActiveRecord
*/ */
public static function readOne($idAccount){ public static function readOne($idAccount)
{
$accounts = null; $accounts = null;
$query = Account::find(); $query = Account::find();
@ -217,10 +232,10 @@ class Account extends \yii\db\ActiveRecord
return $accounts; return $accounts;
} }
public static function toAccaountMap($accounts){ public static function toAccaountMap($accounts)
return ArrayHelper::map( $accounts,'id_account','name' ); {
return ArrayHelper::map($accounts, 'id_account', 'name');
} }
} }

View File

@ -197,6 +197,10 @@ class User extends ActiveRecord implements IdentityInterface
return $this->hasMany(UserAccountAssignment::className(), ['id_user' =>'id']); return $this->hasMany(UserAccountAssignment::className(), ['id_user' =>'id']);
} }
public function getUserTrainerAssignments(){
return $this->hasMany(UserTrainerAssignment::className(), ['id_user' =>'id']);
}
static function statuses() { static function statuses() {
return [ return [
self::STATUS_ACTIVE => Yii::t('app', 'Aktív'), self::STATUS_ACTIVE => Yii::t('app', 'Aktív'),

View File

@ -3,6 +3,7 @@
namespace common\modules\event\models; namespace common\modules\event\models;
use common\components\Helper; use common\components\Helper;
use common\components\RoleDefinition;
use Yii; use Yii;
use yii\base\Model; use yii\base\Model;
use yii\data\ActiveDataProvider; use yii\data\ActiveDataProvider;
@ -86,6 +87,7 @@ class EventSearch extends Event
] ]
); );
$dataProvider = new ActiveDataProvider([ $dataProvider = new ActiveDataProvider([
'query' => $query, 'query' => $query,
'sort' => [ 'sort' => [

View File

@ -6823,6 +6823,11 @@
"set-immediate-shim": "~1.0.1" "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": { "karma": {
"version": "6.3.4", "version": "6.3.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz",

View File

@ -28,6 +28,7 @@
"@ngxs/logger-plugin": "^3.7.2", "@ngxs/logger-plugin": "^3.7.2",
"@ngxs/store": "^3.7.2", "@ngxs/store": "^3.7.2",
"bootstrap": "^5.1.1", "bootstrap": "^5.1.1",
"jwt-decode": "^3.1.2",
"moment": "^2.24.0", "moment": "^2.24.0",
"ngx-bootstrap": "^5.0.0", "ngx-bootstrap": "^5.0.0",
"ngx-toastr": "^14.1.3", "ngx-toastr": "^14.1.3",

View File

@ -1,6 +1,8 @@
import { Injectable } from "@angular/core"; import {Injectable} from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http"; import {HttpInterceptor, HttpRequest, HttpHandler, HttpEvent} from "@angular/common/http";
import { Observable } from "rxjs"; 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. 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.
@ -9,18 +11,22 @@ This JWT token in the request header is required to access the SECURE END API PO
*/ */
@Injectable() @Injectable()
export class JwtInterceptor implements HttpInterceptor{ export class JwtInterceptor implements HttpInterceptor {
constructor(){} constructor(private store: Store) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{ intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// check if the current user is logged in // 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 // 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')); // let currentUser = JSON.parse(localStorage.getItem('currentUser'));
if(currentUser && currentUser.token){
const token = this.store.selectSnapshot<string>(AppState.getToken);
if (token) {
// clone the incoming request and add JWT token in the cloned request's Authorization Header // clone the incoming request and add JWT token in the cloned request's Authorization Header
request = request.clone({ request = request.clone({
setHeaders: { setHeaders: {
Authorization: `Bearer ${currentUser.token}` Authorization: `Bearer ${token}`
} }
}); });
} }

View File

@ -67,11 +67,11 @@ export class FitNavigationComponent implements OnInit {
if ( item.roles && item.roles.length ){ if ( item.roles && item.roles.length ){
let firstRole = item.roles[0]; let firstRole = item.roles[0];
if ( firstRole == '!'){ if ( firstRole == '!'){
return !this.authenticationService.user.value; return !this.authenticationService.isLoggedIn();
}else if ( firstRole == '*'){ }else if ( firstRole == '*'){
return true; return true;
}else if ( firstRole == '@'){ }else if ( firstRole == '@'){
return this.authenticationService.user.value; return this.authenticationService.isLoggedIn();
} }
} }

View File

@ -1,4 +1,7 @@
<fit-navigation></fit-navigation> <fit-navigation></fit-navigation>
<div class="container " > <div class="container " >
<div class="row ">
<div class="col-12">Bejelentkezve: {{username | async}}</div>
</div>
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>

View File

@ -1,4 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {Select} from "@ngxs/store";
import {AppState} from "../../state/app.state";
import {Observable} from "rxjs";
@Component({ @Component({
selector: 'app-secured-layout', selector: 'app-secured-layout',
@ -6,6 +9,7 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./secured-layout.component.scss'] styleUrls: ['./secured-layout.component.scss']
}) })
export class SecuredLayoutComponent implements OnInit { export class SecuredLayoutComponent implements OnInit {
@Select(AppState.getUsername) public username: Observable<string>;
constructor() { } constructor() { }

View File

@ -1,57 +1,71 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import { HttpClient } from "@angular/common/http"; import {HttpClient} from "@angular/common/http";
import { map } from 'rxjs/operators'; import {catchError, map} from 'rxjs/operators';
import {Endpoints} from "./endpoints"; import {Endpoints} from "./endpoints";
import {BehaviorSubject} from "rxjs"; import { throwError} from "rxjs";
import {PasswordChangeRequest} from "../app.types"; 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({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AuthenticationService { export class AuthenticationService {
private _user: BehaviorSubject<any> = new BehaviorSubject(null); constructor(private http: HttpClient, private store: Store) {
constructor(private http: HttpClient){ store.dispatch(new LoginAction(this.getToken()))
let user = localStorage.getItem('currentUser' );
if ( user ){
this.user.next( JSON.stringify(user));
} }
public getToken() {
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
return currentUser?.token;
} }
// login // login
login(username: string, password:string){ login(username: string, password: string) {
return this.http.post<any>(Endpoints.POST_USERS_AUTHENTICATE(), {username:username,password:password}) return this.http.post<any>(Endpoints.POST_USERS_AUTHENTICATE(), {username: username, password: password})
.pipe( .pipe(
// the backend service sends an instance of the user // the backend service sends an instance of the user
// user: any (because .post<any>) // user: any (because .post<any>)
map(user => { map(user => {
// login successful if the response has jwt token // 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 // 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)); localStorage.setItem('currentUser', JSON.stringify(user));
this.user.next(user); const decoded = jwtDecode<JwtPayload>(user.token);
console.info("decoded", decoded);
// this.user.next(user);
this.store.dispatch(new LoginAction(user.token));
} }
return user; 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<any>(Endpoints.POST_USER_PASSWORD_CHANGE(), passwordChangeRequest) return this.http.post<any>(Endpoints.POST_USER_PASSWORD_CHANGE(), passwordChangeRequest)
} }
get user() { // get user() {
return this._user; // return this._user;
} // }
//
public isLoggedIn(){ public isLoggedIn() {
return this.user.value; return this.store.selectSnapshot(AppState.getToken);
} }
// logout // logout
logout(){ logout() {
// remove user from local storage // remove user from local storage
localStorage.removeItem('currentUser'); localStorage.removeItem('currentUser');
this.user.next(null); this.store.dispatch(new LoginAction(null));
// this.user.next(null);
} }
} }

View File

@ -6,3 +6,9 @@ export class FilterTimeTableAction {
constructor(public idTrainer: number, public idEventType: number) {} 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) {}
}

View File

@ -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 {Injectable} from "@angular/core";
import { import {
FilterTimeTableAction, FilterTimeTableAction, LoginAction,
} from "./app.actions"; } from "./app.actions";
import {TimeTableFilter} from "../app.types"; import {TimeTableFilter} from "../app.types";
import jwtDecode, {JwtPayload} from "jwt-decode";
export interface AppStateModel { export interface AppStateModel {
username: string;
token: string;
filterTimeTable: FilterTimeTableAction; filterTimeTable: FilterTimeTableAction;
} }
@ -14,6 +17,8 @@ export interface AppStateModel {
@State<AppStateModel>({ @State<AppStateModel>({
name: "app", name: "app",
defaults: { defaults: {
username: null,
token: null,
filterTimeTable: { filterTimeTable: {
idTrainer: -1, idTrainer: -1,
idEventType: -1 idEventType: -1
@ -23,8 +28,7 @@ export interface AppStateModel {
export class AppState { export class AppState {
constructor( constructor() {
) {
} }
@Selector() @Selector()
@ -32,6 +36,17 @@ export class AppState {
return state.filterTimeTable; 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) @Action(FilterTimeTableAction)
dispatchFilterTimeTable(ctx: StateContext<AppStateModel>, {idEventType, idTrainer}: FilterTimeTableAction): void { dispatchFilterTimeTable(ctx: StateContext<AppStateModel>, {idEventType, idTrainer}: FilterTimeTableAction): void {
ctx.patchState({ ctx.patchState({
@ -42,4 +57,23 @@ export class AppState {
}); });
} }
@Action(LoginAction)
dispatchLogin(ctx: StateContext<AppStateModel>, {token}: LoginAction): void {
let username = null;
try {
const decoded = jwtDecode<JwtPayload & { username: string }>(token);
username = decoded?.username;
} catch (e) {
// user not logged in
token = null;
}
ctx.patchState({
username: username,
token: token
});
}
} }

View File

@ -54,6 +54,7 @@ class LoginController extends CustomerApiController
->issuedAt($time)// Configures the time that the token was issue (iat claim) ->issuedAt($time)// Configures the time that the token was issue (iat claim)
->expiresAt($time + 3600)// Configures the expiration time of the token (exp 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('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 ->getToken($signer, $key); // Retrieves the generated token
return $this->asJson([ return $this->asJson([