Skip to content

Instantly share code, notes, and snippets.

@denistouch
Last active December 30, 2022 06:06
Show Gist options
  • Save denistouch/88a338bac344a6d100c5acd7dc140cec to your computer and use it in GitHub Desktop.
Save denistouch/88a338bac344a6d100c5acd7dc140cec to your computer and use it in GitHub Desktop.
Восстановление пароля через почту
<?php
namespace App\Subscriber;
use App\Event\PasswordRestoreCreateEvent;
use App\Service\MailSendService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PasswordChangeSubscriber implements EventSubscriberInterface
{
public function __construct(
private MailSendService $sendService,
private string $passwordUrlTemplate,
private string $codePasswordExpiredTtl
) {
}
public static function getSubscribedEvents()
{
return [
PasswordRestoreCreateEvent::NAME => 'onRestorePassword'
];
}
public function onRestorePassword(PasswordRestoreCreateEvent $event)
{
$updatedPassword = $event->getUpdatedPassword();
$emailTo = $updatedPassword->getUser()->getEmail();
$link = $this->createRestoreLink($updatedPassword->getCode());
$expiredAt = $this->codePasswordExpiredTtl;
$parameters = [
'updatedPassword' => $updatedPassword,
'confirm_link' => $link,
'expiration_time' => $expiredAt,
];
$this->sendService->sendMail(
$emailTo,
'password/restore.html.twig',
$parameters
);
}
private function createRestoreLink(string $code): string
{
return str_replace('%CODE%', $code, $this->passwordUrlTemplate);
}
}
<?php
namespace App\Event;
use App\Entity\UpdatedPassword;
use Symfony\Contracts\EventDispatcher\Event;
class PasswordRestoreCreateEvent extends Event
{
public const NAME = 'password.update.request.create';
public function __construct(private UpdatedPassword $updatedPassword)
{
}
public function getUpdatedPassword(): UpdatedPassword
{
return $this->updatedPassword;
}
}
<?php
namespace App\Service;
use App\Entity\PasswordRestore;
use App\Entity\User;
use App\Event\PasswordRestoreCreateEvent;
use App\Exception\UserNotFoundException;
use App\Model\CodePasswordConfirmRequest;
use App\Model\PasswordRestoreRequest;
use App\Repository\PasswordRestoreRepository;
use App\Util\Value;
use DateTime;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Translation\TranslatableMessage;
class PasswordRestoreService
{
public function __construct
(
private readonly CodeService $codeService,
private readonly UserService $userService,
private readonly PasswordRestoreRepository $passwordRepository,
private readonly EventDispatcherInterface $dispatcher,
private readonly string $codePasswordExpiredTtl
) {
}
public function createPasswordRestoreRequest(PasswordRestoreRequest $request): void
{
$user = $this->userService->getUserByEmail($request->getEmail());
if (!$user) {
throw new UserNotFoundException($request->getEmail());
}
$passwordRestore = $this->createPasswordRestore($user);
$event = new PasswordRestoreCreateEvent($passwordRestore);
$this->dispatcher->dispatch($event, PasswordRestoreCreateEvent::NAME);
}
public function confirmCode(CodePasswordConfirmRequest $request): void
{
$passwordRestore = $this->passwordRepository->findOneBy(['code' => $request->getCode()]);
if (!$passwordRestore) {
throw new NotFoundHttpException(new TranslatableMessage('code.found.error'));
}
$expiredDate = Value::getDateTimeSubSeconds(new DateTime(), $this->codePasswordExpiredTtl);
if ($passwordRestore->getCreatedAt() < $expiredDate) {
throw new AccessDeniedException(new TranslatableMessage('code.expired'));
}
$user = $passwordRestore->getUser();
$this->userService->passwordRestore($user, $request->getPassword());
$this->passwordRepository->remove($passwordRestore);
}
private function createPasswordRestore(User $user): PasswordRestore
{
$passwordRestore = new PasswordRestore();
$passwordRestore->setUser($user);
$passwordRestore->setCode($this->codeService->generate());
$this->passwordRepository->add($passwordRestore);
return $passwordRestore;
}
}
<?php
namespace App\Service;
use App\Entity\User;
use App\Model\PasswordChangeRequest;
use App\Model\UserListItem;
use App\Model\UserListResponse;
use App\Repository\UserRepository;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Translation\TranslatableMessage;
class UserService
{
public function __construct(
private readonly UserRepository $userRepository,
private readonly UserPasswordHasherInterface $hasher
) {
}
public function getUsers(): UserListResponse
{
$items = collect($this->userRepository->findAll())->map(
fn(User $user) => new UserListItem($user->getId(), $user->getEmail(), $user->getCode())
)->all();
return new UserListResponse($items);
}
public function updateEmail(User $user, $newEmail): void
{
$this->userRepository->updateEmail($user, $newEmail);
$this->addToUserRoleUSER($user);
}
public function passwordRestore(User $user, string $password): void
{
$hashedPassword = $this->hasher->hashPassword($user, $password);
$this->userRepository->upgradePassword($user, $hashedPassword);
$this->addToUserRoleUSER($user);
}
public function create(User $user): void
{
$user->setRoles([User::ROLE_GUEST]);
$this->userRepository->add($user);
}
public function existsByEmail(string $email): bool
{
return $this->userRepository->existsByEmail($email);
}
public function getUserByEmail(string $email): ?User
{
return $this->userRepository->loadUserByIdentifier($email);
}
public function isUserHasOnlyRole(User $user, string $role): bool
{
return (in_array($role, $user->getRoles()) && count($user->getRoles()) === 1);
}
public function changePassword(PasswordChangeRequest $request, User $user): void
{
$old = $request->getOldPassword();
if (!$this->hasher->isPasswordValid($user, $old)) {
throw new AccessDeniedException(new TranslatableMessage('password.correct.error'));
}
$newHashedPassword = $this->hasher->hashPassword($user, $request->getNewPassword());
$this->userRepository->upgradePassword($user, $newHashedPassword);
}
private function addToUserRoleUSER(User $user): void
{
$roles = $user->getRoles();
if (!in_array(User::ROLE_USER, $roles)) {
$roles[] = User::ROLE_USER;
$this->userRepository->updateRoles($user, $roles);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment