From c9eae9da18a6b1f64782605a691e872ff7a14f3a Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 5 Nov 2016 17:10:17 +0100 Subject: [PATCH 1/5] Make test login form available as a regular form type --- .../DependencyInjection/SecurityExtension.php | 1 + .../Type/UsernamePasswordType.php} | 11 ++++++----- .../SecurityBundle/Resources/config/form.xml | 13 +++++++++++++ .../Controller/LoginController.php | 2 +- .../Tests/Functional/CsrfFormLoginTest.php | 16 ++++++++-------- .../Functional/app/CsrfFormLogin/config.yml | 13 ------------- 6 files changed, 29 insertions(+), 27 deletions(-) rename src/Symfony/Bundle/SecurityBundle/{Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php => Form/Type/UsernamePasswordType.php} (86%) create mode 100644 src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 5c88548bcff1d..736c00f269ada 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -59,6 +59,7 @@ public function load(array $configs, ContainerBuilder $container) // load services $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('security.xml'); + $loader->load('form.xml'); $loader->load('security_listeners.xml'); $loader->load('security_rememberme.xml'); $loader->load('templating_php.xml'); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php similarity index 86% rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php rename to src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php index b82e1f827897d..035ddd4585730 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php +++ b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form; +namespace Symfony\Bundle\SecurityBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -27,7 +27,7 @@ * @author Henrik Bjornskov * @author Jeremy Mikola */ -class UserLoginType extends AbstractType +class UsernamePasswordType extends AbstractType { private $requestStack; @@ -42,8 +42,8 @@ public function __construct(RequestStack $requestStack) public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType') - ->add('password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType') + ->add('_username', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('_password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType') ->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType') ; @@ -66,7 +66,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) } $event->setData(array_replace((array) $event->getData(), array( - 'username' => $request->getSession()->get(Security::LAST_USERNAME), + '_username' => $request->getSession()->get(Security::LAST_USERNAME), ))); }); } @@ -81,6 +81,7 @@ public function configureOptions(OptionsResolver $resolver) */ $resolver->setDefaults(array( + 'csrf_field_name' => '_csrf_token', 'csrf_token_id' => 'authenticate', )); } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml new file mode 100644 index 0000000000000..357a86a95e2ce --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php index c232b4cfd9029..ded2307684325 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php @@ -22,7 +22,7 @@ class LoginController implements ContainerAwareInterface public function loginAction() { - $form = $this->container->get('form.factory')->create('Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType'); + $form = $this->container->get('form.factory')->createNamed('', 'Symfony\Bundle\SecurityBundle\Form\Type\UsernamePasswordType'); return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:login.html.twig', array( 'form' => $form->createView(), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 80211f5251b08..98c1d2409c66d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -21,8 +21,8 @@ public function testFormLoginAndLogoutWithCsrfTokens($config) $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); $form = $client->request('GET', '/login')->selectButton('login')->form(); - $form['user_login[username]'] = 'johannes'; - $form['user_login[password]'] = 'test'; + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; $client->submit($form); $this->assertRedirect($client->getResponse(), '/profile'); @@ -51,7 +51,7 @@ public function testFormLoginWithInvalidCsrfToken($config) $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); $form = $client->request('GET', '/login')->selectButton('login')->form(); - $form['user_login[_token]'] = ''; + $form['_csrf_token'] = ''; $client->submit($form); $this->assertRedirect($client->getResponse(), '/login'); @@ -68,9 +68,9 @@ public function testFormLoginWithCustomTargetPath($config) $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); $form = $client->request('GET', '/login')->selectButton('login')->form(); - $form['user_login[username]'] = 'johannes'; - $form['user_login[password]'] = 'test'; - $form['user_login[_target_path]'] = '/foo'; + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $form['_target_path'] = '/foo'; $client->submit($form); $this->assertRedirect($client->getResponse(), '/foo'); @@ -91,8 +91,8 @@ public function testFormLoginRedirectsToProtectedResourceAfterLogin($config) $this->assertRedirect($client->getResponse(), '/login'); $form = $client->followRedirect()->selectButton('login')->form(); - $form['user_login[username]'] = 'johannes'; - $form['user_login[password]'] = 'test'; + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; $client->submit($form); $this->assertRedirect($client->getResponse(), '/protected-resource'); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml index 5a00ac329895d..205203407a056 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -1,14 +1,6 @@ imports: - { resource: ./../config/default.yml } -services: - csrf_form_login.form.type: - class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType - arguments: - - '@request_stack' - tags: - - { name: form.type } - security: encoders: Symfony\Component\Security\Core\User\User: plaintext @@ -31,11 +23,6 @@ security: form_login: check_path: /login_check default_target_path: /profile - target_path_parameter: "user_login[_target_path]" - failure_path_parameter: "user_login[_failure_path]" - username_parameter: "user_login[username]" - password_parameter: "user_login[password]" - csrf_parameter: "user_login[_token]" csrf_token_generator: security.csrf.token_manager anonymous: ~ logout: From b380f66e6343819473913c3cabe88fc24aaf89fa Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 5 Nov 2016 18:39:44 +0100 Subject: [PATCH 2/5] Retrieve error and last username from dedicated service --- .../Form/Type/UsernamePasswordType.php | 23 ++++++------------- .../SecurityBundle/Resources/config/form.xml | 2 +- .../Tests/Functional/CsrfFormLoginTest.php | 22 ++++++++++++++++++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php index 035ddd4585730..0d0f008701d5a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php +++ b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php @@ -16,9 +16,8 @@ use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; /** * Form type for use with the Security component's form-based authentication @@ -29,11 +28,11 @@ */ class UsernamePasswordType extends AbstractType { - private $requestStack; + private $authenticationUtils; - public function __construct(RequestStack $requestStack) + public function __construct(AuthenticationUtils $authenticationUtils) { - $this->requestStack = $requestStack; + $this->authenticationUtils = $authenticationUtils; } /** @@ -47,26 +46,18 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType') ; - $request = $this->requestStack->getCurrentRequest(); - /* Note: since the Security component's form login listener intercepts * the POST request, this form will never really be bound to the * request; however, we can match the expected behavior by checking the * session for an authentication error and last username. */ - $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($request) { - if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { - $error = $request->attributes->get(Security::AUTHENTICATION_ERROR); - } else { - $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); - } - - if ($error) { + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + if (null !== $error = $this->authenticationUtils->getLastAuthenticationError()) { $event->getForm()->addError(new FormError($error->getMessage())); } $event->setData(array_replace((array) $event->getData(), array( - '_username' => $request->getSession()->get(Security::LAST_USERNAME), + '_username' => $this->authenticationUtils->getLastUsername(), ))); }); } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml index 357a86a95e2ce..ca13324b3e76f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml @@ -7,7 +7,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 98c1d2409c66d..76ea88d22bbd5 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -43,6 +43,28 @@ public function testFormLoginAndLogoutWithCsrfTokens($config) $this->assertRedirect($client->getResponse(), '/'); } + /** + * @dataProvider getConfigs + */ + public function testFormLoginWithBadCredentialsAndCsrfTokens($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'wrong'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/login'); + + $crawler = $client->followRedirect(); + + $this->assertContains('Bad credentials.', $crawler->text()); + + $usernameInput = $crawler->filter('[name="_username"]'); + $this->assertSame('johannes', $usernameInput->attr('value')); + } + /** * @dataProvider getConfigs */ From 24092b54c21b731e5c40cb784b29fa4ffd0cf735 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 26 Nov 2016 21:31:00 +0100 Subject: [PATCH 3/5] Add options for field names --- .../Form/Type/UsernamePasswordType.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php index 0d0f008701d5a..b1bbc7148d247 100644 --- a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php +++ b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php @@ -41,9 +41,9 @@ public function __construct(AuthenticationUtils $authenticationUtils) public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('_username', 'Symfony\Component\Form\Extension\Core\Type\TextType') - ->add('_password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType') - ->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType') + ->add($options['username_field_name'], 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add($options['password_field_name'], 'Symfony\Component\Form\Extension\Core\Type\PasswordType') + ->add($options['target_path_field_name'], 'Symfony\Component\Form\Extension\Core\Type\HiddenType') ; /* Note: since the Security component's form login listener intercepts @@ -51,13 +51,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) * request; however, we can match the expected behavior by checking the * session for an authentication error and last username. */ - $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) { if (null !== $error = $this->authenticationUtils->getLastAuthenticationError()) { $event->getForm()->addError(new FormError($error->getMessage())); } $event->setData(array_replace((array) $event->getData(), array( - '_username' => $this->authenticationUtils->getLastUsername(), + $options['username_field_name'] => $this->authenticationUtils->getLastUsername(), ))); }); } @@ -72,6 +72,9 @@ public function configureOptions(OptionsResolver $resolver) */ $resolver->setDefaults(array( + 'username_field_name' => '_username', + 'password_field_name' => '_password', + 'target_path_field_name' => '_target_path', 'csrf_field_name' => '_csrf_token', 'csrf_token_id' => 'authenticate', )); From e4bf5b5d4cfac1ae7dc482353ef2429fe81bc8d3 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 26 Nov 2016 23:15:50 +0100 Subject: [PATCH 4/5] Add target_firewall option --- .../Form/Type/UsernamePasswordType.php | 50 ++++++++++++++++--- .../SecurityBundle/Resources/config/form.xml | 1 + 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php index b1bbc7148d247..d5d29a6ecb303 100644 --- a/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php +++ b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php @@ -16,7 +16,10 @@ use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; /** @@ -29,10 +32,12 @@ class UsernamePasswordType extends AbstractType { private $authenticationUtils; + private $urlGenerator; - public function __construct(AuthenticationUtils $authenticationUtils) + public function __construct(AuthenticationUtils $authenticationUtils, UrlGeneratorInterface $urlGenerator) { $this->authenticationUtils = $authenticationUtils; + $this->urlGenerator = $urlGenerator; } /** @@ -72,11 +77,44 @@ public function configureOptions(OptionsResolver $resolver) */ $resolver->setDefaults(array( - 'username_field_name' => '_username', - 'password_field_name' => '_password', - 'target_path_field_name' => '_target_path', - 'csrf_field_name' => '_csrf_token', - 'csrf_token_id' => 'authenticate', + 'target_firewall' => null, + 'action' => function (Options $options) { + $action = $this->getFirewallOption($options['target_firewall'], 'check_path', '/login_check'); + + if ('/' === $action[0]) { + return $action; + } + + return $this->urlGenerator->generate($action); + }, + 'username_field_name' => function (Options $options) { + return $this->getFirewallOption($options['target_firewall'], 'username_parameter', '_username'); + }, + 'password_field_name' => function (Options $options) { + return $this->getFirewallOption($options['target_firewall'], 'password_parameter', '_password'); + }, + 'target_path_field_name' => function (Options $options) { + return $this->getFirewallOption($options['target_firewall'], 'target_path_parameter', '_target_path'); + }, + 'csrf_field_name' => function (Options $options) { + return $this->getFirewallOption($options['target_firewall'], 'csrf_parameter', '_csrf_token'); + }, + 'csrf_token_id' => function (Options $options) { + return $this->getFirewallOption($options['target_firewall'], 'csrf_token_id', 'authenticate'); + }, )); } + + private function getFirewallOption($firewallName, $optionName, $default) + { + if (null === $firewallName) { + return $default; + } + + if (null === $config = $this->configRegistry->get($firewallName)) { + throw new InvalidOptionsException(sprintf('The firewall "%s" does not exist.', $firewallName)); + } + + return $config->getOption($optionName); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml index ca13324b3e76f..6a01181e974b7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml @@ -8,6 +8,7 @@ + From dcca8a45646d1ebf5d264c7d19fd5fd749e22d57 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 26 Nov 2016 23:32:26 +0100 Subject: [PATCH 5/5] Add shortcut method to create a login form --- .../FrameworkBundle/Controller/Controller.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 1dbf54b5adf9d..2b7c50103333a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; +use Symfony\Bundle\SecurityBundle\Form\Type\UsernamePasswordType; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -345,6 +346,20 @@ protected function createFormBuilder($data = null, array $options = array()) return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); } + /** + * Creates a Form instance that can be rendered as a login form for the given firewall. + * + * @param string $firewall The name of the target firewall + * + * @return Form + */ + protected function createLoginForm($firewall) + { + return $this->container->get('form.factory')->createNamed('', UsernamePasswordType::class, null, array( + 'target_firewall' => $firewall, + )); + } + /** * Shortcut to return the Doctrine Registry service. * pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy