Skip to content

Commit

Permalink
Merge branch 'master' into v2.0.0-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Lorenzo Milesi committed May 8, 2023
2 parents b8e34e0 + 894d756 commit de84979
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 55 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# CHANGELOG

## dev

- Enh: possibility to limit the depth of the recursion when getting user ids from roles (mp1509)

## 1.6.1 March 4th, 2023

- Fix: use correct password recovery url in welcome mail and add functionality to plain text version of the mail (@eluhr)
- Fix: correct viewPath error in LoginWidget (niciz)
- Enh: possibility to call all the api endpoints with either id or username or email (liviuk2)
- Fix: use configured User model in SecurityController 2FA confirmation (jussiaho)
- Enh: possibility to get user ids from roles recursively (mp1509)

## 1.6.0 January 9, 2023

**WARNING**: this release (long time due) makes a step forward in PHP
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,21 @@ module is built to work out of the box with some minor config tweaks and it come
We considered that RBAC was essential to be included into any user management module, even if you simply use one user
with `admin` role, its much better to actually work with RBAC just in case your application scales in the future.

## Documentation
## Boostrap 4 and 5 support

You can read the latest docs on [http://yii2-usuario.readthedocs.io/en/latest/](http://yii2-usuario.readthedocs.io/en/latest/)
With the release of 1.6, contributors started implementing changes for supporting newer versions of the Boostrap library,
being Usuario stuck at 3.

Up until around May 2023, the `master` branch will remain stable, so devs who in these years relied on it for
deployment can have time to migrate to a stable version.
BS5 development is ongoing on branch [`2.0.0-dev`](https://github.com/2amigos/yii2-usuario/tree/v2.0.0-dev),
which will eventually be merged in `master` around May.

You can check issues #476, #488, #500 for updates, or the [branch itself](https://github.com/2amigos/yii2-usuario/tree/v2.0.0-dev).

## Documentation

You can read the latest docs on [http://yii2-usuario.readthedocs.io/en/latest/](http://yii2-usuario.readthedocs.io/en/latest/)

## Need Help?

Expand Down
81 changes: 81 additions & 0 deletions src/User/Component/AuthDbManagerComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
namespace Da\User\Component;

use Da\User\Contracts\AuthManagerInterface;
use yii\base\InvalidArgumentException;
use yii\db\Expression;
use yii\db\Query;
use yii\rbac\DbManager;
use yii\rbac\Role;

class AuthDbManagerComponent extends DbManager implements AuthManagerInterface
{
Expand Down Expand Up @@ -80,4 +83,82 @@ public function getItem($name)
{
return parent::getItem($name);
}


/**
* @inheritdoc
* @param bool|integer $recursive If the roles are to be calculated recursively. If an integer is passed it will limit the depth of the
* recursion to the given number (e.g. 1 would also get ids from users assigned to only the parents of the given role).
* @override to add possibility to get the ids of users assigned to roles that are parents of the given one.
* @since 1.6.1
*/
public function getUserIdsByRole($roleName, $recursive = false)
{
if(!$recursive || empty($roleName)) {
return parent::getUserIdsByRole($roleName);
}

$roles = $this->getParentRoles($roleName, $recursive === true ? null : $recursive);
$userIds = array_reduce($roles, function ($ids, $role) {
$roleIds = parent::getUserIdsByRole($role->name);
return array_merge($ids, $roleIds);
}, []);
return array_unique($userIds);
}


/**
* Returns parent roles of the role specified. Depth isn't limited.
* @param string $roleName name of the role to file parent roles for
* @param null|integer $depth The depth to which to search for parents recursively, if null it won't have any limit. Defaults to `null`.
* @return Role[] Child roles. The array is indexed by the role names.
* First element is an instance of the parent Role itself.
* @throws \yii\base\InvalidParamException if Role was not found that are getting by $roleName
* @since 1.6.1
*/
public function getParentRoles($roleName, $depth = null)
{
$role = $this->getRole($roleName);

if ($role === null) {
throw new InvalidArgumentException("Role \"$roleName\" not found.");
}

$result = [];
$this->getParentsRecursive($roleName, $result, $depth);

$roles = [$roleName => $role];

$roles += $result;

return $roles;
}

/**
* Recursively finds all parents and grandparents of the specified item.
* @param string $name the name of the item whose children are to be looked for.
* @param array $result the children and grand children (in array keys)
* @param null|integer $depth The depth to which to search recursively, if null it won't have any limit. Defaults to `null`.
* @since 1.6.1
*/
protected function getParentsRecursive($name, &$result = [], &$depth = null)
{
$depth -= 1; // Cannot use -- because we have to cast `null` to integer
$query = (new Query())
->select(['name', 'type', 'description', 'rule_name', 'data', 'created_at', 'updated_at'])
->from([$this->itemTable, $this->itemChildTable])
->where(['child' => $name, 'name' => new Expression('[[parent]]')]);

foreach ($query->all($this->db) as $row) {
if(isset($result[$row['name']])) {
continue;
}
$result[$row['name']] = $this->populateItem($row);
// If we have yet to reach the maximum depth, we continue.
// If $depth was orginally `null` it'd start from -1 so decrements will never make reach 0
if($depth !== 0) {
$this->getParentsRecursive($row['name'], $result);
}
}
}
}
5 changes: 3 additions & 2 deletions src/User/Controller/SecurityController.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,10 @@ public function actionConfirm()
$validators = $module->twoFactorAuthenticationValidators;
$credentials = Yii::$app->session->get('credentials');
$login = $credentials['login'];
$user = User::findOne(['email' => $login]);
$userModel = $this->getClassMap()->get(User::class);
$user = $userModel::findOne(['email' => $login]);
if ($user == null) {
$user = User::findOne(['username' => $login]);
$user = $userModel::findOne(['username' => $login]);
}
$tfType = $user->getAuthTfType();

Expand Down
63 changes: 15 additions & 48 deletions src/User/Controller/api/v1/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,37 +139,6 @@ public function checkAccess($action, $model = null, $params = [])
}
}

/**
* Override beforeAction. If the api is called with parameter username get the id of the user and set it in query params
* @param mixed $action
*/
public function beforeAction($action)
{
if ($action == 'create') {
return parent::beforeAction($action);
}

$id = Yii::$app->request->getQueryParam('id');
if (!is_null($id)) {
return parent::beforeAction($action);
}

$username = Yii::$app->request->getQueryParam('username');
if (is_null($username)) {
return parent::beforeAction($action);
}

$user = $this->userQuery->where(['username' => $username])->one();
if (is_null($user)) { // Check user, so ` $username` parameter
return parent::beforeAction($action);
}

$params = Yii::$app->request->getQueryParams();
$params['id'] = $user->id;
Yii::$app->request->setQueryParams($params);

return parent::beforeAction($action);
}
/**
* Create a user.
*/
Expand Down Expand Up @@ -207,15 +176,14 @@ public function actionCreate()
* Update a user.
* @param int $id ID of the user.
*/
public function actionUpdate($id = null)
public function actionUpdate($id)
{
// Check access
$this->checkAccess($this->action);
$id = Yii::$app->request->getQueryParam('id');

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand Down Expand Up @@ -247,18 +215,18 @@ public function actionDelete($id)
// Check access
$this->checkAccess($this->action);

// Check ID parameter (whether own account)
if ((int)$id === Yii::$app->user->getId()) {
throw new BadRequestHttpException(Yii::t('usuario', 'You cannot remove your own account.'));
}

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}

// Check ID parameter (whether own account)
if ($user->id === Yii::$app->user->getId()) {
throw new BadRequestHttpException(Yii::t('usuario', 'You cannot remove your own account.'));
}

// Create event object
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
Expand All @@ -284,7 +252,7 @@ public function actionUpdateProfile($id)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand Down Expand Up @@ -322,7 +290,7 @@ public function actionAssignments($id)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand All @@ -343,7 +311,7 @@ public function actionConfirm($id)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand All @@ -366,11 +334,10 @@ public function actionConfirm($id)
* Block and unblock the user.
* @param int $id ID of the user.
*/
public function actionBlock($id = null)
public function actionBlock($id)
{
// Check access
$this->checkAccess($this->action);
$id = Yii::$app->request->getQueryParam('id');

// Check ID parameter (whether own account)
if ((int)$id === Yii::$app->user->getId()) {
Expand All @@ -379,7 +346,7 @@ public function actionBlock($id = null)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand Down Expand Up @@ -407,7 +374,7 @@ public function actionPasswordReset($id)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand All @@ -432,7 +399,7 @@ public function actionForcePasswordChange($id)

// Get user model
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$user = $this->userQuery->whereIdOrUsernameOrEmail($id)->one();
if (is_null($user)) { // Check user, so `$id` parameter
$this->throwUser404();
}
Expand Down
12 changes: 12 additions & 0 deletions src/User/Query/UserQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@

class UserQuery extends ActiveQuery
{
/**
* @param $idOrUsernameOrEmail
*
* @return $this
*/
public function whereIdOrUsernameOrEmail($idOrUsernameOrEmail)
{
return filter_var($idOrUsernameOrEmail, FILTER_VALIDATE_INT)
? $this->whereId($idOrUsernameOrEmail)
: $this->whereUsernameOrEmail($idOrUsernameOrEmail);
}

/**
* @param $usernameOrEmail
*
Expand Down
2 changes: 1 addition & 1 deletion src/User/Widget/LoginWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class LoginWidget extends Widget
public function run()
{
return $this->render(
$this->getModule()->$this->viewPath .'/widgets/login/form',
$this->getModule()->viewPath .'/widgets/login/form',
[
'model' => Yii::createObject(LoginForm::class),
]
Expand Down
2 changes: 1 addition & 1 deletion src/User/resources/i18n/es/usuario.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
'Here you can download your personal data in a comma separated values format.' => 'Aquí puede descargar su información personal en formato de valores separados por comas.',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => 'Doy mi consentimiento para el procesamiento de mis datos personales y el uso de «cookies» para facilitar el funcionamiento de este sitio. Para más información lea nuestra {privacyPolicy}',
'If you already registered, sign in and connect this account on settings page' => 'Si ya está registrado, inicie sesión y conecte esta cuenta en la página de configuración',
'If you cannot click the link, please try pasting the text into your browser' => 'Si no puede pulsar en el enlace, intente pegar el siguiente texto en su navegador web',
'If you cannot click the link, please try pasting the text into your browser' => 'Si no puede pulsar en el enlace, intente pegar el texto en su navegador web',
'If you did not make this request you can ignore this email' => 'Si no hizo esta petición, puede ignorar este mensaje',
'Impersonate this user' => 'Personificar este usuario',
'In order to complete your registration, please click the link below' => 'Para completar el registro, pulse el siguiente enlace',
Expand Down
7 changes: 7 additions & 0 deletions src/User/resources/views/mail/text/welcome.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
* @var \Da\User\Module $module
* @var bool $showPassword
*/

use yii\helpers\Url;

?>
<?= Yii::t('usuario', 'Hello') ?>,

Expand All @@ -23,6 +26,10 @@
<?= Yii::t('usuario', 'We have generated a password for you') ?>:
<?= $user->password ?>
<?php endif ?>
<?php if ($module->allowPasswordRecovery): ?>
<?= Yii::t('usuario', 'If you haven\'t received a password, you can reset it at') ?>:
<?= Url::to(['/user/recovery/request'], true) ?>
<?php endif ?>

<?php if ($token !== null): ?>
<?= Yii::t('usuario', 'In order to complete your registration, please click the link below') ?>.
Expand Down
2 changes: 1 addition & 1 deletion src/User/resources/views/mail/welcome.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<?= Yii::t('usuario', 'We have generated a password for you') ?>: <strong><?= $user->password ?></strong>
<?php endif ?>
<?php if ($module->allowPasswordRecovery): ?>
<?= Yii::t('usuario', 'If you haven\'t received a password, you can reset it at') ?>: <strong><?= Html::a(Html::encode(Url::to(['/user/forgot'], true)), Url::to(['/user/forgot'], true)) ?></strong>
<?= Yii::t('usuario', 'If you haven\'t received a password, you can reset it at') ?>: <strong><?= Html::a(Html::encode(Url::to(['/user/recovery/request'], true)), Url::to(['/user/recovery/request'], true)) ?></strong>
<?php endif ?>

</p>
Expand Down

0 comments on commit de84979

Please sign in to comment.