Skip to content

Commit

Permalink
Fix login error
Browse files Browse the repository at this point in the history
Fixed ticket #183
  • Loading branch information
core23 authored Jan 8, 2021
2 parents abe925c + 7c1405a commit 481a838
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 61 deletions.
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
parameters:
ignoreErrors:
-
message: "#^Call to method Symfony\\\\Component\\\\HttpFoundation\\\\Request\\:\\:hasSession\\(\\) will always evaluate to true\\.$#"
message: "#^Call to an undefined method Symfony\\\\Component\\\\Form\\\\FormError\\|Symfony\\\\Component\\\\Form\\\\FormErrorIterator\\:\\:getMessage\\(\\)\\.$#"
count: 1
path: src/Action/LoginAction.php

Expand Down
56 changes: 18 additions & 38 deletions src/Action/LoginAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@
use Nucleos\UserBundle\Event\GetResponseLoginEvent;
use Nucleos\UserBundle\Form\Type\LoginFormType;
use Nucleos\UserBundle\NucleosUserEvents;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Twig\Environment;

Expand All @@ -49,16 +46,23 @@ final class LoginAction
*/
private $router;

/**
* @var CsrfTokenManagerInterface
*/
private $csrfTokenManager;

public function __construct(
Environment $twig,
EventDispatcherInterface $eventDispatcher,
FormFactoryInterface $formFactory,
RouterInterface $router
RouterInterface $router,
CsrfTokenManagerInterface $csrfTokenManager
) {
$this->twig = $twig;
$this->eventDispatcher = $eventDispatcher;
$this->formFactory = $formFactory;
$this->router = $router;
$this->twig = $twig;
$this->eventDispatcher = $eventDispatcher;
$this->formFactory = $formFactory;
$this->router = $router;
$this->csrfTokenManager = $csrfTokenManager;
}

/**
Expand All @@ -73,46 +77,22 @@ public function __invoke(Request $request): Response
return $event->getResponse();
}

$session = $this->getSession($request);

$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;
}

$form = $this->formFactory->create(LoginFormType::class, null, [
'action' => $this->router->generate('nucleos_user_security_check'),
'method' => 'POST',
]);

if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
} else {
$form->addError(new FormError($error->getMessageKey(), null, $error->getMessageData()));
$error = null;
if ($form->getErrors()->count() > 0) {
$error = $form->getErrors()->current()->getMessage();
}

// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);

return new Response($this->twig->render('@NucleosUser/Security/login.html.twig', [
'last_username' => $lastUsername,
'form' => $form->createView(),
// TODO: Remove this fields with the next major release
'last_username' => $form->getData()['_username'],
'error' => $error,
'csrf_token' => '',
'csrf_token' => $this->csrfTokenManager->getToken('authenticate'),
]));
}

private function getSession(Request $request): ?SessionInterface
{
return $request->hasSession() ? $request->getSession() : null;
}
}
33 changes: 12 additions & 21 deletions src/Form/Type/LoginFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,19 @@
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

final class LoginFormType extends AbstractType
{
/**
* @var RequestStack
* @var AuthenticationUtils
*/
private $requestStack;
private $authenticationUtils;

public function __construct(RequestStack $requestStack)
public function __construct(AuthenticationUtils $authenticationUtils)
{
$this->requestStack = $requestStack;
$this->authenticationUtils = $authenticationUtils;
}

public function buildForm(FormBuilderInterface $builder, array $options): void
Expand Down Expand Up @@ -63,27 +62,16 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
])
;

$request = $this->requestStack->getCurrentRequest();

$builder->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $event) use ($request): void {
if (null === $request) {
return;
}

$error = null;

if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(Security::AUTHENTICATION_ERROR);
}
$authenticationUtils = $this->authenticationUtils;

$builder->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $event) use ($authenticationUtils): void {
$error = $authenticationUtils->getLastAuthenticationError();
if (null !== $error) {
$event->getForm()->addError(new FormError($error->getMessage()));
}

$event->setData(array_replace((array) $event->getData(), [
'username' => $request->getSession()->get(Security::LAST_USERNAME),
'username' => $authenticationUtils->getLastUsername(),
]));
});
}
Expand All @@ -92,6 +80,9 @@ public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'translation_domain' => 'NucleosUserBundle',

'csrf_field_name' => '_csrf_token',
'csrf_token_id' => 'authenticate',
]);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Resources/config/security.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
->set(LoginFormType::class)
->tag('form.type')
->args([
new Reference('request_stack'),
new Reference('security.authentication_utils'),
])

->set(LoginAction::class)
Expand All @@ -72,6 +72,7 @@
new Reference('event_dispatcher'),
new Reference('form.factory'),
new Reference('router'),
new Reference('security.csrf.token_manager'),
])

->set(LogoutAction::class)
Expand Down

0 comments on commit 481a838

Please sign in to comment.