diff --git a/lib/User/Bootstrap.php b/lib/User/Bootstrap.php index 48a710e..ac75791 100644 --- a/lib/User/Bootstrap.php +++ b/lib/User/Bootstrap.php @@ -3,7 +3,6 @@ namespace Da\User; use Da\User\Helper\ClassMapHelper; -use Da\User\Model\Profile; use Da\User\Validator\TimeZoneValidator; use Yii; use yii\authclient\Collection; @@ -22,8 +21,8 @@ class Bootstrap implements BootstrapInterface public function bootstrap($app) { if ($app->hasModule('user') && $app->getModule('user') instanceof Module) { - $classMap = $this->buildClassMap(); - $this->initContainer($classMap); + $map = $this->buildClassMap($app->getModule('user')->classMap); + $this->initContainer($map); $this->initTranslations($app); $this->initMailServiceConfiguration($app, $app->getModule('user')); @@ -184,22 +183,25 @@ class Bootstrap implements BootstrapInterface } /** - * Builds class map according to use configuration + * Builds class map according to user configuration + * + * @param array $userClassMap user configuration on the module * * @return array */ - protected function buildClassMap() + protected function buildClassMap(array $userClassMap) { $map = []; $defaults = [ + // --- models 'User' => 'Da\User\Model\User', 'Account' => 'Da\User\Model\Account', 'Profile' => 'Da\User\Model\Profile', 'Token' => 'Da\User\Model\Token', - // --- + // --- search 'UserSearch' => 'Da\User\Search\UserSearch', - // --- + // --- forms 'RegistrationForm' => 'Da\User\Form\RegistrationForm', 'ResendForm' => 'Da\User\Form\ResendForm', 'LoginForm' => 'Da\User\Form\LoginForm', @@ -226,7 +228,9 @@ class Bootstrap implements BootstrapInterface ] ]; - foreach ($defaults as $name => $definition) { + $mapping = array_merge($defaults, $userClassMap); + + foreach ($mapping as $name => $definition) { $map[$this->getRoute($routes, $name) . "\\$name"] = $definition; } diff --git a/lib/User/Controller/AdminController.php b/lib/User/Controller/AdminController.php index 2454f70..9e5cb75 100644 --- a/lib/User/Controller/AdminController.php +++ b/lib/User/Controller/AdminController.php @@ -54,7 +54,7 @@ class AdminController extends Controller */ public function beforeAction($action) { - if (in_array($action->id, ['index', 'update', 'update-profile', 'info', 'assingments'])) { + if (in_array($action->id, ['index', 'update', 'update-profile', 'info', 'assignments'])) { Url::remember('', 'actions-redirect'); } @@ -120,11 +120,13 @@ class AdminController extends Controller $mailService = MailFactory::makeWelcomeMailerService($user); - $this->make(UserCreateService::class, [$user, $mailService])->run(); + if ($this->make(UserCreateService::class, [$user, $mailService])->run()) { - $this->trigger(UserEvent::EVENT_AFTER_CREATE, $event); + Yii::$app->getSession()->setFlash('success', Yii::t('user', 'User has been created')); + $this->trigger(UserEvent::EVENT_AFTER_CREATE, $event); - return $this->redirect(['update', 'id' => $user->id]); + return $this->redirect(['update', 'id' => $user->id]); + } } return $this->render('create', ['user' => $user]); diff --git a/lib/User/Controller/RecoveryController.php b/lib/User/Controller/RecoveryController.php new file mode 100644 index 0000000..203932b --- /dev/null +++ b/lib/User/Controller/RecoveryController.php @@ -0,0 +1,96 @@ +userQuery = $userQuery; + $this->tokenQuery = $tokenQuery; + parent::__construct($id, $module, $config); + } + + /** + * @inheritdoc + */ + public function behaviors() + { + return [ + 'access' => [ + 'class' => AccessControl::className(), + 'rules' => [ + [ + 'allow' => true, + 'actions' => ['request', 'reset'], + 'roles' => ['?'] + ], + ], + ], + ]; + } + + public function actionRequest() + { + if (!$this->getModule()->allowPasswordRecovery) { + throw new NotFoundHttpException(); + } + + /** @var RecoveryForm $form */ + $form = $this->make(RecoveryForm::class, ['scenario' => RecoveryForm::SCENARIO_REQUEST]); + + $event = $this->make(FormEvent::class, [$form]); + + $this->make(AjaxRequestModelValidator::class, $form)->validate(); + + $this->trigger(FormEvent::EVENT_BEFORE_REQUEST, $event); + + if ($form->load(Yii::$app->request->post())) { + $mailService = MailFactory::makeRecoveryMailerService($form->email); + + if ($this->make(PasswordRecoveryService::class, [$form->email, $mailService])->run()) { + $this->trigger(FormEvent::EVENT_AFTER_REQUEST, $event); + + return $this->render( + 'message', + [ + 'title' => Yii::t('user', 'Recovery message sent'), + 'module' => $this->getModule(), + ] + ); + } + } + + return $this->render('request', ['model' => $form,]); + } +} diff --git a/lib/User/Event/FormEvent.php b/lib/User/Event/FormEvent.php index 5b30f8b..1750572 100644 --- a/lib/User/Event/FormEvent.php +++ b/lib/User/Event/FormEvent.php @@ -4,16 +4,11 @@ namespace Da\User\Event; use yii\base\Event; use yii\base\Model; -/** - * - * FormEvent.php - * - * Date: 4/12/16 - * Time: 15:11 - * @author Antonio Ramirez - */ class FormEvent extends Event { + const EVENT_BEFORE_REQUEST = 'beforeRequest'; + const EVENT_AFTER_REQUEST = 'afterRequest'; + protected $form; public function __construct(Model $form, array $config = []) diff --git a/lib/User/Event/ResetPasswordEvent.php b/lib/User/Event/ResetPasswordEvent.php index 1505216..f81cab1 100644 --- a/lib/User/Event/ResetPasswordEvent.php +++ b/lib/User/Event/ResetPasswordEvent.php @@ -8,6 +8,11 @@ use yii\base\Event; class ResetPasswordEvent extends Event { + const EVENT_BEFORE_TOKEN_VALIDATE = 'beforeTokenValidate'; + const EVENT_AFTER_TOKEN_VALIDATE = 'afterTokenValidate'; + const EVENT_BEFORE_RESET = 'beforeReset'; + const EVENT_AFTER_RESET = 'afterReset'; + protected $form; protected $token; diff --git a/lib/User/Factory/MailFactory.php b/lib/User/Factory/MailFactory.php index 4323438..1ebd67a 100644 --- a/lib/User/Factory/MailFactory.php +++ b/lib/User/Factory/MailFactory.php @@ -1,14 +1,19 @@ getModule('user'); + $to = $email; + $from = $module->mailParams['fromEmail']; + $subject = $module->mailParams['recoveryMailSubject']; + + return static::makeMailerService($from, $to, $subject, 'recovery'); + } /** * Builds a MailerService * diff --git a/lib/User/Module.php b/lib/User/Module.php index 4e2abca..a07788a 100644 --- a/lib/User/Module.php +++ b/lib/User/Module.php @@ -67,6 +67,10 @@ class Module extends \yii\base\Module * compute the hash doubles for every increment by one of $cost. */ public $blowfishCost = 10; + /** + * @var array the class map. How the container should load specific classes. + */ + public $classMap = []; /** * @var array the url rules (routes) diff --git a/lib/User/Service/MailService.php b/lib/User/Service/MailService.php index d5187dd..bcd84a2 100644 --- a/lib/User/Service/MailService.php +++ b/lib/User/Service/MailService.php @@ -38,6 +38,19 @@ class MailService implements ServiceInterface $this->mailer->getView()->theme = Yii::$app->view->theme; } + /** + * @param $name + * @param $value + * + * @return $this + */ + public function setViewParam($name, $value) + { + $this->params[$name] = $value; + + return $this; + } + /** * @return bool */ diff --git a/lib/User/Service/PasswordRecoveryService.php b/lib/User/Service/PasswordRecoveryService.php index eed9202..06297be 100644 --- a/lib/User/Service/PasswordRecoveryService.php +++ b/lib/User/Service/PasswordRecoveryService.php @@ -1,15 +1,63 @@ - */ -class PasswordRecoveryService +use Da\User\Contracts\ServiceInterface; +use Da\User\Model\Token; +use Da\User\Model\User; +use Da\User\Query\UserQuery; +use Exception; +use Yii; +use yii\log\Logger; + +class PasswordRecoveryService implements ServiceInterface { + protected $query; + + protected $email; + protected $mailService; + protected $securityHelper; + protected $logger; + + public function __construct($email, MailService $mailService, UserQuery $query, Logger $logger) + { + $this->email = $email; + $this->mailService = $mailService; + $this->query = $query; + $this->logger = $logger; + } + + public function run() + { + try { + /** @var User $user */ + $user = $this->query->whereEmail($this->email)->one(); + /** @var Token $token */ + $token = Yii::createObject( + [ + 'class' => Token::class, + 'user_id' => $user->id, + 'type' => Token::TYPE_RECOVERY + ] + ); + + if (!$token->save(false)) { + return false; + } + + $this->mailService->setViewParam('user', $user); + $this->mailService->setViewParam('token', $token); + + if (!$this->mailService->run()) { + return false; + } + + return true; + + } catch (Exception $e) { + $this->logger->log($e->getMessage(), Logger::LEVEL_ERROR); + + return false; + } + } } diff --git a/lib/User/Service/ResetPasswordService.php b/lib/User/Service/ResetPasswordService.php index 99bd1b8..3c3b28d 100644 --- a/lib/User/Service/ResetPasswordService.php +++ b/lib/User/Service/ResetPasswordService.php @@ -1,15 +1,31 @@ - */ -class ResetPasswordService + +use Da\User\Contracts\ServiceInterface; +use Da\User\Helper\SecurityHelper; +use Da\User\Model\User; + +class ResetPasswordService implements ServiceInterface { + protected $password; + protected $model; + protected $securityHelper; + + public function __construct($password, User $model, SecurityHelper $securityHelper) + { + $this->password; + $this->model = $model; + $this->securityHelper = $securityHelper; + } + + public function run() + { + return (bool)$this->model->updateAttributes( + [ + 'password_hash' => $this->securityHelper->generatePasswordHash($this->password) + ] + ); + } } diff --git a/lib/User/Service/UserCreateService.php b/lib/User/Service/UserCreateService.php index fa2569a..5d42a0c 100644 --- a/lib/User/Service/UserCreateService.php +++ b/lib/User/Service/UserCreateService.php @@ -60,7 +60,7 @@ class UserCreateService implements ServiceInterface } catch (Exception $e) { $transaction->rollBack(); - $this->logger->log($e->getMessage(), Logger::LEVEL_WARNING); + $this->logger->log($e->getMessage(), Logger::LEVEL_ERROR); return false; }