added email change strategies
This commit is contained in:
9
lib/User/Contracts/MailChangeStrategyInterface.php
Normal file
9
lib/User/Contracts/MailChangeStrategyInterface.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
namespace Da\User\Contracts;
|
||||||
|
|
||||||
|
interface MailChangeStrategyInterface extends StrategyInterface
|
||||||
|
{
|
||||||
|
const TYPE_INSECURE = 0;
|
||||||
|
const TYPE_DEFAULT = 1;
|
||||||
|
const TYPE_SECURE = 2;
|
||||||
|
}
|
||||||
@ -1,15 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Da\User\Contracts;
|
namespace Da\User\Contracts;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* StrategyInterface.php
|
|
||||||
*
|
|
||||||
* Date: 3/12/16
|
|
||||||
* Time: 16:23
|
|
||||||
* @author Antonio Ramirez <hola@2amigos.us>
|
|
||||||
*/
|
|
||||||
interface StrategyInterface
|
interface StrategyInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function run();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -117,7 +117,7 @@ class RecoveryController extends Controller
|
|||||||
throw new NotFoundHttpException();
|
throw new NotFoundHttpException();
|
||||||
}
|
}
|
||||||
/** @var Token $token */
|
/** @var Token $token */
|
||||||
$token = $this->tokenQuery->whereIsRecoveryType($id, $code)->one();
|
$token = $this->tokenQuery->whereUserId($id)->whereCode($code)->whereIsRecoveryType()->one();
|
||||||
/** @var ResetPasswordEvent $event */
|
/** @var ResetPasswordEvent $event */
|
||||||
$event = $this->make(ResetPasswordEvent::class, [$token]);
|
$event = $this->make(ResetPasswordEvent::class, [$token]);
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use Da\User\Model\SocialNetworkAccount;
|
|||||||
use Da\User\Model\User;
|
use Da\User\Model\User;
|
||||||
use Da\User\Query\SocialNetworkAccountQuery;
|
use Da\User\Query\SocialNetworkAccountQuery;
|
||||||
use Da\User\Query\UserQuery;
|
use Da\User\Query\UserQuery;
|
||||||
|
use Da\User\Service\AccountConfirmationService;
|
||||||
use Da\User\Service\EmailConfirmationService;
|
use Da\User\Service\EmailConfirmationService;
|
||||||
use Da\User\Service\ResendConfirmationService;
|
use Da\User\Service\ResendConfirmationService;
|
||||||
use Da\User\Service\UserConfirmationService;
|
use Da\User\Service\UserConfirmationService;
|
||||||
@ -172,7 +173,7 @@ class RegistrationController extends Controller
|
|||||||
|
|
||||||
$this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event);
|
$this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event);
|
||||||
|
|
||||||
if ($this->make(EmailConfirmationService::class, [$code, $user, $userConfirmationService])->run()) {
|
if ($this->make(AccountConfirmationService::class, [$code, $user, $userConfirmationService])->run()) {
|
||||||
Yii::$app->user->login($user, $this->module->rememberLoginLifespan);
|
Yii::$app->user->login($user, $this->module->rememberLoginLifespan);
|
||||||
Yii::$app->session->setFlash('success', Yii::t('user', 'Thank you, registration is now complete.'));
|
Yii::$app->session->setFlash('success', Yii::t('user', 'Thank you, registration is now complete.'));
|
||||||
|
|
||||||
|
|||||||
160
lib/User/Controller/SettingsController.php
Normal file
160
lib/User/Controller/SettingsController.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
namespace Da\User\Controller;
|
||||||
|
|
||||||
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Event\FormEvent;
|
||||||
|
use Da\User\Event\ProfileEvent;
|
||||||
|
use Da\User\Event\UserEvent;
|
||||||
|
use Da\User\Form\SettingsForm;
|
||||||
|
use Da\User\Model\Profile;
|
||||||
|
use Da\User\Module;
|
||||||
|
use Da\User\Query\ProfileQuery;
|
||||||
|
use Da\User\Query\SocialNetworkAccountQuery;
|
||||||
|
use Da\User\Query\UserQuery;
|
||||||
|
use Da\User\Service\EmailChangeService;
|
||||||
|
use Da\User\Traits\ContainerTrait;
|
||||||
|
use Da\User\Validator\AjaxRequestModelValidator;
|
||||||
|
use yii\filters\AccessControl;
|
||||||
|
use yii\filters\VerbFilter;
|
||||||
|
use yii\web\Controller;
|
||||||
|
use Yii;
|
||||||
|
use yii\web\NotFoundHttpException;
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsController extends Controller
|
||||||
|
{
|
||||||
|
use ContainerTrait;
|
||||||
|
|
||||||
|
protected $profileQuery;
|
||||||
|
protected $userQuery;
|
||||||
|
protected $socialNetworkAccountQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public $defaultAction = 'profile';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SettingsController constructor.
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param Module $module
|
||||||
|
* @param ProfileQuery $profileQuery
|
||||||
|
* @param UserQuery $userQuery
|
||||||
|
* @param SocialNetworkAccountQuery $socialNetworkAccountQuery
|
||||||
|
* @param array $config
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$id,
|
||||||
|
Module $module,
|
||||||
|
ProfileQuery $profileQuery,
|
||||||
|
UserQuery $userQuery,
|
||||||
|
SocialNetworkAccountQuery $socialNetworkAccountQuery,
|
||||||
|
array $config
|
||||||
|
) {
|
||||||
|
$this->profileQuery = $profileQuery;
|
||||||
|
$this->userQuery = $userQuery;
|
||||||
|
$this->socialNetworkAccountQuery = $socialNetworkAccountQuery;
|
||||||
|
parent::__construct($id, $module, $config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function behaviors()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'verbs' => [
|
||||||
|
'class' => VerbFilter::className(),
|
||||||
|
'actions' => [
|
||||||
|
'disconnect' => ['post'],
|
||||||
|
'delete' => ['post'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'access' => [
|
||||||
|
'class' => AccessControl::className(),
|
||||||
|
'rules' => [
|
||||||
|
[
|
||||||
|
'allow' => true,
|
||||||
|
'actions' => ['profile', 'account', 'networks', 'disconnect', 'delete'],
|
||||||
|
'roles' => ['@'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'allow' => true,
|
||||||
|
'actions' => ['confirm'],
|
||||||
|
'roles' => ['?', '@'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionProfile()
|
||||||
|
{
|
||||||
|
$profile = $this->profileQuery->whereId(Yii::$app->user->identity->getId())->one();
|
||||||
|
|
||||||
|
if ($profile === null) {
|
||||||
|
$profile = $this->make(Profile::class);
|
||||||
|
$profile->link('user', Yii::$app->user->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$event = $this->make(ProfileEvent::class, [$profile]);
|
||||||
|
|
||||||
|
$this->make(AjaxRequestModelValidator::class, [$profile])->validate();
|
||||||
|
|
||||||
|
if ($profile->load(Yii::$app->request->post())) {
|
||||||
|
$this->trigger(UserEvent::EVENT_BEFORE_PROFILE_UPDATE, $event);
|
||||||
|
if ($profile->save()) {
|
||||||
|
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'Your profile has been updated'));
|
||||||
|
$this->trigger(UserEvent::EVENT_AFTER_PROFILE_UPDATE, $event);
|
||||||
|
|
||||||
|
return $this->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'profile',
|
||||||
|
[
|
||||||
|
'model' => $profile,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionAccount()
|
||||||
|
{
|
||||||
|
$form = $this->make(SettingsForm::class);
|
||||||
|
$event = $this->make(FormEvent::class, [$form]);
|
||||||
|
|
||||||
|
$this->make(AjaxRequestModelValidator::class, [$form])->validate();
|
||||||
|
|
||||||
|
if($form->load(Yii::$app->request->post())) {
|
||||||
|
$this->trigger(UserEvent::EVENT_BEFORE_ACCOUNT_UPDATE, $event);
|
||||||
|
if($form->save()) {
|
||||||
|
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'Your account details have been updated-'));
|
||||||
|
$this->trigger(UserEvent::EVENT_AFTER_ACCOUNT_UPDATE, $event);
|
||||||
|
|
||||||
|
return $this->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('account', [
|
||||||
|
'model' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionConfirm($id, $code) {
|
||||||
|
$user = $this->userQuery->whereId($id)->one();
|
||||||
|
|
||||||
|
if ($user === null || $this->module->emailChangeStrategy == MailChangeStrategyInterface::TYPE_INSECURE) {
|
||||||
|
throw new NotFoundHttpException();
|
||||||
|
}
|
||||||
|
$event = $this->make(UserEvent::class, [$user]);
|
||||||
|
|
||||||
|
$this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event);
|
||||||
|
if($this->make(EmailChangeService::class, [$code, $user])->run()) {
|
||||||
|
$this->trigger(UserEvent::EVENT_AFTER_CONFIRMATION, $event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirect(['account']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,8 @@ class UserEvent extends Event
|
|||||||
const EVENT_AFTER_CREATE = 'afterCreate';
|
const EVENT_AFTER_CREATE = 'afterCreate';
|
||||||
const EVENT_BEFORE_REGISTER = 'beforeRegister';
|
const EVENT_BEFORE_REGISTER = 'beforeRegister';
|
||||||
const EVENT_AFTER_REGISTER = 'afterRegister';
|
const EVENT_AFTER_REGISTER = 'afterRegister';
|
||||||
|
const EVENT_BEFORE_ACCOUNT_UPDATE = 'beforeAccountUpdate';
|
||||||
|
const EVENT_AFTER_ACCOUNT_UPDATE = 'afterAccountUpdate';
|
||||||
const EVENT_BEFORE_PROFILE_UPDATE = 'beforeProfileUpdate';
|
const EVENT_BEFORE_PROFILE_UPDATE = 'beforeProfileUpdate';
|
||||||
const EVENT_AFTER_PROFILE_UPDATE = 'afterProfileUpdate';
|
const EVENT_AFTER_PROFILE_UPDATE = 'afterProfileUpdate';
|
||||||
const EVENT_BEFORE_CONFIRMATION = 'beforeConfirmation';
|
const EVENT_BEFORE_CONFIRMATION = 'beforeConfirmation';
|
||||||
|
|||||||
67
lib/User/Factory/EmailChangeStrategyFactory.php
Normal file
67
lib/User/Factory/EmailChangeStrategyFactory.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace Da\User\Factory;
|
||||||
|
|
||||||
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Contracts\StrategyInterface;
|
||||||
|
use Da\User\Form\SettingsForm;
|
||||||
|
use Da\User\Strategy\DefaultEmailChangeStrategy;
|
||||||
|
use Da\User\Strategy\InsecureEmailChangeStrategy;
|
||||||
|
use Da\User\Strategy\SecureEmailChangeStrategy;
|
||||||
|
use Yii;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class EmailChangeStrategyFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
protected static $map = [
|
||||||
|
MailChangeStrategyInterface::TYPE_INSECURE => InsecureEmailChangeStrategy::class,
|
||||||
|
MailChangeStrategyInterface::TYPE_DEFAULT => DefaultEmailChangeStrategy::class,
|
||||||
|
MailChangeStrategyInterface::TYPE_SECURE => SecureEmailChangeStrategy::class
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $strategy
|
||||||
|
* @param SettingsForm $form
|
||||||
|
*
|
||||||
|
* @return MailChangeStrategyInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function makeByStrategyType($strategy, SettingsForm $form)
|
||||||
|
{
|
||||||
|
if (array_key_exists($strategy, static::$map)) {
|
||||||
|
return Yii::createObject(static::$map[$strategy], [$form]);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('Unknown strategy type');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsForm $form
|
||||||
|
*
|
||||||
|
* @return DefaultEmailChangeStrategy
|
||||||
|
*/
|
||||||
|
public static function makeDefaultEmailChangeStrategy(SettingsForm $form)
|
||||||
|
{
|
||||||
|
return Yii::createObject(static::$map[MailChangeStrategyInterface::TYPE_DEFAULT], [$form]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsForm $form
|
||||||
|
*
|
||||||
|
* @return InsecureEmailChangeStrategy
|
||||||
|
*/
|
||||||
|
public static function makeInsecureEmailChangeStrategy(SettingsForm $form)
|
||||||
|
{
|
||||||
|
return Yii::createObject(static::$map[MailChangeStrategyInterface::TYPE_INSECURE], [$form]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsForm $form
|
||||||
|
*
|
||||||
|
* @return SecureEmailChangeStrategy
|
||||||
|
*/
|
||||||
|
public static function makeSecureEmailChangeStrategy(SettingsForm $form)
|
||||||
|
{
|
||||||
|
return Yii::createObject(static::$map[MailChangeStrategyInterface::TYPE_SECURE], [$form]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,6 +52,12 @@ class MailFactory
|
|||||||
return static::makeMailerService($from, $to, $subject, 'recovery', $params);
|
return static::makeMailerService($from, $to, $subject, 'recovery', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param Token|null $token
|
||||||
|
*
|
||||||
|
* @return MailService
|
||||||
|
*/
|
||||||
public static function makeConfirmationMailerService(User $user, Token $token = null)
|
public static function makeConfirmationMailerService(User $user, Token $token = null)
|
||||||
{
|
{
|
||||||
/** @var Module $module */
|
/** @var Module $module */
|
||||||
@ -67,6 +73,29 @@ class MailFactory
|
|||||||
return static::makeMailerService($from, $to, $subject, 'recovery', $params);
|
return static::makeMailerService($from, $to, $subject, 'recovery', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param Token $token
|
||||||
|
*
|
||||||
|
* @return MailService
|
||||||
|
*/
|
||||||
|
public static function makeReconfirmationMailerService(User $user, Token $token) {
|
||||||
|
/** @var Module $module */
|
||||||
|
$module = Yii::$app->getModule('user');
|
||||||
|
$to = $token->type === Token::TYPE_CONFIRM_NEW_EMAIL
|
||||||
|
? $user->unconfirmed_email
|
||||||
|
: $user->email;
|
||||||
|
|
||||||
|
$from = $module->mailParams['fromEmail'];
|
||||||
|
$subject = $module->mailParams['reconfirmationMailSubject'];
|
||||||
|
$params = [
|
||||||
|
'user' => $token && $token->user ? $token->user : null,
|
||||||
|
'token' => $token
|
||||||
|
];
|
||||||
|
|
||||||
|
return static::makeMailerService($from, $to, $subject, 'recovery', $params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a MailerService
|
* Builds a MailerService
|
||||||
*
|
*
|
||||||
|
|||||||
@ -37,6 +37,20 @@ class TokenFactory
|
|||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $userId
|
||||||
|
*
|
||||||
|
* @return Token
|
||||||
|
*/
|
||||||
|
public static function makeConfirmOldMailToken($userId)
|
||||||
|
{
|
||||||
|
$token = self::make($userId, Token::TYPE_CONFIRM_OLD_EMAIL);
|
||||||
|
|
||||||
|
$token->save(false);
|
||||||
|
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $userId
|
* @param $userId
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,75 +1,53 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
namespace Da\User\Form;
|
||||||
* 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 dektrium\user\models;
|
|
||||||
|
|
||||||
use Da\User\Factory\TokenFactory;
|
use Da\User\Factory\TokenFactory;
|
||||||
use dektrium\user\helpers\Password;
|
use Da\User\Helper\SecurityHelper;
|
||||||
use dektrium\user\Mailer;
|
use Da\User\Model\User;
|
||||||
use dektrium\user\Module;
|
use Da\User\Traits\ContainerTrait;
|
||||||
use dektrium\user\traits\ModuleTrait;
|
use Da\User\Traits\ModuleTrait;
|
||||||
use Yii;
|
use Yii;
|
||||||
use yii\base\Model;
|
use yii\base\Model;
|
||||||
|
|
||||||
/**
|
|
||||||
* SettingsForm gets user's username, email and password and changes them.
|
|
||||||
*
|
|
||||||
* @property User $user
|
|
||||||
*
|
|
||||||
* @author Dmitry Erofeev <dmeroff@gmail.com>
|
|
||||||
*/
|
|
||||||
class SettingsForm extends Model
|
class SettingsForm extends Model
|
||||||
{
|
{
|
||||||
use ModuleTrait;
|
use ModuleTrait;
|
||||||
|
use ContainerTrait;
|
||||||
|
|
||||||
/** @var string */
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
public $email;
|
public $email;
|
||||||
|
/**
|
||||||
/** @var string */
|
* @var string
|
||||||
|
*/
|
||||||
public $username;
|
public $username;
|
||||||
|
/**
|
||||||
/** @var string */
|
* @var string
|
||||||
|
*/
|
||||||
public $new_password;
|
public $new_password;
|
||||||
|
/**
|
||||||
/** @var string */
|
* @var string
|
||||||
|
*/
|
||||||
public $current_password;
|
public $current_password;
|
||||||
|
/**
|
||||||
/** @var Mailer */
|
* @var SecurityHelper
|
||||||
protected $mailer;
|
*/
|
||||||
|
protected $securityHelper;
|
||||||
|
|
||||||
/** @var User */
|
/** @var User */
|
||||||
private $_user;
|
private $user;
|
||||||
|
|
||||||
/** @return User */
|
public function __construct(SecurityHelper $securityHelper, array $config)
|
||||||
public function getUser()
|
|
||||||
{
|
{
|
||||||
if ($this->_user == null) {
|
$this->securityHelper = $securityHelper;
|
||||||
$this->_user = Yii::$app->user->identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->_user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
public function __construct(Mailer $mailer, $config = [])
|
|
||||||
{
|
|
||||||
$this->mailer = $mailer;
|
|
||||||
$this->setAttributes([
|
|
||||||
'username' => $this->user->username,
|
|
||||||
'email' => $this->user->unconfirmed_email ?: $this->user->email,
|
|
||||||
], false);
|
|
||||||
parent::__construct($config);
|
parent::__construct($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -80,20 +58,30 @@ class SettingsForm extends Model
|
|||||||
'emailRequired' => ['email', 'required'],
|
'emailRequired' => ['email', 'required'],
|
||||||
'emailTrim' => ['email', 'filter', 'filter' => 'trim'],
|
'emailTrim' => ['email', 'filter', 'filter' => 'trim'],
|
||||||
'emailPattern' => ['email', 'email'],
|
'emailPattern' => ['email', 'email'],
|
||||||
'emailUsernameUnique' => [['email', 'username'], 'unique', 'when' => function ($model, $attribute) {
|
'emailUsernameUnique' => [
|
||||||
|
['email', 'username'],
|
||||||
|
'unique',
|
||||||
|
'when' => function ($model, $attribute) {
|
||||||
return $this->user->$attribute != $model->$attribute;
|
return $this->user->$attribute != $model->$attribute;
|
||||||
}, 'targetClass' => $this->module->modelMap['User']],
|
},
|
||||||
|
'targetClass' => $this->getClassMap()[User::class]
|
||||||
|
],
|
||||||
'newPasswordLength' => ['new_password', 'string', 'max' => 72, 'min' => 6],
|
'newPasswordLength' => ['new_password', 'string', 'max' => 72, 'min' => 6],
|
||||||
'currentPasswordRequired' => ['current_password', 'required'],
|
'currentPasswordRequired' => ['current_password', 'required'],
|
||||||
'currentPasswordValidate' => ['current_password', function ($attr) {
|
'currentPasswordValidate' => [
|
||||||
if (!Password::validate($this->$attr, $this->user->password_hash)) {
|
'current_password',
|
||||||
$this->addError($attr, Yii::t('user', 'Current password is not valid'));
|
function ($attribute) {
|
||||||
|
if (!$this->securityHelper->validatePassword($this->$attribute, $this->user->password_hash)) {
|
||||||
|
$this->addError($attribute, Yii::t('user', 'Current password is not valid'));
|
||||||
}
|
}
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
public function attributeLabels()
|
public function attributeLabels()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -104,10 +92,16 @@ class SettingsForm extends Model
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/**
|
||||||
public function formName()
|
* @return User|null|\yii\web\IdentityInterface
|
||||||
|
*/
|
||||||
|
public function getUser()
|
||||||
{
|
{
|
||||||
return 'settings-form';
|
if ($this->user == null) {
|
||||||
|
$this->user = Yii::$app->user->identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,11 +173,13 @@ class SettingsForm extends Model
|
|||||||
{
|
{
|
||||||
$this->defaultEmailChange();
|
$this->defaultEmailChange();
|
||||||
/** @var Token $token */
|
/** @var Token $token */
|
||||||
$token = Yii::createObject([
|
$token = Yii::createObject(
|
||||||
|
[
|
||||||
'class' => Token::className(),
|
'class' => Token::className(),
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'type' => Token::TYPE_CONFIRM_OLD_EMAIL,
|
'type' => Token::TYPE_CONFIRM_OLD_EMAIL,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
$token->save(false);
|
$token->save(false);
|
||||||
$this->mailer->sendReconfirmationMessage($this->user, $token);
|
$this->mailer->sendReconfirmationMessage($this->user, $token);
|
||||||
|
|
||||||
|
|||||||
@ -24,11 +24,13 @@ use yii\web\IdentityInterface;
|
|||||||
* @property integer $id
|
* @property integer $id
|
||||||
* @property string $username
|
* @property string $username
|
||||||
* @property string $email
|
* @property string $email
|
||||||
|
* @property string $unconfirmed_email
|
||||||
* @property string $password_hash
|
* @property string $password_hash
|
||||||
* @property string $auth_key
|
* @property string $auth_key
|
||||||
* @property integer $registration_ip
|
* @property integer $registration_ip
|
||||||
* @property integer $confirmed_at
|
* @property integer $confirmed_at
|
||||||
* @property integer $blocked_at
|
* @property integer $blocked_at
|
||||||
|
* @property integer $flags
|
||||||
* @property integer $created_at
|
* @property integer $created_at
|
||||||
* @property integer $updated_at
|
* @property integer $updated_at
|
||||||
*
|
*
|
||||||
@ -41,6 +43,10 @@ class User extends ActiveRecord implements IdentityInterface
|
|||||||
use ModuleTrait;
|
use ModuleTrait;
|
||||||
use ContainerTrait;
|
use ContainerTrait;
|
||||||
|
|
||||||
|
// following constants are used on secured email changing process
|
||||||
|
const OLD_EMAIL_CONFIRMED = 0b1;
|
||||||
|
const NEW_EMAIL_CONFIRMED = 0b10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string default user name regular expression.
|
* @var string default user name regular expression.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Da\User;
|
namespace Da\User;
|
||||||
|
|
||||||
use Da\User\Strategy\DefaultEmailChangeStrategy;
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
|
||||||
class Module extends \yii\base\Module
|
class Module extends \yii\base\Module
|
||||||
{
|
{
|
||||||
@ -29,7 +29,7 @@ class Module extends \yii\base\Module
|
|||||||
/**
|
/**
|
||||||
* @var string the class name of the strategy class to handle user's email change.
|
* @var string the class name of the strategy class to handle user's email change.
|
||||||
*/
|
*/
|
||||||
public $emailChangeStrategy = DefaultEmailChangeStrategy::class;
|
public $emailChangeStrategy = MailChangeStrategyInterface::TYPE_DEFAULT;
|
||||||
/**
|
/**
|
||||||
* @var int the time user will be auto logged in.
|
* @var int the time user will be auto logged in.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,13 +6,49 @@ use yii\db\ActiveQuery;
|
|||||||
|
|
||||||
class TokenQuery extends ActiveQuery
|
class TokenQuery extends ActiveQuery
|
||||||
{
|
{
|
||||||
public function whereIsRecoveryType($userId, $code)
|
/**
|
||||||
|
* @param $userId
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereUserId($userId)
|
||||||
{
|
{
|
||||||
return $this->andWhere(['user_id' => $userId, 'code' => $code, 'type' => Token::TYPE_RECOVERY]);
|
return $this->andWhere(['user_id' => $userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function whereIsConfirmationType($userId, $code)
|
/**
|
||||||
|
* @param $code
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereCode($code)
|
||||||
{
|
{
|
||||||
return $this->andWhere(['user_id' => $userId, 'code' => $code, 'type' => Token::TYPE_CONFIRM_NEW_EMAIL]);
|
return $this->andWhere(['code' => $code]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereIsRecoveryType()
|
||||||
|
{
|
||||||
|
return $this->andWhere(['type' => Token::TYPE_RECOVERY]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereIsConfirmationType()
|
||||||
|
{
|
||||||
|
return $this->andWhere(['type' => Token::TYPE_CONFIRM_NEW_EMAIL]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $types
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereIsTypes(array $types)
|
||||||
|
{
|
||||||
|
return $this->andWhere(['in', 'type', $types]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,11 @@ use yii\db\ActiveQuery;
|
|||||||
|
|
||||||
class UserQuery extends ActiveQuery
|
class UserQuery extends ActiveQuery
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param $usernameOrEmail
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function whereUsernameOrEmail($usernameOrEmail)
|
public function whereUsernameOrEmail($usernameOrEmail)
|
||||||
{
|
{
|
||||||
return filter_var($usernameOrEmail, FILTER_VALIDATE_EMAIL)
|
return filter_var($usernameOrEmail, FILTER_VALIDATE_EMAIL)
|
||||||
@ -13,18 +18,44 @@ class UserQuery extends ActiveQuery
|
|||||||
: $this->whereUsername($usernameOrEmail);
|
: $this->whereUsername($usernameOrEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $email
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function whereEmail($email)
|
public function whereEmail($email)
|
||||||
{
|
{
|
||||||
return $this->andWhere(['email' => $email]);
|
return $this->andWhere(['email' => $email]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $username
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function whereUsername($username)
|
public function whereUsername($username)
|
||||||
{
|
{
|
||||||
return $this->andWhere(['username' => $username]);
|
return $this->andWhere(['username' => $username]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $id
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function whereId($id)
|
public function whereId($id)
|
||||||
{
|
{
|
||||||
return $this->andWhere(['id' => $id]);
|
return $this->andWhere(['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $id
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function whereNotId($id)
|
||||||
|
{
|
||||||
|
return $this->andWhere(['<>', 'id', $id]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use Da\User\Model\Token;
|
|||||||
use Da\User\Model\User;
|
use Da\User\Model\User;
|
||||||
use Da\User\Query\TokenQuery;
|
use Da\User\Query\TokenQuery;
|
||||||
|
|
||||||
class EmailConfirmationService implements ServiceInterface
|
class AccountConfirmationService implements ServiceInterface
|
||||||
{
|
{
|
||||||
protected $model;
|
protected $model;
|
||||||
protected $code;
|
protected $code;
|
||||||
@ -29,7 +29,11 @@ class EmailConfirmationService implements ServiceInterface
|
|||||||
|
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
$token = $this->tokenQuery->whereIsConfirmationType($this->model->id, $this->code)->one();
|
$token = $this->tokenQuery
|
||||||
|
->whereUserId($this->model->id)
|
||||||
|
->whereCode($this->code)
|
||||||
|
->whereIsConfirmationType()
|
||||||
|
->one();
|
||||||
|
|
||||||
if ($token instanceof Token && !$token->getIsExpired()) {
|
if ($token instanceof Token && !$token->getIsExpired()) {
|
||||||
$token->delete();
|
$token->delete();
|
||||||
87
lib/User/Service/EmailChangeService.php
Normal file
87
lib/User/Service/EmailChangeService.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
namespace Da\User\Service;
|
||||||
|
|
||||||
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Contracts\ServiceInterface;
|
||||||
|
use Da\User\Model\Token;
|
||||||
|
use Da\User\Model\User;
|
||||||
|
use Da\User\Query\TokenQuery;
|
||||||
|
use Da\User\Query\UserQuery;
|
||||||
|
use Da\User\Traits\ModuleTrait;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
class EmailChangeService implements ServiceInterface
|
||||||
|
{
|
||||||
|
use ModuleTrait;
|
||||||
|
|
||||||
|
protected $code;
|
||||||
|
protected $model;
|
||||||
|
protected $tokenQuery;
|
||||||
|
protected $userQuery;
|
||||||
|
|
||||||
|
public function __construct($code, User $model, TokenQuery $tokenQuery, UserQuery $userQuery)
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
$this->model = $model;
|
||||||
|
$this->tokenQuery = $tokenQuery;
|
||||||
|
$this->userQuery = $userQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
/** @var Token $token */
|
||||||
|
$token = $this->tokenQuery
|
||||||
|
->whereUserId($this->model->id)
|
||||||
|
->whereCode($this->code)
|
||||||
|
->whereIsTypes([Token::TYPE_CONFIRM_NEW_EMAIL, Token::TYPE_CONFIRM_OLD_EMAIL])
|
||||||
|
->one();
|
||||||
|
|
||||||
|
if ($token === null || $token->getIsExpired()) {
|
||||||
|
Yii::$app->session->setFlash('danger', Yii::t('user', 'Your confirmation token is invalid or expired'));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$token->delete();
|
||||||
|
if (empty($this->model->unconfirmed_email)) {
|
||||||
|
Yii::$app->session->setFlash('danger', Yii::t('user', 'An error occurred processing your request'));
|
||||||
|
|
||||||
|
} elseif ($this->userQuery->whereEmail($this->model->unconfirmed_email)->exists() === false) {
|
||||||
|
|
||||||
|
if ($this->getModule()->emailChangeStrategy === MailChangeStrategyInterface::TYPE_SECURE) {
|
||||||
|
if ($token->type === Token::TYPE_CONFIRM_NEW_EMAIL) {
|
||||||
|
$this->model->flags |= User::NEW_EMAIL_CONFIRMED;
|
||||||
|
Yii::$app->session->setFlash(
|
||||||
|
'success',
|
||||||
|
Yii::t(
|
||||||
|
'user',
|
||||||
|
'Awesome, almost there. ' .
|
||||||
|
'Now you need to click the confirmation link sent to your old email address.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} elseif ($token->type === Token::TYPE_CONFIRM_OLD_EMAIL) {
|
||||||
|
$this->model->flags |= User::OLD_EMAIL_CONFIRMED;
|
||||||
|
Yii::$app->session->setFlash(
|
||||||
|
'success',
|
||||||
|
Yii::t(
|
||||||
|
'user',
|
||||||
|
'Awesome, almost there. ' .
|
||||||
|
'Now you need to click the confirmation link sent to your new email address.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->getModule()->emailChangeStrategy === MailChangeStrategyInterface::TYPE_DEFAULT
|
||||||
|
|| ($this->model->flags & User::NEW_EMAIL_CONFIRMED & $this->model->flags & User::OLD_EMAIL_CONFIRMED)
|
||||||
|
) {
|
||||||
|
$this->model->email = $this->model->unconfirmed_email;
|
||||||
|
$this->model->unconfirmed_email = null;
|
||||||
|
Yii::$app->session->setFlash('success', Yii::t('user', 'Your email address has been changed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->model->save(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,17 +1,43 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Da\User\Strategy;
|
namespace Da\User\Strategy;
|
||||||
|
|
||||||
use Da\User\Contracts\StrategyInterface;
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Factory\MailFactory;
|
||||||
|
use Da\User\Factory\TokenFactory;
|
||||||
|
use Da\User\Form\SettingsForm;
|
||||||
|
use Da\User\Traits\ContainerTrait;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
/**
|
class DefaultEmailChangeStrategy implements MailChangeStrategyInterface
|
||||||
*
|
|
||||||
* DefaultEmailChangeStrategy.php
|
|
||||||
*
|
|
||||||
* Date: 3/12/16
|
|
||||||
* Time: 16:22
|
|
||||||
* @author Antonio Ramirez <hola@2amigos.us>
|
|
||||||
*/
|
|
||||||
class DefaultEmailChangeStrategy implements StrategyInterface
|
|
||||||
{
|
{
|
||||||
|
use ContainerTrait;
|
||||||
|
|
||||||
|
protected $form;
|
||||||
|
|
||||||
|
public function __construct(SettingsForm $form)
|
||||||
|
{
|
||||||
|
$this->form = $form;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->form->getUser()->unconfirmed_email = $this->form->email;
|
||||||
|
|
||||||
|
$token = TokenFactory::makeConfirmNewMailToken($this->form->getUser()->id);
|
||||||
|
|
||||||
|
$mailService = MailFactory::makeReconfirmationMailerService($this->form->getUser(), $token);
|
||||||
|
|
||||||
|
if ($mailService->run()) {
|
||||||
|
Yii::$app
|
||||||
|
->session
|
||||||
|
->setFlash('info', Yii::t('user', 'A confirmation message has been sent to your new email address'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Da\User\Strategy;
|
namespace Da\User\Strategy;
|
||||||
|
|
||||||
use Da\User\Contracts\StrategyInterface;
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Form\SettingsForm;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
class InsecureEmailChangeStrategy implements MailChangeStrategyInterface
|
||||||
* InsecuredEmailChangeStrategy.php
|
|
||||||
*
|
|
||||||
* Date: 3/12/16
|
|
||||||
* Time: 16:21
|
|
||||||
* @author Antonio Ramirez <hola@2amigos.us>
|
|
||||||
*/
|
|
||||||
class InsecureEmailChangeStrategy implements StrategyInterface
|
|
||||||
{
|
{
|
||||||
|
protected $form;
|
||||||
|
|
||||||
|
public function __construct(SettingsForm $form)
|
||||||
|
{
|
||||||
|
$this->form = $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->form->getUser()->email = $this->form->email;
|
||||||
|
return $this->form->getUser()->save();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,55 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Da\User\Strategy;
|
namespace Da\User\Strategy;
|
||||||
|
|
||||||
use Da\User\Contracts\StrategyInterface;
|
use Da\User\Contracts\MailChangeStrategyInterface;
|
||||||
|
use Da\User\Factory\MailFactory;
|
||||||
|
use Da\User\Factory\TokenFactory;
|
||||||
|
use Da\User\Form\SettingsForm;
|
||||||
|
use Da\User\Model\User;
|
||||||
|
use Da\User\Traits\ContainerTrait;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
/**
|
class SecureEmailChangeStrategy implements MailChangeStrategyInterface
|
||||||
*
|
|
||||||
* SecuredEmailChangeStrategy.php
|
|
||||||
*
|
|
||||||
* Date: 3/12/16
|
|
||||||
* Time: 16:20
|
|
||||||
* @author Antonio Ramirez <hola@2amigos.us>
|
|
||||||
*/
|
|
||||||
class SecureEmailChangeStrategy implements StrategyInterface
|
|
||||||
{
|
{
|
||||||
|
use ContainerTrait;
|
||||||
|
|
||||||
|
protected $form;
|
||||||
|
|
||||||
|
public function __construct(SettingsForm $form)
|
||||||
|
{
|
||||||
|
$this->form = $form;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
if ($this->make(DefaultEmailChangeStrategy::class, [$this->form])->run()) {
|
||||||
|
|
||||||
|
$token = TokenFactory::makeConfirmOldMailToken($this->form->getUser()->id);
|
||||||
|
$mailService = MailFactory::makeReconfirmationMailerService($this->form->getUser(), $token);
|
||||||
|
|
||||||
|
if ($mailService->run()) {
|
||||||
|
// unset flags if they exist
|
||||||
|
$this->form->getUser()->flags &= ~User::NEW_EMAIL_CONFIRMED;
|
||||||
|
$this->form->getUser()->flags &= ~User::OLD_EMAIL_CONFIRMED;
|
||||||
|
if ($this->form->getUser()->save(false)) {
|
||||||
|
Yii::$app
|
||||||
|
->session
|
||||||
|
->setFlash(
|
||||||
|
'info',
|
||||||
|
Yii::t(
|
||||||
|
'user',
|
||||||
|
'We have sent confirmation links to both old and new email addresses. ' .
|
||||||
|
'You must click both links to complete your request.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user