diff --git a/app/modules/web/Controllers/UserPassReset/IndexController.php b/app/modules/web/Controllers/UserPassReset/IndexController.php new file mode 100644 index 00000000..416ec7e9 --- /dev/null +++ b/app/modules/web/Controllers/UserPassReset/IndexController.php @@ -0,0 +1,47 @@ +. + */ + +namespace SP\Modules\Web\Controllers\UserPassReset; + +use SP\Modules\Web\Controllers\ControllerBase; +use SP\Util\ErrorUtil; + +/** + * Class IndexController + * + * @package SP\Modules\Web\Controllers + */ +final class IndexController extends ControllerBase +{ + public function indexAction(): void + { + $this->layoutHelper->getCustomLayout('request', strtolower($this->getViewBaseName())); + + if (!$this->configData->isMailEnabled()) { + ErrorUtil::showErrorInView($this->view, self::ERR_UNAVAILABLE, true, 'request'); + } + + $this->view(); + } +} \ No newline at end of file diff --git a/app/modules/web/Controllers/UserPassReset/ResetController.php b/app/modules/web/Controllers/UserPassReset/ResetController.php new file mode 100644 index 00000000..9596e799 --- /dev/null +++ b/app/modules/web/Controllers/UserPassReset/ResetController.php @@ -0,0 +1,52 @@ +. + */ + +namespace SP\Modules\Web\Controllers\UserPassReset; + + +use SP\Modules\Web\Controllers\ControllerBase; +use SP\Util\ErrorUtil; + +/** + * Class ResetController + */ +final class ResetController extends ControllerBase +{ + /** + * @param string|null $hash + * + */ + public function resetAction(?string $hash = null): void + { + $this->layoutHelper->getCustomLayout('reset', strtolower($this->getViewBaseName())); + + if ($hash !== null && $this->configData->isMailEnabled()) { + $this->view->assign('hash', $hash); + } else { + ErrorUtil::showErrorInView($this->view, self::ERR_UNAVAILABLE, true, 'reset'); + } + + $this->view(); + } +} \ No newline at end of file diff --git a/app/modules/web/Controllers/UserPassReset/SaveRequestController.php b/app/modules/web/Controllers/UserPassReset/SaveRequestController.php new file mode 100644 index 00000000..6dabd82e --- /dev/null +++ b/app/modules/web/Controllers/UserPassReset/SaveRequestController.php @@ -0,0 +1,102 @@ +. + */ + +namespace SP\Modules\Web\Controllers\UserPassReset; + + +use Exception; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; +use SP\Core\Exceptions\SPException; +use SP\Domain\User\Services\UserPassRecoverService; +use SP\Http\JsonResponse; +use SP\Modules\Web\Controllers\Traits\JsonTrait; + +/** + * Class SaveRequestController + */ +final class SaveRequestController extends UserPassResetSaveBase +{ + use JsonTrait; + + /** + * @return bool + * @throws \JsonException + */ + public function saveRequestAction(): bool + { + try { + $this->checkTracking(); + + $login = $this->request->analyzeString('login'); + $email = $this->request->analyzeEmail('email'); + + $userData = $this->userService->getByLogin($login); + + if ($userData->getEmail() !== $email) { + throw new SPException(__u('Wrong data'), SPException::WARNING); + } + + if ($userData->isDisabled() || $userData->isLdap()) { + throw new SPException( + __u('Unable to reset the password'), + SPException::WARNING, + __u('Please contact to the administrator') + ); + } + + $hash = $this->userPassRecoverService->requestForUserId($userData->getId()); + + $this->eventDispatcher->notifyEvent( + 'request.user.passReset', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('Password Recovery')) + ->addDetail(__u('Requested for'), sprintf('%s (%s)', $login, $email)) + ) + ); + + $this->mailService->send( + __('Password Change'), + $email, + UserPassRecoverService::getMailMessage($hash) + ); + + return $this->returnJsonResponse( + JsonResponse::JSON_SUCCESS, + __u('Request sent'), + [__u('You will receive an email to complete the request shortly.')] + ); + } catch (Exception $e) { + processException($e); + + $this->addTracking(); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + return $this->returnJsonResponseException($e); + } + } +} \ No newline at end of file diff --git a/app/modules/web/Controllers/UserPassReset/SaveResetController.php b/app/modules/web/Controllers/UserPassReset/SaveResetController.php new file mode 100644 index 00000000..817a6956 --- /dev/null +++ b/app/modules/web/Controllers/UserPassReset/SaveResetController.php @@ -0,0 +1,95 @@ +. + */ + +namespace SP\Modules\Web\Controllers\UserPassReset; + + +use Exception; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; +use SP\Core\Exceptions\ValidationException; +use SP\Http\JsonResponse; +use SP\Modules\Web\Controllers\Traits\JsonTrait; + +/** + * Class SaveResetController + */ +final class SaveResetController extends UserPassResetSaveBase +{ + use JsonTrait; + + /** + * @return bool + * @throws \JsonException + */ + public function saveResetAction(): bool + { + try { + $this->checkTracking(); + + $pass = $this->request->analyzeEncrypted('password'); + $passR = $this->request->analyzeEncrypted('password_repeat'); + + if (!$pass || !$passR) { + throw new ValidationException(__u('Password cannot be blank')); + } + + if ($pass !== $passR) { + throw new ValidationException(__u('Passwords do not match')); + } + + $hash = $this->request->analyzeString('hash'); + + $userId = $this->userPassRecoverService->getUserIdForHash($hash); + + $this->userPassRecoverService->toggleUsedByHash($hash); + + $this->userService->updatePass($userId, $pass); + + $user = $this->userService->getById($userId); + + $this->eventDispatcher->notifyEvent( + 'edit.user.password', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('Password updated')) + ->addDetail(__u('User'), $user->getLogin()) + ->addExtra('userId', $userId) + ->addExtra('email', $user->getEmail()) + ) + ); + + return $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Password updated')); + } catch (Exception $e) { + processException($e); + + $this->addTracking(); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + return $this->returnJsonResponseException($e); + } + } +} \ No newline at end of file diff --git a/app/modules/web/Controllers/UserPassReset/UserPassResetSaveBase.php b/app/modules/web/Controllers/UserPassReset/UserPassResetSaveBase.php new file mode 100644 index 00000000..1a016d2e --- /dev/null +++ b/app/modules/web/Controllers/UserPassReset/UserPassResetSaveBase.php @@ -0,0 +1,95 @@ +. + */ + +namespace SP\Modules\Web\Controllers\UserPassReset; + + +use Exception; +use SP\Core\Application; +use SP\Core\Exceptions\SPException; +use SP\Domain\Notification\MailServiceInterface; +use SP\Domain\Security\TrackServiceInterface; +use SP\Domain\User\UserPassRecoverServiceInterface; +use SP\Domain\User\UserServiceInterface; +use SP\Infrastructure\Security\Repositories\TrackRequest; +use SP\Modules\Web\Controllers\ControllerBase; +use SP\Mvc\Controller\WebControllerHelper; + +/** + * Class UserPassResetSaveBase + */ +abstract class UserPassResetSaveBase extends ControllerBase +{ + protected UserPassRecoverServiceInterface $userPassRecoverService; + protected UserServiceInterface $userService; + protected MailServiceInterface $mailService; + private TrackServiceInterface $trackService; + private TrackRequest $trackRequest; + + /** + * @throws \SP\Core\Exceptions\SessionTimeout + * @throws \SP\Core\Exceptions\InvalidArgumentException + * @throws \JsonException + */ + public function __construct( + Application $application, + WebControllerHelper $webControllerHelper, + UserPassRecoverServiceInterface $userPassRecoverService, + UserServiceInterface $userService, + MailServiceInterface $mailService, + TrackServiceInterface $trackService + + ) { + parent::__construct($application, $webControllerHelper); + $this->userPassRecoverService = $userPassRecoverService; + $this->userService = $userService; + $this->mailService = $mailService; + $this->trackService = $trackService; + + $this->trackRequest = $this->trackService->getTrackRequest($this->getViewBaseName()); + } + + /** + * @throws SPException + * @throws Exception + */ + final protected function checkTracking(): void + { + if ($this->trackService->checkTracking($this->trackRequest)) { + throw new SPException(__u('Attempts exceeded'), SPException::INFO); + } + } + + /** + * Añadir un seguimiento + */ + final protected function addTracking(): void + { + try { + $this->trackService->add($this->trackRequest); + } catch (Exception $e) { + processException($e); + } + } +} \ No newline at end of file diff --git a/app/modules/web/Controllers/UserPassResetController.php b/app/modules/web/Controllers/UserPassResetController.php deleted file mode 100644 index 737118ab..00000000 --- a/app/modules/web/Controllers/UserPassResetController.php +++ /dev/null @@ -1,273 +0,0 @@ -. - */ - -namespace SP\Modules\Web\Controllers; - -use DI\DependencyException; -use DI\NotFoundException; -use Exception; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use SP\Core\Events\Event; -use SP\Core\Events\EventMessage; -use SP\Core\Exceptions\InvalidArgumentException; -use SP\Core\Exceptions\SPException; -use SP\Core\Exceptions\ValidationException; -use SP\Domain\Notification\Services\MailService; -use SP\Domain\Security\Services\TrackService; -use SP\Domain\User\Services\UserPassRecoverService; -use SP\Domain\User\Services\UserService; -use SP\Http\JsonResponse; -use SP\Infrastructure\Security\Repositories\TrackRequest; -use SP\Modules\Web\Controllers\Helpers\LayoutHelper; -use SP\Modules\Web\Controllers\Traits\JsonTrait; -use SP\Util\ErrorUtil; - -/** - * Class PassresetController - * - * @package SP\Modules\Web\Controllers - */ -final class UserPassResetController extends ControllerBase -{ - use JsonTrait; - - protected ?TrackService $trackService = null; - protected ?TrackRequest $trackRequest = null; - - /** - * Password reset action - * - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - public function indexAction(): void - { - $this->dic->get(LayoutHelper::class) - ->getCustomLayout('request', strtolower($this->getViewBaseName())); - - if (!$this->configData->isMailEnabled()) { - ErrorUtil::showErrorInView( - $this->view, - self::ERR_UNAVAILABLE, - true, - 'request' - ); - } - - $this->view(); - } - - /** - * @return bool - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \JsonException - */ - public function saveRequestAction(): bool - { - try { - $this->checkTracking(); - - $login = $this->request->analyzeString('login'); - $email = $this->request->analyzeEmail('email'); - - $userData = $this->dic->get(UserService::class)->getByLogin($login); - - if ($userData->getEmail() !== $email) { - throw new SPException(__u('Wrong data'), SPException::WARNING); - } - - if ($userData->isDisabled() || $userData->isLdap()) { - throw new SPException( - __u('Unable to reset the password'), - SPException::WARNING, - __u('Please contact to the administrator') - ); - } - - $hash = $this->dic->get(UserPassRecoverService::class) - ->requestForUserId($userData->getId()); - - $this->eventDispatcher->notifyEvent( - 'request.user.passReset', - new Event( - $this, - EventMessage::factory() - ->addDescription(__u('Password Recovery')) - ->addDetail(__u('Requested for'), sprintf('%s (%s)', $login, $email)) - ) - ); - - $this->dic->get(MailService::class) - ->send( - __('Password Change'), - $email, - UserPassRecoverService::getMailMessage($hash) - ); - - return $this->returnJsonResponse( - JsonResponse::JSON_SUCCESS, - __u('Request sent'), - [__u('You will receive an email to complete the request shortly.')] - ); - } catch (Exception $e) { - processException($e); - - $this->addTracking(); - - $this->eventDispatcher->notifyEvent( - 'exception', - new Event($e) - ); - - return $this->returnJsonResponseException($e); - } - } - - /** - * @throws SPException - * @throws Exception - */ - protected function checkTracking(): void - { - if ($this->trackService->checkTracking($this->trackRequest)) { - throw new SPException( - __u('Attempts exceeded'), - SPException::INFO - ); - } - } - - /** - * Añadir un seguimiento - */ - private function addTracking(): void - { - try { - $this->trackService->add($this->trackRequest); - } catch (Exception $e) { - processException($e); - } - } - - /** - * @param string|null $hash - * - * @throws DependencyException - * @throws NotFoundException - */ - public function resetAction(?string $hash = null): void - { - $this->dic->get(LayoutHelper::class) - ->getCustomLayout('reset', strtolower($this->getViewBaseName())); - - if ($hash && $this->configData->isMailEnabled()) { - $this->view->assign('hash', $hash); - } else { - ErrorUtil::showErrorInView( - $this->view, - self::ERR_UNAVAILABLE, - true, - 'reset' - ); - } - - $this->view(); - } - - /** - * @return bool - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \JsonException - */ - public function saveResetAction(): bool - { - try { - $this->checkTracking(); - - $pass = $this->request->analyzeEncrypted('password'); - $passR = $this->request->analyzeEncrypted('password_repeat'); - - if (!$pass || !$passR) { - throw new ValidationException(__u('Password cannot be blank')); - } - - if ($pass !== $passR) { - throw new ValidationException(__u('Passwords do not match')); - } - - $hash = $this->request->analyzeString('hash'); - - $userPassRecoverService = $this->dic->get(UserPassRecoverService::class); - $userId = $userPassRecoverService->getUserIdForHash($hash); - $userPassRecoverService->toggleUsedByHash($hash); - - $userService = $this->dic->get(UserService::class); - $userService->updatePass($userId, $pass); - - $user = $userService->getById($userId); - - $this->eventDispatcher->notifyEvent( - 'edit.user.password', - new Event( - $this, - EventMessage::factory() - ->addDescription(__u('Password updated')) - ->addDetail(__u('User'), $user->getLogin()) - ->addExtra('userId', $userId) - ->addExtra('email', $user->getEmail()) - ) - ); - - return $this->returnJsonResponse( - JsonResponse::JSON_SUCCESS, - __u('Password updated') - ); - } catch (Exception $e) { - processException($e); - - $this->addTracking(); - - $this->eventDispatcher->notifyEvent( - 'exception', - new Event($e) - ); - - return $this->returnJsonResponseException($e); - } - } - - /** - * @throws DependencyException - * @throws NotFoundException - * @throws InvalidArgumentException - */ - protected function initialize(): void - { - $this->trackService = $this->dic->get(TrackService::class); - $this->trackRequest = $this->trackService->getTrackRequest($this->getViewBaseName()); - } -} \ No newline at end of file diff --git a/lib/SP/Domain/User/Services/UserService.php b/lib/SP/Domain/User/Services/UserService.php index 825ecbef..4b8d835f 100644 --- a/lib/SP/Domain/User/Services/UserService.php +++ b/lib/SP/Domain/User/Services/UserService.php @@ -157,7 +157,7 @@ final class UserService extends Service implements UserServiceInterface * @throws \SP\Core\Exceptions\QueryException * @throws \SP\Infrastructure\Common\Repositories\NoSuchItemException */ - public function getByLogin(string $login): ?UserData + public function getByLogin(string $login): UserData { $result = $this->userRepository->getByLogin($login); diff --git a/lib/SP/Domain/User/UserServiceInterface.php b/lib/SP/Domain/User/UserServiceInterface.php index d4ff6470..2839bac8 100644 --- a/lib/SP/Domain/User/UserServiceInterface.php +++ b/lib/SP/Domain/User/UserServiceInterface.php @@ -74,7 +74,7 @@ interface UserServiceInterface * @throws \SP\Core\Exceptions\QueryException * @throws \SP\Infrastructure\Common\Repositories\NoSuchItemException */ - public function getByLogin(string $login): ?UserData; + public function getByLogin(string $login): UserData; /** * Deletes an item