Meged from upstream master
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,11 +1,22 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 1.1.2 - Work in progress
|
||||
## 1.1.4 - Work in progress
|
||||
- Enh: Check enableEmailConfirmation on registration (faenir)
|
||||
- Fix #154: Fix DateTime constructor with Unix timestamps (tonydspaniard)
|
||||
|
||||
## 1.1.2-3 - February 9, 2018
|
||||
- Bug: Bugfix for Model events UserEvent::EVENT_BEFORE_CONFIRMATION and UserEvent::EVENT_AFTER_CONFIRMATION (ajmedway)
|
||||
- Bug: Bugfix for Model events UserEvent::EVENT_BEFORE_CREATE and UserEvent::EVENT_AFTER_CREATE (ajmedway)
|
||||
- Enh #137: Added the ability to make `enableAutologin` configurable (pappfer)
|
||||
- Enh #135: Added Estonian translation (tonisormisson)
|
||||
- Bug #133: Fix user search returning no results in admin page (phiurs)
|
||||
- Bug #125: Fix validation in non-ajax requests (faenir)
|
||||
- Bug #122: Fix wrong email message for email address change (liviuk2)
|
||||
- Bug #102: Implemented password expiration feature (maxxer)
|
||||
- Enh #143: Introduced "conflict" configuration in composer.json (maxxer)
|
||||
- Enh #145: Allowed the `+` sign in username (maxxer)
|
||||
- Bug #9: Documentation about migration from Dektrium tools (maxxer)
|
||||
- Bug #110: Honor `enableFlashMessages` in `PasswordRecoveryService` (maxxer)
|
||||
|
||||
## 1.1.1 - November 27, 2017
|
||||
- Bug #115: Convert client_id to string because pgsql fail with type convertion (Dezinger)
|
||||
|
||||
@ -89,6 +89,10 @@
|
||||
"pattern-skip-version": "(-build|-patch)"
|
||||
}
|
||||
},
|
||||
"conflict": {
|
||||
"dektrium/yii2-rbac": "*",
|
||||
"dektrium/yii2-user": "*"
|
||||
},
|
||||
"extra": {
|
||||
"bootstrap": "Da\\User\\Bootstrap"
|
||||
}
|
||||
|
||||
@ -110,10 +110,27 @@ Once we have it installed, we have to configure it on your `config.php` file.
|
||||
'modules' => [
|
||||
'user' => [
|
||||
'class' => Da\User\Module::class,
|
||||
// ...other configs from here: [Configuration Options](installation/configuration-options.md), e.g.
|
||||
// 'generatePasswords' => true,
|
||||
// 'switchIdentitySessionKey' => 'myown_usuario_admin_user_key',
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
NOTE: If you are using the Yii2 Basic Template, make sure you remove this (default user model config) from your `config.php`,
|
||||
i.e. `config/web.php` file:
|
||||
|
||||
```php
|
||||
'components' => [
|
||||
'user' => [
|
||||
'identityClass' => 'app\models\User',
|
||||
'enableAutoLogin' => true,
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
This will ensure the proper functionality of login/guest user detection etc.
|
||||
|
||||
Configuration may differ from template to template, the following are some guidelines for sidekit app template and
|
||||
the official Yii2 advanced application template:
|
||||
|
||||
@ -127,6 +144,7 @@ See also all the possible configuration options available:
|
||||
- [Console Commands](installation/console-commands.md)
|
||||
- [Mailer](installation/mailer.md)
|
||||
- [Available Actions](installation/available-actions.md)
|
||||
- [Migration guide from Dektrium tools](installation/migration-guide-from-dektrium-tools.md)
|
||||
|
||||
Enhancing and Overriding
|
||||
------------------------
|
||||
@ -171,4 +189,4 @@ Contributing
|
||||
- [How to Contribute](contributing/how-to.md)
|
||||
- [Clean Code](contributing/clean-code.md)
|
||||
|
||||
© [2amigos](http://www.2amigos.us/) 2013-2017
|
||||
© [2amigos](http://www.2amigos.us/) 2013-2018
|
||||
|
||||
@ -12,7 +12,7 @@ packages, and everything you need to start project in no time with batteries inc
|
||||
> [Check this manual page](sidekit-application-template.md) if you decided to use it.
|
||||
|
||||
If you want to use the default [Basic Application Template](https://github.com/yiisoft/yii2-app-basic) with our
|
||||
extension, then check [Basic Application Template documentation page](basic-application-template.md).
|
||||
extension, then check [Basic Application Template documentation page](yii2-application-template.md).
|
||||
|
||||
Step 1 - Install Advanced Application template
|
||||
----------------------------------------------
|
||||
|
||||
@ -48,6 +48,25 @@ If `true` it will enable password recovery process.
|
||||
If `true` and `allowPasswordRecovery` is false, it will enable administrator to send a password recovery email to a
|
||||
user.
|
||||
|
||||
#### maxPasswordAge (type: `integer`, default: `null`)
|
||||
|
||||
If set to an integer value it will check user password age. If the days since last password change are greater than this configuration value
|
||||
user will be forced to change it. This enforcement is done only at login stage. In order to perform the check in every action you must configure
|
||||
a filter into your controller like this:
|
||||
```
|
||||
use Da\User\Filter\PasswordAgeEnforceFilter;
|
||||
class SiteController extends Controller
|
||||
{
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
[...]
|
||||
'enforcePasswordAge' => [
|
||||
'class' => PasswordAgeEnforceFilter::className(),
|
||||
],
|
||||
```
|
||||
This will redirect the user to their account page until the password has been updated.
|
||||
|
||||
#### allowAccountDelete (type: `boolean`, default: `true`)
|
||||
|
||||
If `true` users will be able to remove their own accounts.
|
||||
|
||||
387
docs/installation/migration-guide-from-dektrium-tools.md
Normal file
387
docs/installation/migration-guide-from-dektrium-tools.md
Normal file
@ -0,0 +1,387 @@
|
||||
# Migration guide from Dektrium tools
|
||||
|
||||
yii2-usuario is 99% compatible with [dektrium](https://github.com/dektrium/) tools.
|
||||
|
||||
## Package removal
|
||||
|
||||
First of all you need to remove the old packages. Depending on your installation you
|
||||
need to remove one or both:
|
||||
```
|
||||
composer remove dektrium/yii2-user
|
||||
composer remove dektrium/yii2-rbac
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure the `config/console.php` stuff:
|
||||
|
||||
```php
|
||||
'authManager' => [
|
||||
'class' => 'yii\rbac\DbManager',
|
||||
],
|
||||
```
|
||||
|
||||
Configure the controller map for migrations
|
||||
|
||||
```php
|
||||
'controllerMap' => [
|
||||
'migrate' => [
|
||||
'class' => 'yii\console\controllers\MigrateController',
|
||||
'migrationNamespaces' => [
|
||||
'Da\User\Migration',
|
||||
],
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
Remove the *modules > rbac* configuration parameter, and replace the value of *modules > user* with:
|
||||
```php
|
||||
'user' => Da\User\Module::class,
|
||||
```
|
||||
|
||||
In `config/web.php` remove *module > rbac* configuration and change the *modules > user* with:
|
||||
```php
|
||||
...
|
||||
'user' => [
|
||||
'class' => Da\User\Module::class,
|
||||
// Othe yii2-usuario configuration parameters
|
||||
'enableRegistration' => false,
|
||||
],
|
||||
...
|
||||
```
|
||||
|
||||
* If you had `modelMap` customization you have to replace them with `classMap`.
|
||||
* In your extended model replace the `BaseUser` inheritance from `dektrium\user\models\User` to `Da\User\Model\User`
|
||||
* If you had controller remapping replace the inheritance from `dektrium\user\controllers\XX` to `Da\User\Controller\XX`
|
||||
|
||||
## Rbac migrations
|
||||
|
||||
[yii2-rbac](https://github.com/dektrium/yii2-rbac) had a nice tool which are rbac migrations, which helped writing new permissions and roles.
|
||||
There's no such feature in yii2-usuario, but in case you need to still apply them you can:
|
||||
|
||||
1. create a migration component which basically it's the same as the original [Migration](https://github.com/dektrium/yii2-rbac/blob/master/migrations/Migration.php) object, with some minor changes. Copy the content below and save it in your `@app/components/RbacMigration.php`:
|
||||
```php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Dektrium project.
|
||||
*
|
||||
* (c) Dektrium project <http://github.com/dektrium/>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace app\components;
|
||||
|
||||
use yii\rbac\DbManager;
|
||||
use yii\db\Migration;
|
||||
use yii\di\Instance;
|
||||
use yii\rbac\Item;
|
||||
use yii\rbac\Permission;
|
||||
use yii\rbac\Role;
|
||||
use yii\rbac\Rule;
|
||||
|
||||
/**
|
||||
* Migration for applying new RBAC items.
|
||||
*
|
||||
* @author Dmitry Erofeev <dmeroff@gmail.com>
|
||||
*/
|
||||
class RbacMigration extends Migration
|
||||
{
|
||||
/**
|
||||
* @var string|DbManager The auth manager component ID that this migration should work with.
|
||||
*/
|
||||
public $authManager = 'authManager';
|
||||
|
||||
/**
|
||||
* Initializes the migration.
|
||||
* This method will set [[authManager]] to be the 'authManager' application component, if it is `null`.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
|
||||
$this->authManager = Instance::ensure($this->authManager, DbManager::className());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new permission.
|
||||
*
|
||||
* @param string $name The name of the permission
|
||||
* @param string $description The description of the permission
|
||||
* @param string|null $ruleName The rule associated with the permission
|
||||
* @param mixed|null $data The additional data associated with the permission
|
||||
* @return Permission
|
||||
*/
|
||||
protected function createPermission($name, $description = '', $ruleName = null, $data = null)
|
||||
{
|
||||
echo " > create permission $name ...";
|
||||
$time = microtime(true);
|
||||
$permission = $this->authManager->createPermission($name);
|
||||
|
||||
$permission->description = $description;
|
||||
$permission->ruleName = $ruleName;
|
||||
$permission->data = $data;
|
||||
|
||||
$this->authManager->add($permission);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new role.
|
||||
*
|
||||
* @param string $name The name of the role
|
||||
* @param string $description The description of the role
|
||||
* @param string|null $ruleName The rule associated with the role
|
||||
* @param mixed|null $data The additional data associated with the role
|
||||
* @return Role
|
||||
*/
|
||||
protected function createRole($name, $description = '', $ruleName = null, $data = null)
|
||||
{
|
||||
echo " > create role $name ...";
|
||||
$time = microtime(true);
|
||||
$role = $this->authManager->createRole($name);
|
||||
|
||||
$role->description = $description;
|
||||
$role->ruleName = $ruleName;
|
||||
$role->data = $data;
|
||||
|
||||
$this->authManager->add($role);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new rule.
|
||||
*
|
||||
* @param string $ruleName The name of the rule
|
||||
* @param string|array $definition The class of the rule
|
||||
* @return Rule
|
||||
*/
|
||||
protected function createRule($ruleName, $definition)
|
||||
{
|
||||
echo " > create rule $ruleName ...";
|
||||
$time = microtime(true);
|
||||
|
||||
if (is_array($definition)) {
|
||||
$definition['name'] = $ruleName;
|
||||
} else {
|
||||
$definition = [
|
||||
'class' => $definition,
|
||||
'name' => $ruleName,
|
||||
];
|
||||
}
|
||||
|
||||
/** @var Rule $rule */
|
||||
$rule = \Yii::createObject($definition);
|
||||
|
||||
$this->authManager->add($rule);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds either role or permission or throws an exception if it is not found.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Permission|Role|null
|
||||
*/
|
||||
protected function findItem($name)
|
||||
{
|
||||
$item = $this->authManager->getRole($name);
|
||||
|
||||
if ($item instanceof Role) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
$item = $this->authManager->getPermission($name);
|
||||
|
||||
if ($item instanceof Permission) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the role or throws an exception if it is not found.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Role|null
|
||||
*/
|
||||
protected function findRole($name)
|
||||
{
|
||||
$role = $this->authManager->getRole($name);
|
||||
|
||||
if ($role instanceof Role) {
|
||||
return $role;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the permission or throws an exception if it is not found.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Permission|null
|
||||
*/
|
||||
protected function findPermission($name)
|
||||
{
|
||||
$permission = $this->authManager->getPermission($name);
|
||||
|
||||
if ($permission instanceof Permission) {
|
||||
return $permission;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes auth item.
|
||||
*
|
||||
* @param string|Item $item Either item name or item instance to be removed.
|
||||
*/
|
||||
protected function removeItem($item)
|
||||
{
|
||||
if (is_string($item)) {
|
||||
$item = $this->findItem($item);
|
||||
}
|
||||
|
||||
echo " > removing $item->name ...";
|
||||
$time = microtime(true);
|
||||
$this->authManager->remove($item);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds child.
|
||||
*
|
||||
* @param Item|string $parent Either name or Item instance which is parent
|
||||
* @param Item|string $child Either name or Item instance which is child
|
||||
*/
|
||||
protected function addChild($parent, $child)
|
||||
{
|
||||
if (is_string($parent)) {
|
||||
$parent = $this->findItem($parent);
|
||||
}
|
||||
|
||||
if (is_string($child)) {
|
||||
$child = $this->findItem($child);
|
||||
}
|
||||
|
||||
echo " > adding $child->name as child to $parent->name ...";
|
||||
$time = microtime(true);
|
||||
$this->authManager->addChild($parent, $child);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a role to a user.
|
||||
*
|
||||
* @param string|Role $role
|
||||
* @param string|int $userId
|
||||
*/
|
||||
protected function assign($role, $userId)
|
||||
{
|
||||
if (is_string($role)) {
|
||||
$role = $this->findRole($role);
|
||||
}
|
||||
|
||||
echo " > assigning $role->name to user $userId ...";
|
||||
$time = microtime(true);
|
||||
$this->authManager->assign($role, $userId);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates role.
|
||||
*
|
||||
* @param string|Role $role
|
||||
* @param string $description
|
||||
* @param string $ruleName
|
||||
* @param mixed $data
|
||||
* @return Role
|
||||
*/
|
||||
protected function updateRole($role, $description = '', $ruleName = null, $data = null)
|
||||
{
|
||||
if (is_string($role)) {
|
||||
$role = $this->findRole($role);
|
||||
}
|
||||
|
||||
echo " > update role $role->name ...";
|
||||
$time = microtime(true);
|
||||
|
||||
$role->description = $description;
|
||||
$role->ruleName = $ruleName;
|
||||
$role->data = $data;
|
||||
|
||||
$this->authManager->update($role->name, $role);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates permission.
|
||||
*
|
||||
* @param string|Permission $permission
|
||||
* @param string $description
|
||||
* @param string $ruleName
|
||||
* @param mixed $data
|
||||
* @return Permission
|
||||
*/
|
||||
protected function updatePermission($permission, $description = '', $ruleName = null, $data = null)
|
||||
{
|
||||
if (is_string($permission)) {
|
||||
$permission = $this->findPermission($permission);
|
||||
}
|
||||
|
||||
echo " > update permission $permission->name ...";
|
||||
$time = microtime(true);
|
||||
|
||||
$permission->description = $description;
|
||||
$permission->ruleName = $ruleName;
|
||||
$permission->data = $data;
|
||||
|
||||
$this->authManager->update($permission->name, $permission);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates rule.
|
||||
*
|
||||
* @param string $ruleName
|
||||
* @param string $className
|
||||
* @return Rule
|
||||
*/
|
||||
protected function updateRule($ruleName, $className)
|
||||
{
|
||||
echo " > update rule $ruleName ...";
|
||||
$time = microtime(true);
|
||||
|
||||
/** @var Rule $rule */
|
||||
$rule = \Yii::createObject([
|
||||
'class' => $className,
|
||||
'name' => $ruleName,
|
||||
]);
|
||||
|
||||
$this->authManager->update($ruleName, $rule);
|
||||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n";
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
2. change the inheritance of the `@app/rbac/migrations` files to `app\components\RbacMigration as Migration`
|
||||
|
||||
... and you're done! You can still apply your rbac migrations with `./yii migrate/up --migrationPath=@yii/rbac/migrations`.
|
||||
@ -25,6 +25,10 @@ use yii\console\Application as ConsoleApplication;
|
||||
use yii\i18n\PhpMessageSource;
|
||||
use yii\web\Application as WebApplication;
|
||||
|
||||
use yii\base\Event;
|
||||
use Da\User\Event\FormEvent;
|
||||
use Da\User\Controller\SecurityController;
|
||||
|
||||
/**
|
||||
* Bootstrap class of the yii2-usuario extension. Configures container services, initializes translations,
|
||||
* builds class map, and does the other setup actions participating in the application bootstrap process.
|
||||
@ -91,6 +95,7 @@ class Bootstrap implements BootstrapInterface
|
||||
// services
|
||||
$di->set(Service\AccountConfirmationService::class);
|
||||
$di->set(Service\EmailChangeService::class);
|
||||
$di->set(Service\PasswordExpireService::class);
|
||||
$di->set(Service\PasswordRecoveryService::class);
|
||||
$di->set(Service\ResendConfirmationService::class);
|
||||
$di->set(Service\ResetPasswordService::class);
|
||||
@ -144,12 +149,24 @@ class Bootstrap implements BootstrapInterface
|
||||
$di->set(Search\RoleSearch::class);
|
||||
}
|
||||
|
||||
// Attach an event to check if the password has expired
|
||||
if (!is_null(Yii::$app->getModule('user')->maxPasswordAge)) {
|
||||
Event::on(SecurityController::class, FormEvent::EVENT_AFTER_LOGIN, function (FormEvent $event) {
|
||||
$user = $event->form->user;
|
||||
if ($user->password_age >= Yii::$app->getModule('user')->maxPasswordAge) {
|
||||
// Force password change
|
||||
Yii::$app->session->setFlash('warning', Yii::t('usuario', 'Your password has expired, you must change it now'));
|
||||
Yii::$app->response->redirect(['/user/settings/account'])->send();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($app instanceof WebApplication) {
|
||||
// override Yii
|
||||
$di->set(
|
||||
'yii\web\User',
|
||||
[
|
||||
'enableAutoLogin' => $app->getModule('user')->enableAutologin,
|
||||
'enableAutoLogin' => $app->getModule('user')->enableAutoLogin,
|
||||
'loginUrl' => ['/user/security/login'],
|
||||
'identityClass' => $di->get(ClassMapHelper::class)->get(User::class),
|
||||
]
|
||||
|
||||
@ -18,6 +18,7 @@ use Da\User\Model\Profile;
|
||||
use Da\User\Model\User;
|
||||
use Da\User\Query\UserQuery;
|
||||
use Da\User\Search\UserSearch;
|
||||
use Da\User\Service\PasswordExpireService;
|
||||
use Da\User\Service\PasswordRecoveryService;
|
||||
use Da\User\Service\SwitchIdentityService;
|
||||
use Da\User\Service\UserBlockService;
|
||||
@ -328,4 +329,20 @@ class AdminController extends Controller
|
||||
|
||||
return $this->redirect(['index']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the user to change password at next login
|
||||
* @param integer $id
|
||||
*/
|
||||
public function actionForcePasswordChange($id)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->userQuery->where(['id' => $id])->one();
|
||||
if ($this->make(PasswordExpireService::class, [$user])->run()) {
|
||||
Yii::$app->session->setFlash("success", Yii::t('usuario', 'User will be required to change password at next login'));
|
||||
} else {
|
||||
Yii::$app->session->setFlash("danger", Yii::t('usuario', 'There was an error in saving user'));
|
||||
}
|
||||
$this->redirect(['index']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,13 +106,17 @@ class RegistrationController extends Controller
|
||||
$mailService = MailFactory::makeWelcomeMailerService($user);
|
||||
|
||||
if ($this->make(UserRegisterService::class, [$user, $mailService])->run()) {
|
||||
Yii::$app->session->setFlash(
|
||||
'info',
|
||||
Yii::t(
|
||||
'usuario',
|
||||
'Your account has been created and a message with further instructions has been sent to your email'
|
||||
)
|
||||
);
|
||||
if ($this->module->enableEmailConfirmation) {
|
||||
Yii::$app->session->setFlash(
|
||||
'info',
|
||||
Yii::t(
|
||||
'usuario',
|
||||
'Your account has been created and a message with further instructions has been sent to your email'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your account has been created'));
|
||||
}
|
||||
$this->trigger(FormEvent::EVENT_AFTER_REGISTER, $event);
|
||||
return $this->render(
|
||||
'/shared/message',
|
||||
|
||||
39
src/User/Filter/PasswordAgeEnforceFilter.php
Normal file
39
src/User/Filter/PasswordAgeEnforceFilter.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the 2amigos/yii2-usuario project.
|
||||
*
|
||||
* (c) 2amigOS! <http://2amigos.us/>
|
||||
* @author Lorenzo Milesi <maxxer@yetopen.it>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Da\User\Filter;
|
||||
|
||||
use Yii;
|
||||
use yii\base\ActionFilter;
|
||||
|
||||
class PasswordAgeEnforceFilter extends ActionFilter
|
||||
{
|
||||
public function beforeAction($action)
|
||||
{
|
||||
$maxPasswordAge = Yii::$app->getModule('user')->maxPasswordAge;
|
||||
// If feature is not set do nothing (or raise a configuration error?)
|
||||
if (is_null($maxPasswordAge)) {
|
||||
return parent::beforeAction($action);
|
||||
}
|
||||
if (Yii::$app->user->isGuest) {
|
||||
// Not our business
|
||||
return parent::beforeAction($action);
|
||||
}
|
||||
if (Yii::$app->user->identity->password_age >= $maxPasswordAge) {
|
||||
// Force password change
|
||||
Yii::$app->getSession()->setFlash('warning', Yii::t('usuario', 'Your password has expired, you must change it now'));
|
||||
return Yii::$app->response->redirect(['/user/settings/account'])->send();
|
||||
}
|
||||
|
||||
return parent::beforeAction($action);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the 2amigos/yii2-usuario project.
|
||||
*
|
||||
* (c) 2amigOS! <http://2amigos.us/>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Da\User\Migration;
|
||||
|
||||
use yii\db\Migration;
|
||||
|
||||
class m000000_000007_enable_password_expiration extends Migration
|
||||
{
|
||||
public function safeUp()
|
||||
{
|
||||
$this->addColumn('{{%user}}', 'password_changed_at', $this->integer()->null());
|
||||
}
|
||||
|
||||
public function safeDown()
|
||||
{
|
||||
$this->dropColumn('{{%user}}', 'password_changed_at');
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,7 @@ use yii\base\InvalidParamException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\behaviors\TimestampBehavior;
|
||||
use yii\db\ActiveRecord;
|
||||
use yii\db\Expression;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\web\Application;
|
||||
use yii\web\IdentityInterface;
|
||||
@ -49,6 +50,8 @@ use yii\web\IdentityInterface;
|
||||
* @property int $created_at
|
||||
* @property int $updated_at
|
||||
* @property int $last_login_at
|
||||
* @property int $password_changed_at
|
||||
* @property int $password_age
|
||||
*
|
||||
* Defined relations:
|
||||
* @property SocialNetworkAccount[] $socialNetworkAccounts
|
||||
@ -95,6 +98,7 @@ class User extends ActiveRecord implements IdentityInterface
|
||||
'password_hash',
|
||||
$security->generatePasswordHash($this->password, $this->getModule()->blowfishCost)
|
||||
);
|
||||
$this->password_changed_at = time();
|
||||
}
|
||||
|
||||
return parent::beforeSave($insert);
|
||||
@ -147,6 +151,8 @@ class User extends ActiveRecord implements IdentityInterface
|
||||
'created_at' => Yii::t('usuario', 'Registration time'),
|
||||
'confirmed_at' => Yii::t('usuario', 'Confirmation time'),
|
||||
'last_login_at' => Yii::t('usuario', 'Last login'),
|
||||
'password_changed_at' => Yii::t('usuario', 'Last password change'),
|
||||
'password_age' => Yii::t('usuario', 'Password age'),
|
||||
];
|
||||
}
|
||||
|
||||
@ -175,7 +181,7 @@ class User extends ActiveRecord implements IdentityInterface
|
||||
return [
|
||||
// username rules
|
||||
'usernameRequired' => ['username', 'required', 'on' => ['register', 'create', 'connect', 'update']],
|
||||
'usernameMatch' => ['username', 'match', 'pattern' => '/^[-a-zA-Z0-9_\.@]+$/'],
|
||||
'usernameMatch' => ['username', 'match', 'pattern' => '/^[-a-zA-Z0-9_\.@\+]+$/'],
|
||||
'usernameLength' => ['username', 'string', 'min' => 3, 'max' => 255],
|
||||
'usernameTrim' => ['username', 'trim'],
|
||||
'usernameUnique' => [
|
||||
@ -328,4 +334,17 @@ class User extends ActiveRecord implements IdentityInterface
|
||||
{
|
||||
throw new NotSupportedException('Method "' . __CLASS__ . '::' . __METHOD__ . '" is not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns password age in days
|
||||
* @return integer
|
||||
*/
|
||||
public function getPassword_age()
|
||||
{
|
||||
if (is_null($this->password_changed_at)) {
|
||||
return $this->getModule()->maxPasswordAge;
|
||||
}
|
||||
$d = new \DateTime("@{$this->password_changed_at}");
|
||||
return $d->diff(new \DateTime(), true)->format("%a");
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,9 +29,9 @@ class Module extends BaseModule
|
||||
*/
|
||||
public $twoFactorAuthenticationCycles = 1;
|
||||
/**
|
||||
* @var bool whether to allow autologin or not
|
||||
* @var bool whether to allow auto login or not
|
||||
*/
|
||||
public $enableAutologin = true;
|
||||
public $enableAutoLogin = true;
|
||||
/**
|
||||
* @var bool whether to allow registration process or not
|
||||
*/
|
||||
@ -132,4 +132,8 @@ class Module extends BaseModule
|
||||
* @var string the session key name to impersonate users. Please, modify it for security reasons!
|
||||
*/
|
||||
public $switchIdentitySessionKey = 'yuik_usuario';
|
||||
/**
|
||||
* @var integer If != NULL sets a max password age in days
|
||||
*/
|
||||
public $maxPasswordAge = null;
|
||||
}
|
||||
|
||||
@ -66,8 +66,6 @@ class RuleSearch extends Rule
|
||||
|
||||
if (!$this->validate()) {
|
||||
$query->where('0=1');
|
||||
var_dump($this->load($params));
|
||||
die();
|
||||
}
|
||||
|
||||
return $this->make(
|
||||
|
||||
30
src/User/Service/PasswordExpireService.php
Normal file
30
src/User/Service/PasswordExpireService.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the 2amigos/yii2-usuario project.
|
||||
*
|
||||
* (c) 2amigOS! <http://2amigos.us/>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Da\User\Service;
|
||||
|
||||
use Da\User\Contracts\ServiceInterface;
|
||||
use Da\User\Model\User;
|
||||
|
||||
class PasswordExpireService implements ServiceInterface
|
||||
{
|
||||
protected $model;
|
||||
|
||||
public function __construct(User $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->model->updateAttributes(['last_login_at' => time()]);
|
||||
}
|
||||
}
|
||||
@ -16,12 +16,14 @@ use Da\User\Factory\TokenFactory;
|
||||
use Da\User\Model\User;
|
||||
use Da\User\Query\UserQuery;
|
||||
use Da\User\Traits\MailAwareTrait;
|
||||
use Da\User\Traits\ModuleAwareTrait;
|
||||
use Exception;
|
||||
use Yii;
|
||||
|
||||
class PasswordRecoveryService implements ServiceInterface
|
||||
{
|
||||
use MailAwareTrait;
|
||||
use ModuleAwareTrait;
|
||||
|
||||
protected $query;
|
||||
|
||||
@ -58,10 +60,12 @@ class PasswordRecoveryService implements ServiceInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
Yii::$app->session->setFlash(
|
||||
'info',
|
||||
Yii::t('usuario', 'An email has been sent with instructions for resetting your password')
|
||||
);
|
||||
if ($this->getModule()->enableFlashMessages == true) {
|
||||
Yii::$app->session->setFlash(
|
||||
'info',
|
||||
Yii::t('usuario', 'An email has been sent with instructions for resetting your password')
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
|
||||
@ -14,9 +14,12 @@ namespace Da\User\Service;
|
||||
use Da\User\Contracts\ServiceInterface;
|
||||
use Da\User\Event\UserEvent;
|
||||
use Da\User\Model\User;
|
||||
use Da\User\Traits\MailAwareTrait;
|
||||
|
||||
class UserConfirmationService implements ServiceInterface
|
||||
{
|
||||
use MailAwareTrait;
|
||||
|
||||
protected $model;
|
||||
|
||||
public function __construct(User $model)
|
||||
@ -26,9 +29,12 @@ class UserConfirmationService implements ServiceInterface
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->model->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION);
|
||||
$model = $this->model;
|
||||
$event = $this->make(UserEvent::class, [$model]);
|
||||
|
||||
$this->model->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event);
|
||||
if ((bool)$this->model->updateAttributes(['confirmed_at' => time()])) {
|
||||
$this->model->trigger(UserEvent::EVENT_AFTER_CONFIRMATION);
|
||||
$this->model->trigger(UserEvent::EVENT_AFTER_CONFIRMATION, $event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -57,14 +57,15 @@ class UserCreateService implements ServiceInterface
|
||||
? $model->password
|
||||
: $this->securityHelper->generatePassword(8);
|
||||
|
||||
$model->trigger(UserEvent::EVENT_BEFORE_CREATE);
|
||||
$event = $this->make(UserEvent::class, [$model]);
|
||||
$model->trigger(UserEvent::EVENT_BEFORE_CREATE, $event);
|
||||
|
||||
if (!$model->save()) {
|
||||
$transaction->rollBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
$model->trigger(UserEvent::EVENT_AFTER_CREATE);
|
||||
$model->trigger(UserEvent::EVENT_AFTER_CREATE, $event);
|
||||
if (!$this->sendMail($model)) {
|
||||
Yii::$app->session->setFlash(
|
||||
'warning',
|
||||
|
||||
@ -54,7 +54,7 @@ class ReCaptchaWidget extends InputWidget
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (Yii::$app->get('recaptcha')) {
|
||||
if (!Yii::$app->get('recaptcha')) {
|
||||
throw new InvalidConfigException(Yii::t('usuario', 'The "recaptcha" component must be configured.'));
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +92,7 @@ $module = Yii::$app->getModule('user');
|
||||
'format' => 'raw',
|
||||
'visible' => Yii::$app->getModule('user')->enableEmailConfirmation,
|
||||
],
|
||||
'password_age',
|
||||
[
|
||||
'header' => Yii::t('usuario', 'Block status'),
|
||||
'value' => function ($model) {
|
||||
@ -121,7 +122,7 @@ $module = Yii::$app->getModule('user');
|
||||
],
|
||||
[
|
||||
'class' => 'yii\grid\ActionColumn',
|
||||
'template' => '{switch} {reset} {update} {delete}',
|
||||
'template' => '{switch} {reset} {force-password-change} {update} {delete}',
|
||||
'buttons' => [
|
||||
'switch' => function ($url, $model) use ($module) {
|
||||
if ($model->id != Yii::$app->user->id && $module->enableSwitchIdentities) {
|
||||
@ -158,7 +159,24 @@ $module = Yii::$app->getModule('user');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
'force-password-change' => function ($url, $model) use ($module) {
|
||||
if (is_null($module->maxPasswordAge)) {
|
||||
return null;
|
||||
}
|
||||
return Html::a(
|
||||
'<span class="glyphicon glyphicon-time"></span>',
|
||||
['/user/admin/force-password-change', 'id' => $model->id],
|
||||
[
|
||||
'title' => Yii::t('usuario', 'Force password change at next login'),
|
||||
'data-confirm' => Yii::t(
|
||||
'usuario',
|
||||
'Are you sure you wish the user to change their password at next login?'
|
||||
),
|
||||
'data-method' => 'POST',
|
||||
]
|
||||
);
|
||||
},
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
@ -73,6 +73,7 @@ CREATE TABLE `user` (
|
||||
`auth_tf_enabled` tinyint(1) DEFAULT '0',
|
||||
`flags` int(11) NOT NULL DEFAULT '0',
|
||||
`last_login_at` int(11) DEFAULT NULL,
|
||||
`password_changed_at` int(11) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_unique_email` (`email`),
|
||||
|
||||
@ -50,7 +50,7 @@ class RegistrationCest
|
||||
$I->see(Html::encode('This email address has already been taken'));
|
||||
|
||||
$this->register($I, 'tester@example.com', 'tester', 'tester');
|
||||
$I->see('Your account has been created and a message with further instructions has been sent to your email');
|
||||
$I->see('Your account has been created');
|
||||
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
|
||||
$I->assertTrue($user->isConfirmed);
|
||||
|
||||
@ -96,7 +96,7 @@ class RegistrationCest
|
||||
]);
|
||||
$I->amOnRoute('/user/registration/register');
|
||||
$this->register($I, 'tester@example.com', 'tester');
|
||||
$I->see('Your account has been created and a message with further instructions has been sent to your email');
|
||||
$I->see('Your account has been created');
|
||||
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
|
||||
$I->assertEquals('tester', $user->username);
|
||||
/** @var yii\swiftmailer\Message $message */
|
||||
|
||||
Reference in New Issue
Block a user