chg: dev: refactor the SecurityController #7
This commit is contained in:
@@ -15,16 +15,17 @@ use App\Form\ForgotPasswordFormType;
|
||||
use App\Form\RegistrationFormType;
|
||||
use App\Form\ResetPasswordFormType;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Service\Email\SendActivationEmailService;
|
||||
use App\Service\Email\SendPasswordResetEmailService;
|
||||
use App\Service\Email\SendUserActivationNotificationService;
|
||||
use App\Service\Email\SendUserRegistrationNotificationService;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use LogicException;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Attribute\AsController;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
@@ -44,21 +45,28 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
#[Autowire(env: 'APP_CONTACT_MAIL_ADDRESS')]
|
||||
private readonly string $appContactMailAddress,
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly UserPasswordHasherInterface $passwordHasher,
|
||||
private readonly AuthenticationUtils $authenticationUtils,
|
||||
private readonly SendActivationEmailService $activationEmail,
|
||||
private readonly SendPasswordResetEmailService $passwordResetEmail,
|
||||
private readonly SendUserActivationNotificationService $activationNotificationEmail,
|
||||
private readonly SendUserRegistrationNotificationService $registrationNotificationEmail,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/login', name: 'MineSeekerBundle_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
public function login(): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('MineSeekerBundle_homepage');
|
||||
}
|
||||
|
||||
return $this->render('Security/login.html.twig', [
|
||||
'last_username' => $authenticationUtils->getLastUsername(),
|
||||
'error' => $authenticationUtils->getLastAuthenticationError(),
|
||||
'last_username' => $this->authenticationUtils->getLastUsername(),
|
||||
'error' => $this->authenticationUtils->getLastAuthenticationError(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -69,29 +77,25 @@ class SecurityController extends AbstractController
|
||||
}
|
||||
|
||||
#[Route('/register', name: 'MineSeekerBundle_register')]
|
||||
public function register(
|
||||
Request $request,
|
||||
UserPasswordHasherInterface $hasher,
|
||||
EntityManagerInterface $em,
|
||||
MailerInterface $mailer,
|
||||
): Response {
|
||||
public function register(): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('MineSeekerBundle_homepage');
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
$form = $this->createForm(RegistrationFormType::class, $user);
|
||||
$form->handleRequest($request);
|
||||
$form->handleRequest($this->requestStack->getCurrentRequest());
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$token = bin2hex(random_bytes(32));
|
||||
|
||||
$user->isVerified = false;
|
||||
$user->verificationToken = $token;
|
||||
$user->password = $hasher->hashPassword($user, $form->get('plainPassword')->getData());
|
||||
$user->password = $this->passwordHasher->hashPassword($user, $form->get('plainPassword')->getData());
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
|
||||
$activationUrl = $this->generateUrl(
|
||||
'MineSeekerBundle_activate',
|
||||
@@ -104,30 +108,8 @@ class SecurityController extends AbstractController
|
||||
$activationUrl = str_replace('http://', 'https://', $activationUrl);
|
||||
}
|
||||
|
||||
$mailer->send(
|
||||
new TemplatedEmail()
|
||||
->from('noreply@mineseeker.hu')
|
||||
->to($user->email)
|
||||
->subject('Activate your MineSeeker account')
|
||||
->htmlTemplate('emails/activation.html.twig')
|
||||
->context([
|
||||
'username' => $user->getUsername(),
|
||||
'activation_url' => $activationUrl,
|
||||
])
|
||||
);
|
||||
|
||||
/** Send admin notification about new user registration */
|
||||
$mailer->send(
|
||||
new TemplatedEmail()
|
||||
->from('noreply@mineseeker.hu')
|
||||
->to($this->appContactMailAddress)
|
||||
->subject('🎉 New User Registration: ' . $user->getUsername())
|
||||
->htmlTemplate('emails/user_registration_notification.html.twig')
|
||||
->context([
|
||||
'user' => $user,
|
||||
'registeredAt' => new DateTime(),
|
||||
])
|
||||
);
|
||||
$this->activationEmail->send($user, $activationUrl);
|
||||
$this->registrationNotificationEmail->send($user, new DateTime());
|
||||
|
||||
$this->addFlash('verify_email', $user->email);
|
||||
|
||||
@@ -138,28 +120,24 @@ class SecurityController extends AbstractController
|
||||
}
|
||||
|
||||
#[Route('/forgot-password', name: 'MineSeekerBundle_forgot_password')]
|
||||
public function forgotPassword(
|
||||
Request $request,
|
||||
UserRepository $userRepository,
|
||||
EntityManagerInterface $em,
|
||||
MailerInterface $mailer,
|
||||
): Response {
|
||||
public function forgotPassword(): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('MineSeekerBundle_homepage');
|
||||
}
|
||||
|
||||
$form = $this->createForm(ForgotPasswordFormType::class);
|
||||
$form->handleRequest($request);
|
||||
$form->handleRequest($this->requestStack->getCurrentRequest());
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$email = $form->get('email')->getData();
|
||||
$user = $userRepository->findOneByEmail($email);
|
||||
$user = $this->userRepository->findOneByEmail($email);
|
||||
|
||||
if ($user && $user->isVerified) {
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$user->resetToken = $token;
|
||||
$user->resetTokenExpiresAt = new DateTime('+1 hour');
|
||||
$em->flush();
|
||||
$this->em->flush();
|
||||
|
||||
$resetUrl = $this->generateUrl(
|
||||
'MineSeekerBundle_reset_password',
|
||||
@@ -172,20 +150,9 @@ class SecurityController extends AbstractController
|
||||
$resetUrl = str_replace('http://', 'https://', $resetUrl);
|
||||
}
|
||||
|
||||
$mailer->send(
|
||||
new TemplatedEmail()
|
||||
->from('noreply@mineseeker.hu')
|
||||
->to($email)
|
||||
->subject('Reset your MineSeeker password')
|
||||
->htmlTemplate('emails/reset_password.html.twig')
|
||||
->context([
|
||||
'username' => $user->getUsername(),
|
||||
'reset_url' => $resetUrl,
|
||||
])
|
||||
);
|
||||
$this->passwordResetEmail->send($email, $user->getUsername(), $resetUrl);
|
||||
}
|
||||
|
||||
// Always show the same flash to prevent email enumeration
|
||||
$this->addFlash('reset_sent', $email);
|
||||
|
||||
return $this->redirectToRoute('MineSeekerBundle_forgot_password');
|
||||
@@ -195,14 +162,9 @@ class SecurityController extends AbstractController
|
||||
}
|
||||
|
||||
#[Route('/reset-password/{token}', name: 'MineSeekerBundle_reset_password')]
|
||||
public function resetPassword(
|
||||
string $token,
|
||||
Request $request,
|
||||
UserRepository $userRepository,
|
||||
EntityManagerInterface $em,
|
||||
UserPasswordHasherInterface $hasher,
|
||||
): Response {
|
||||
$user = $userRepository->findOneByResetToken($token);
|
||||
public function resetPassword(string $token): Response
|
||||
{
|
||||
$user = $this->userRepository->findOneByResetToken($token);
|
||||
|
||||
if (!$user || $user->resetTokenExpiresAt < new DateTime()) {
|
||||
$this->addFlash('error', 'This password reset link is invalid or has expired.');
|
||||
@@ -210,13 +172,14 @@ class SecurityController extends AbstractController
|
||||
}
|
||||
|
||||
$form = $this->createForm(ResetPasswordFormType::class);
|
||||
$form->handleRequest($request);
|
||||
$form->handleRequest($this->requestStack->getCurrentRequest());
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$user->password = $hasher->hashPassword($user, $form->get('plainPassword')->getData());
|
||||
$user->password = $this->passwordHasher->hashPassword($user, $form->get('plainPassword')->getData());
|
||||
$user->resetToken = null;
|
||||
$user->resetTokenExpiresAt = null;
|
||||
$em->flush();
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$this->addFlash('success', 'Your password has been reset. You can now sign in.');
|
||||
|
||||
@@ -227,9 +190,9 @@ class SecurityController extends AbstractController
|
||||
}
|
||||
|
||||
#[Route('/activate/{token}', name: 'MineSeekerBundle_activate')]
|
||||
public function activate(string $token, EntityManagerInterface $em, MailerInterface $mailer): Response
|
||||
public function activate(string $token): Response
|
||||
{
|
||||
$user = $em->getRepository(User::class)->findOneBy(['verificationToken' => $token]);
|
||||
$user = $this->em->getRepository(User::class)->findOneBy(['verificationToken' => $token]);
|
||||
|
||||
if (!$user) {
|
||||
$this->addFlash('error', 'This activation link is invalid or has already been used.');
|
||||
@@ -238,20 +201,9 @@ class SecurityController extends AbstractController
|
||||
|
||||
$user->isVerified = true;
|
||||
$user->verificationToken = null;
|
||||
$em->flush();
|
||||
$this->em->flush();
|
||||
|
||||
/** Send admin notification about account activation */
|
||||
$mailer->send(
|
||||
new TemplatedEmail()
|
||||
->from('noreply@mineseeker.hu')
|
||||
->to($this->appContactMailAddress)
|
||||
->subject('✅ User Account Activated: ' . $user->getUsername())
|
||||
->htmlTemplate('emails/user_activation_notification.html.twig')
|
||||
->context([
|
||||
'user' => $user,
|
||||
'activatedAt' => new DateTime(),
|
||||
])
|
||||
);
|
||||
$this->activationNotificationEmail->send($user, new DateTime());
|
||||
|
||||
$this->addFlash('success', 'Your account is now active. Welcome, ' . $user->getUsername() . '!');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user