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. * 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/Form/Type/UsernamePasswordType.php b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php new file mode 100644 index 0000000000000..d5d29a6ecb303 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Form/Type/UsernamePasswordType.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +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; + +/** + * Form type for use with the Security component's form-based authentication + * listener. + * + * @author Henrik Bjornskov + * @author Jeremy Mikola + */ +class UsernamePasswordType extends AbstractType +{ + private $authenticationUtils; + private $urlGenerator; + + public function __construct(AuthenticationUtils $authenticationUtils, UrlGeneratorInterface $urlGenerator) + { + $this->authenticationUtils = $authenticationUtils; + $this->urlGenerator = $urlGenerator; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->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 + * 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 ($options) { + if (null !== $error = $this->authenticationUtils->getLastAuthenticationError()) { + $event->getForm()->addError(new FormError($error->getMessage())); + } + + $event->setData(array_replace((array) $event->getData(), array( + $options['username_field_name'] => $this->authenticationUtils->getLastUsername(), + ))); + }); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + /* Note: the form's csrf_token_id must correspond to that for the form login + * listener in order for the CSRF token to validate successfully. + */ + + $resolver->setDefaults(array( + '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 new file mode 100644 index 0000000000000..6a01181e974b7 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/form.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + 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/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php deleted file mode 100644 index b82e1f827897d..0000000000000 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form; - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilderInterface; -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; - -/** - * Form type for use with the Security component's form-based authentication - * listener. - * - * @author Henrik Bjornskov - * @author Jeremy Mikola - */ -class UserLoginType extends AbstractType -{ - private $requestStack; - - public function __construct(RequestStack $requestStack) - { - $this->requestStack = $requestStack; - } - - /** - * {@inheritdoc} - */ - 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') - ; - - $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) { - $event->getForm()->addError(new FormError($error->getMessage())); - } - - $event->setData(array_replace((array) $event->getData(), array( - 'username' => $request->getSession()->get(Security::LAST_USERNAME), - ))); - }); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - /* Note: the form's csrf_token_id must correspond to that for the form login - * listener in order for the CSRF token to validate successfully. - */ - - $resolver->setDefaults(array( - 'csrf_token_id' => 'authenticate', - )); - } -} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 80211f5251b08..76ea88d22bbd5 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'); @@ -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 */ @@ -51,7 +73,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 +90,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 +113,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: 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