Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)

This commit is contained in:
Kevin Papst
2022-12-31 21:19:55 +01:00
committed by GitHub
parent 95e06746bd
commit 90a0fd8a22
2164 changed files with 83700 additions and 86426 deletions

View File

@@ -27,28 +27,22 @@ use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @Route(path="/resetting")
*/
#[Route(path: '/resetting')]
final class PasswordResetController extends AbstractController
{
private $eventDispatcher;
private $userService;
private $configuration;
public function __construct(EventDispatcherInterface $eventDispatcher, UserService $userService, SystemConfiguration $configuration)
{
$this->eventDispatcher = $eventDispatcher;
$this->userService = $userService;
$this->configuration = $configuration;
public function __construct(
private EventDispatcherInterface $eventDispatcher,
private UserService $userService,
private SystemConfiguration $configuration
) {
}
/**
* Request reset user password: show form.
*
* @Route(path="/request", name="fos_user_resetting_request", methods={"GET"})
*/
#[Route(path: '/request', name: 'resetting_request', methods: ['GET'])]
public function requestAction(): Response
{
if (!$this->configuration->isPasswordResetActive()) {
@@ -60,10 +54,9 @@ final class PasswordResetController extends AbstractController
/**
* Request reset user password: submit form and send email.
*
* @Route(path="/send-email", name="fos_user_resetting_send_email", methods={"POST"})
*/
public function sendEmailAction(Request $request): Response
#[Route(path: '/send-email', name: 'resetting_send_email', methods: ['POST'])]
public function sendEmailAction(Request $request, TranslatorInterface $translator): Response
{
if (!$this->configuration->isPasswordResetActive()) {
throw $this->createNotFoundException();
@@ -75,7 +68,7 @@ final class PasswordResetController extends AbstractController
if (null !== $user && !$user->isPasswordRequestNonExpired($this->configuration->getPasswordResetRetryLifetime())) {
if (!$user->isInternalUser()) {
throw $this->createAccessDeniedException(
sprintf('The user "%s" tried to reset the password, but it is registered as "%s" auth-type.', $user->getUsername(), $user->getAuth())
sprintf('The user "%s" tried to reset the password, but it is registered as "%s" auth-type.', $user->getUserIdentifier(), $user->getAuth())
);
}
@@ -83,7 +76,7 @@ final class PasswordResetController extends AbstractController
$user->setConfirmationToken($this->userService->generateSecurityToken());
}
$mail = $this->generateResettingEmailMessage($user);
$mail = $this->generateResettingEmailMessage($user, $translator);
$event = new EmailPasswordResetEvent($user, $mail);
$this->eventDispatcher->dispatch($event);
@@ -94,14 +87,13 @@ final class PasswordResetController extends AbstractController
$this->userService->updateUser($user);
}
return $this->redirectToRoute('fos_user_resetting_check_email', ['username' => $username]);
return $this->redirectToRoute('resetting_check_email', ['username' => $username]);
}
/**
* Tell the user to check his email provider.
*
* @Route(path="/check-email", name="fos_user_resetting_check_email", methods={"GET"})
*/
#[Route(path: '/check-email', name: 'resetting_check_email', methods: ['GET'])]
public function checkEmailAction(Request $request): Response
{
if (!$this->configuration->isPasswordResetActive()) {
@@ -112,7 +104,7 @@ final class PasswordResetController extends AbstractController
if (empty($username)) {
// the user does not come from the sendEmail action
return $this->redirectToRoute('fos_user_resetting_request');
return $this->redirectToRoute('resetting_request');
}
return $this->render('security/password-reset/check_email.html.twig', [
@@ -122,9 +114,8 @@ final class PasswordResetController extends AbstractController
/**
* Reset user password.
*
* @Route(path="/reset/{token}", name="fos_user_resetting_reset", methods={"GET", "POST"})
*/
#[Route(path: '/reset/{token}', name: 'resetting_reset', methods: ['GET', 'POST'])]
public function resetAction(Request $request, LoginManager $loginManager, ?string $token): Response
{
if (!$this->configuration->isPasswordResetActive()) {
@@ -134,11 +125,11 @@ final class PasswordResetController extends AbstractController
$user = $this->userService->findUserByConfirmationToken($token);
if (null === $user) {
return $this->redirectToRoute('fos_user_security_login');
return $this->redirectToRoute('login');
}
if (!$user->isPasswordRequestNonExpired($this->configuration->getPasswordResetTokenLifetime())) {
return $this->redirectToRoute('fos_user_resetting_request');
return $this->redirectToRoute('resetting_request');
}
$form = $this->createResetForm();
@@ -169,20 +160,20 @@ final class PasswordResetController extends AbstractController
{
$options = ['validation_groups' => ['ResetPassword', 'Default']];
return $this->createFormBuilder()->create('fos_user_resetting_form', PasswordResetForm::class, $options)->getForm();
return $this->createFormBuilder()->create('resetting_form', PasswordResetForm::class, $options)->getForm();
}
private function generateResettingEmailMessage(User $user): Email
private function generateResettingEmailMessage(User $user, TranslatorInterface $translator): Email
{
$username = $user->getDisplayName();
$language = $user->getLanguage();
$url = $this->generateUrl('fos_user_resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
$url = $this->generateUrl('resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
return (new TemplatedEmail())
->to(new Address($user->getEmail()))
->subject(
$this->getTranslator()->trans('reset.subject', ['%username%' => $username], 'email', $language)
$translator->trans('reset.subject', ['%username%' => $username], 'email', $language)
)
->htmlTemplate('emails/password-reset.html.twig')
->context([

View File

@@ -10,62 +10,36 @@
namespace App\Controller\Security;
use App\Configuration\SamlConfigurationInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use App\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
final class SecurityController extends AbstractController
{
private $tokenManager;
private $samlConfiguration;
public function __construct(CsrfTokenManagerInterface $tokenManager, SamlConfigurationInterface $samlConfiguration)
public function __construct(private CsrfTokenManagerInterface $tokenManager, private SamlConfigurationInterface $samlConfiguration)
{
$this->tokenManager = $tokenManager;
$this->samlConfiguration = $samlConfiguration;
}
/**
* @Route(path="/login", name="fos_user_security_login", methods={"GET", "POST"})
*/
public function loginAction(Request $request): Response
#[Route(path: '/login', name: 'login', methods: ['GET', 'POST'])]
public function loginAction(AuthenticationUtils $authenticationUtils): Response
{
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirectToRoute('homepage');
}
/** @var SessionInterface $session */
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
$lastUsername = '';
if ($request->hasSession()) {
$lastUsername = $session->get($lastUsernameKey);
}
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
$csrfToken = $this->tokenManager->getToken('authenticate')->getValue();
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED') && $this->getUser()->isInternalUser()) {
return $this->render('security/unlock.html.twig', [
'error' => $error,
'csrf_token' => $csrfToken,
]);
}
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
@@ -74,17 +48,13 @@ final class SecurityController extends AbstractController
]);
}
/**
* @Route(path="/login_check", name="fos_user_security_check", methods={"POST"})
*/
#[Route(path: '/login_check', name: 'security_check', methods: ['POST'])]
public function checkAction()
{
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
}
/**
* @Route(path="/logout", name="fos_user_security_logout", methods={"GET", "POST"})
*/
#[Route(path: '/logout', name: 'logout', methods: ['GET', 'POST'])]
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');

View File

@@ -28,29 +28,21 @@ use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @Route(path="/register")
*/
class SelfRegistrationController extends AbstractController
#[Route(path: '/register')]
final class SelfRegistrationController extends AbstractController
{
private $eventDispatcher;
private $userService;
private $tokenStorage;
private $configuration;
public function __construct(EventDispatcherInterface $eventDispatcher, UserService $userService, TokenStorageInterface $tokenStorage, SystemConfiguration $configuration)
{
$this->eventDispatcher = $eventDispatcher;
$this->userService = $userService;
$this->tokenStorage = $tokenStorage;
$this->configuration = $configuration;
public function __construct(
private EventDispatcherInterface $eventDispatcher,
private UserService $userService,
private TokenStorageInterface $tokenStorage,
private SystemConfiguration $configuration
) {
}
/**
* @Route(path="/", name="fos_user_registration_register", methods={"GET", "POST"})
*/
public function registerAction(Request $request): Response
#[Route(path: '/', name: 'registration_register', methods: ['GET', 'POST'])]
public function registerAction(Request $request, TranslatorInterface $translator): Response
{
if (!$this->configuration->isSelfRegistrationActive()) {
throw $this->createNotFoundException();
@@ -68,18 +60,18 @@ class SelfRegistrationController extends AbstractController
$user->setEnabled(false);
$user->setConfirmationToken($this->userService->generateSecurityToken());
$mail = $this->generateConfirmationEmail($user);
$mail = $this->generateConfirmationEmail($user, $translator);
$event = new EmailSelfRegistrationEvent($user, $mail);
$this->eventDispatcher->dispatch($event);
// this will finally send the email
$this->eventDispatcher->dispatch(new EmailEvent($event->getEmail()));
$request->getSession()->set('fos_user_send_confirmation_email/email', $user->getEmail());
$request->getSession()->set('confirmation_email_address', $user->getEmail());
$this->userService->saveNewUser($user);
return $this->redirectToRoute('fos_user_registration_check_email');
return $this->redirectToRoute('user_registration_check_email');
}
return $this->render('security/self-registration/register.html.twig', [
@@ -89,26 +81,25 @@ class SelfRegistrationController extends AbstractController
/**
* Tell the user to check their email provider.
*
* @Route(path="/check-email", name="fos_user_registration_check_email", methods={"GET"})
*/
#[Route(path: '/check-email', name: 'user_registration_check_email', methods: ['GET'])]
public function checkEmailAction(Request $request): Response
{
if (!$this->configuration->isSelfRegistrationActive()) {
throw $this->createNotFoundException();
}
$email = $request->getSession()->get('fos_user_send_confirmation_email/email');
$email = $request->getSession()->get('confirmation_email_address');
if (empty($email)) {
return $this->redirectToRoute('fos_user_registration_register');
return $this->redirectToRoute('registration_register');
}
$request->getSession()->remove('fos_user_send_confirmation_email/email');
$request->getSession()->remove('confirmation_email_address');
$user = $this->userService->findUserByEmail($email);
if (null === $user) {
return $this->redirectToRoute('fos_user_security_login');
return $this->redirectToRoute('login');
}
return $this->render('security/self-registration/check_email.html.twig', [
@@ -118,9 +109,8 @@ class SelfRegistrationController extends AbstractController
/**
* Receive the confirmation token from user email provider, login the user.
*
* @Route(path="/confirm/{token}", name="fos_user_registration_confirm", methods={"GET"})
*/
#[Route(path: '/confirm/{token}', name: 'registration_confirm', methods: ['GET'])]
public function confirmAction(LoginManager $loginManager, ?string $token): Response
{
if (!$this->configuration->isSelfRegistrationActive()) {
@@ -130,7 +120,7 @@ class SelfRegistrationController extends AbstractController
$user = $this->userService->findUserByConfirmationToken($token);
if (null === $user) {
return $this->redirectToRoute('fos_user_security_login');
return $this->redirectToRoute('login');
}
$user->setConfirmationToken(null);
@@ -138,7 +128,7 @@ class SelfRegistrationController extends AbstractController
$this->userService->updateUser($user);
$response = $this->redirectToRoute('fos_user_registration_confirmed');
$response = $this->redirectToRoute('registration_confirmed');
$loginManager->logInUser($user, $response);
return $response;
@@ -146,22 +136,16 @@ class SelfRegistrationController extends AbstractController
/**
* Tell the user his account is now confirmed.
*
* @Route(path="/confirmed", name="fos_user_registration_confirmed", methods={"GET"})
*/
#[Route(path: '/confirmed', name: 'registration_confirmed', methods: ['GET'])]
public function confirmedAction(Request $request): Response
{
if (!$this->configuration->isSelfRegistrationActive()) {
throw $this->createNotFoundException();
}
$user = $this->getUser();
if ($user === null) {
throw $this->createAccessDeniedException('This user does not have access to this section.');
}
return $this->render('security/self-registration/confirmed.html.twig', [
'user' => $user,
'user' => $this->getUser(),
'targetUrl' => $this->getTargetUrlFromSession($request->getSession()),
]);
}
@@ -170,13 +154,13 @@ class SelfRegistrationController extends AbstractController
{
$options = ['validation_groups' => ['Registration', 'Default']];
return $this->createFormBuilder()->create('fos_user_registration_form', SelfRegistrationForm::class, $options)->getForm();
return $this->createFormBuilder()->create('user_registration_form', SelfRegistrationForm::class, $options)->getForm();
}
private function getTargetUrlFromSession(SessionInterface $session): ?string
{
$token = $this->tokenStorage->getToken();
if (!method_exists($token, 'getProviderKey')) {
if ($token === null || !method_exists($token, 'getProviderKey')) {
return null;
}
@@ -189,17 +173,17 @@ class SelfRegistrationController extends AbstractController
return null;
}
private function generateConfirmationEmail(User $user): Email
private function generateConfirmationEmail(User $user, TranslatorInterface $translator): Email
{
$username = $user->getDisplayName();
$language = $user->getLanguage();
$url = $this->generateUrl('fos_user_registration_confirm', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
$url = $this->generateUrl('registration_confirm', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
return (new TemplatedEmail())
->to(new Address($user->getEmail()))
->subject(
$this->getTranslator()->trans('registration.subject', ['%username%' => $username], 'email', $language)
$translator->trans('registration.subject', ['%username%' => $username], 'email', $language)
)
->htmlTemplate('emails/confirmation.html.twig')
->context([