瀏覽代碼

merged branch schmittjoh/httpUtilFixes (PR #1739)

Commits
-------

eae6a77 fixed wrong case
d0a175b fixes #1659
f300ede fixes several bugs
a4f05ac added some tests

Discussion
----------

Http util fixes

Fixes several bugs in the http utils.

Please don't add anymore features without sufficient tests. Especially for the Security\Http namespace, regressions are very likely otherwise.

---------------------------------------------------------------------------

by fabpot at 2011/07/19 22:37:26 -0700

Tests do not pass for me:

    There were 2 errors:

    1) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testLoginLogoutProcedure with data set #0 ('en')
    InvalidArgumentException: The current node list is empty.

    .../src/Symfony/Component/DomCrawler/Crawler.php:604
    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:16

    2) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testLoginLogoutProcedure with data set #1 ('de')
    InvalidArgumentException: The current node list is empty.

    .../src/Symfony/Component/DomCrawler/Crawler.php:604
    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:16

    --

    There were 4 failures:

    1) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testAccessRestrictedResource with data set #0 ('en')
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -http://localhost/en/login
    +http://localhost/login

    .../src/Symfony/Bundle/Securitybundle/Tests/Functional/WebTestCase.php:22
    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:38

    2) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testAccessRestrictedResource with data set #1 ('de')
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -http://localhost/de/login
    +http://localhost/login

    .../src/Symfony/Bundle/Securitybundle/Tests/Functional/WebTestCase.php:22
    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:38

    3) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testAccessRestrictedResourceWithForward with data set #0 ('en')
    HTTP/1.0 302 Found
    Cache-Control:  no-cache
    Content-Length: 299
    Content-Type:   text/html; charset=UTF-8
    Date:           Wed, 20 Jul 2011 05:36:27 GMT
    Location:       http://localhost/login
    Set-Cookie: PHPSESSID=11c9c6a7e7620e13bddef223a5ba46d9; path=/; domain=

    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <meta http-equiv="refresh" content="1;url=http://localhost/login" />
        </head>
        <body>
            Redirecting to <a href="http://localhost/login">http://localhost/login</a>.
        </body>
    </html>
    Failed asserting that <integer:0> matches expected <integer:1>.

    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:50

    4) Symfony\Bundle\SecurityBundle\Tests\Functional\LocalizedRoutesAsPathTest::testAccessRestrictedResourceWithForward with data set #1 ('de')
    HTTP/1.0 302 Found
    Cache-Control:  no-cache
    Content-Length: 299
    Content-Type:   text/html; charset=UTF-8
    Date:           Wed, 20 Jul 2011 05:36:28 GMT
    Location:       http://localhost/login
    Set-Cookie: PHPSESSID=2bbe63786a088471ade3717917f4ba4f; path=/; domain=

    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <meta http-equiv="refresh" content="1;url=http://localhost/login" />
        </head>
        <body>
            Redirecting to <a href="http://localhost/login">http://localhost/login</a>.
        </body>
    </html>
    Failed asserting that <integer:0> matches expected <integer:1>.

    .../src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php:50

---------------------------------------------------------------------------

by schmittjoh at 2011/07/19 23:47:29 -0700

I fixed a wrong case, but I couldn't reproduce the other errors (tested on Ubuntu).

My guess is that the temporary directory on your machine couldn't be deleted for some reason, and the test runs with the configuration of some of the previous tests.

---------------------------------------------------------------------------

by fabpot at 2011/07/20 00:28:41 -0700

That does not make any difference for me. For instance, in `LocalizedRoutesAsPathTest::testLoginLogoutProcedure()`, the first request to `'/'.$locale.'/login'` returns the following Response:

    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <meta http-equiv="refresh" content="1;url=http://localhost/login" />
        </head>
        <body>
            Redirecting to <a href="http://localhost/login">http://localhost/login</a>.
        </body>
    </html>

---------------------------------------------------------------------------

by schmittjoh at 2011/07/20 00:31:34 -0700

That's weird, did you make sure that the temporary directory does not exist?

``rm -Rf /tmp/StandardFormLogin/``

On Wed, Jul 20, 2011 at 9:28 AM, fabpot <
reply@reply.github.com>wrote:

> That does not make any difference for me. For instance, in
> `LocalizedRoutesAsPathTest::testLoginLogoutProcedure()`, the first request
> to `'/'.$locale.'/login'` returns the following Response:
>
>    <html>
>        <head>
>            <meta http-equiv="Content-Type" content="text/html;
> charset=utf-8" />
>            <meta http-equiv="refresh" content="1;url=
> http://localhost/login" />
>        </head>
>        <body>
>            Redirecting to <a href="http://localhost/login">
> http://localhost/login</a>.
>        </body>
>    </html>
>
> --
> Reply to this email directly or view it on GitHub:
> https://github.com/symfony/symfony/pull/1739#issuecomment-1613504
>

---------------------------------------------------------------------------

by fabpot at 2011/07/20 00:33:40 -0700

Yes, I've just checked and the directory does not exist.

---------------------------------------------------------------------------

by schmittjoh at 2011/07/20 00:39:55 -0700

Sorry, I can't reproduce it on Ubuntu and unless someone wants to sponsor me a Mac, there is not much I can do.
Fabien Potencier 14 年之前
父節點
當前提交
aab0bf7e2c
共有 23 個文件被更改,包括 403 次插入46 次删除
  1. 3 0
      UPDATE.md
  2. 15 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php
  3. 51 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php
  4. 6 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php
  5. 30 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/localized_routing.yml
  6. 11 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml
  7. 21 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig
  8. 28 8
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php
  9. 71 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php
  10. 24 7
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
  11. 1 1
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
  12. 28 4
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php
  13. 2 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
  14. 26 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml
  15. 9 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml
  16. 13 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml
  17. 3 0
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routing.yml
  18. 1 2
      src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml
  19. 1 5
      src/Symfony/Component/HttpFoundation/RequestMatcher.php
  20. 5 0
      src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php
  21. 42 15
      src/Symfony/Component/Security/Http/HttpUtils.php
  22. 1 4
      tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php
  23. 11 0
      tests/Symfony/Tests/Component/Security/Http/HttpUtilsTest.php

+ 3 - 0
UPDATE.md

@@ -84,6 +84,9 @@ RC4 to RC5
         Session::getAttributes() -> Session::all()
         Session::setAttributes() -> Session::replace()
 
+* {_locale} is not supported in paths in the access_control section anymore. You can
+  rewrite the paths using a regular expression such as "(?:[a-z]{2})".
+
 RC3 to RC4
 ----------
 

+ 15 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
+
+class AuthenticationCommencingTest extends WebTestCase
+{
+    public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped()
+    {
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'config.yml'));
+        $client->insulate();
+
+        $client->request('GET', '/secure-but-not-covered-by-access-control');
+        $this->assertRedirect($client->getResponse(), '/login');
+    }
+}

+ 51 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller;
+
+use Symfony\Component\Security\Core\SecurityContext;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\DependencyInjection\ContainerAware;
+
+class LocalizedController extends ContainerAware
+{
+    public function loginAction()
+    {
+        // get the login error if there is one
+        if ($this->container->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
+            $error = $this->container->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
+        } else {
+            $error = $this->container->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
+        }
+
+        return $this->container->get('templating')->renderResponse('FormLoginBundle:Localized:login.html.twig', array(
+            // last username entered by the user
+            'last_username' => $this->container->get('request')->getSession()->get(SecurityContext::LAST_USERNAME),
+            'error'         => $error,
+        ));
+    }
+
+    public function loginCheckAction()
+    {
+        throw new \RuntimeException('loginCheckAction() should never be called.');
+    }
+
+    public function logoutAction()
+    {
+        throw new \RuntimeException('logoutAction() should never be called.');
+    }
+
+    public function secureAction()
+    {
+        throw new \RuntimeException('secureAction() should never be called.');
+    }
+
+    public function profileAction()
+    {
+        return new Response('Profile');
+    }
+
+    public function homepageAction()
+    {
+        return new Response('Homepage');
+    }
+}

+ 6 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php

@@ -11,6 +11,7 @@
 
 namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller;
 
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Security\Core\SecurityContext;
 use Symfony\Component\DependencyInjection\ContainerAware;
@@ -42,4 +43,9 @@ class LoginController extends ContainerAware
     {
         return new Response('', 400);
     }
+
+    public function secureAction()
+    {
+        throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException()));
+    }
 }

+ 30 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/localized_routing.yml

@@ -0,0 +1,30 @@
+localized_login_path:
+    pattern: /{_locale}/login
+    defaults: { _controller: FormLoginBundle:Localized:login }
+    requirements: { _locale: "^[a-z]{2}$" }
+
+localized_check_path:
+    pattern: /{_locale}/login_check
+    defaults: { _controller: FormLoginBundle:Localized:loginCheck }
+    requirements: { _locale: "^[a-z]{2}$" }
+    
+localized_default_target_path:
+    pattern: /{_locale}/profile
+    defaults: { _controller: FormLoginBundle:Localized:profile }
+    requirements: { _locale: "^[a-z]{2}$" }
+
+localized_logout_path:
+    pattern: /{_locale}/logout
+    defaults: { _controller: FormLoginBundle:Localized:logout }
+    requirements: { _locale: "^[a-z]{2}$" }
+    
+localized_logout_target_path:
+    pattern: /{_locale}/
+    defaults: { _controller: FormLoginBundle:Localized:homepage }
+    requirements: { _locale: "^[a-z]{2}$" }
+    
+localized_secure_path:
+    pattern: /{_locale}/secure/
+    defaults: { _controller: FormLoginBundle:Localized:secure }
+    requirements: { _locale: "^[a-z]{2}$" }
+    

+ 11 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml

@@ -14,6 +14,17 @@ form_login_custom_target_path:
     pattern: /foo
     defaults: { _controller: FormLoginBundle:Login:afterLogin }
 
+form_login_default_target_path:
+    pattern: /profile
+    defaults: { _controller: FormLoginBundle:Login:afterLogin }
+
 form_login_redirect_to_protected_resource_after_login:
     pattern: /protected-resource
     defaults: { _controller: FormLoginBundle:Login:afterLogin }
+
+form_logout:
+    pattern: /logout_path
+
+form_secure_action:
+    pattern: /secure-but-not-covered-by-access-control
+    defaults: { _controller: FormLoginBundle:Login:secure }

+ 21 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig

@@ -0,0 +1,21 @@
+{% extends "::base.html.twig" %}
+
+{% block body %}
+
+    {% if error %}
+        <div>{{ error.message }}</div>
+    {% endif %}
+
+    <form action="{{ path('localized_check_path') }}" method="post">
+        <label for="username">Username:</label>
+        <input type="text" id="username" name="_username" value="{{ last_username }}" />
+
+        <label for="password">Password:</label>
+        <input type="password" id="password" name="_password" />
+
+        <input type="hidden" name="_target_path" value="" />
+
+        <input type="submit" name="login" />
+    </form>
+
+{% endblock %}

+ 28 - 8
src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php

@@ -16,25 +16,33 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
  */
 class FormLoginTest extends WebTestCase
 {
-    public function testFormLogin()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testFormLogin($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
 
         $form = $client->request('GET', '/login')->selectButton('login')->form();
         $form['_username'] = 'johannes';
         $form['_password'] = 'test';
         $client->submit($form);
 
-        $this->assertRedirect($client->getResponse(), '/');
+        $this->assertRedirect($client->getResponse(), '/profile');
 
         $text = $client->followRedirect()->text();
         $this->assertContains('Hello johannes!', $text);
-        $this->assertContains('You\'re browsing to path "/".', $text);
+        $this->assertContains('You\'re browsing to path "/profile".', $text);
     }
 
-    public function testFormLoginWithCustomTargetPath()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testFormLoginWithCustomTargetPath($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
 
         $form = $client->request('GET', '/login')->selectButton('login')->form();
         $form['_username'] = 'johannes';
@@ -49,9 +57,13 @@ class FormLoginTest extends WebTestCase
         $this->assertContains('You\'re browsing to path "/foo".', $text);
     }
 
-    public function testFormLoginRedirectsToProtectedResourceAfterLogin()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
 
         $client->request('GET', '/protected-resource');
         $this->assertRedirect($client->getResponse(), '/login');
@@ -67,6 +79,14 @@ class FormLoginTest extends WebTestCase
         $this->assertContains('You\'re browsing to path "/protected-resource".', $text);
     }
 
+    public function getConfigs()
+    {
+        return array(
+            array('config.yml'),
+            array('routes_as_path.yml'),
+        );
+    }
+
     protected function setUp()
     {
         parent::setUp();

+ 71 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
+
+class LocalizedRoutesAsPathTest extends WebTestCase
+{
+    /**
+     * @dataProvider getLocales
+     */
+    public function testLoginLogoutProcedure($locale)
+    {
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml'));
+        $client->insulate();
+
+        $crawler = $client->request('GET', '/'.$locale.'/login');
+        $form = $crawler->selectButton('login')->form();
+        $form['_username'] = 'johannes';
+        $form['_password'] = 'test';
+        $client->submit($form);
+
+        $this->assertRedirect($client->getResponse(), '/'.$locale.'/profile');
+        $this->assertEquals('Profile', $client->followRedirect()->text());
+
+        $client->request('GET', '/'.$locale.'/logout');
+        $this->assertRedirect($client->getResponse(), '/'.$locale.'/');
+        $this->assertEquals('Homepage', $client->followRedirect()->text());
+    }
+
+    /**
+     * @dataProvider getLocales
+     */
+    public function testAccessRestrictedResource($locale)
+    {
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml'));
+        $client->insulate();
+
+        $client->request('GET', '/'.$locale.'/secure/');
+        $this->assertRedirect($client->getResponse(), '/'.$locale.'/login');
+    }
+
+    /**
+     * @dataProvider getLocales
+     */
+    public function testAccessRestrictedResourceWithForward($locale)
+    {
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes_with_forward.yml'));
+        $client->insulate();
+
+        $crawler = $client->request('GET', '/'.$locale.'/secure/');
+        $this->assertEquals(1, count($crawler->selectButton('login')), (string) $client->getResponse());
+    }
+
+    public function getLocales()
+    {
+        return array(array('en'), array('de'));
+    }
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->deleteTmpDir('StandardFormLogin');
+    }
+
+    protected function tearDown()
+    {
+        parent::setUp();
+
+        $this->deleteTmpDir('StandardFormLogin');
+    }
+}

+ 24 - 7
src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php

@@ -13,25 +13,37 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
 
 class SecurityRoutingIntegrationTest extends WebTestCase
 {
-    public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
         $client->request('GET', '/protected_resource');
 
         $this->assertRedirect($client->getResponse(), '/login');
     }
 
-    public function testRoutingErrorIsExposedWhenNotProtected()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testRoutingErrorIsExposedWhenNotProtected($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
         $client->request('GET', '/unprotected_resource');
 
-        $this->assertEquals(404, $client->getResponse()->getStatusCode());
+        $this->assertEquals(404, $client->getResponse()->getStatusCode(), (string) $client->getResponse());
     }
 
-    public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights()
+    /**
+     * @dataProvider getConfigs
+     */
+    public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights($config)
     {
-        $client = $this->createClient(array('test_case' => 'StandardFormLogin'));
+        $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
+        $client->insulate();
 
         $form = $client->request('GET', '/login')->selectButton('login')->form();
         $form['_username'] = 'johannes';
@@ -43,6 +55,11 @@ class SecurityRoutingIntegrationTest extends WebTestCase
         $this->assertNotEquals(404, $client->getResponse()->getStatusCode());
     }
 
+    public function getConfigs()
+    {
+        return array(array('config.yml'), array('routes_as_path.yml'));
+    }
+
     protected function setUp()
     {
         parent::setUp();

+ 1 - 1
src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php

@@ -18,7 +18,7 @@ class WebTestCase extends BaseWebTestCase
 {
     static public function assertRedirect($response, $location)
     {
-        self::assertTrue($response->isRedirect());
+        self::assertTrue($response->isRedirect(), 'Response is not a redirect, got status code: '.$response->getStatusCode());
         self::assertEquals('http://localhost'.$location, $response->headers->get('Location'));
     }
 

+ 28 - 4
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php

@@ -11,8 +11,21 @@
 
 namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
 
-use Symfony\Component\HttpKernel\Util\Filesystem;
+// get the autoload file
+$dir = __DIR__;
+$lastDir = null;
+while ($dir !== $lastDir) {
+    $lastDir = $dir;
+
+    if (file_exists($dir.'/autoload.php.dist')) {
+        require_once $dir.'/autoload.php.dist';
+        break;
+    }
+
+    $dir = dirname($dir);
+}
 
+use Symfony\Component\HttpKernel\Util\Filesystem;
 use Symfony\Component\Config\Loader\LoaderInterface;
 use Symfony\Component\HttpKernel\Kernel;
 
@@ -33,10 +46,11 @@ class AppKernel extends Kernel
         }
         $this->testCase = $testCase;
 
-        if (!file_exists($filename = __DIR__.'/'.$testCase.'/'.$rootConfig)) {
-            throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $filename));
+        $fs = new Filesystem();
+        if (!$fs->isAbsolutePath($rootConfig) && !file_exists($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) {
+            throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig));
         }
-        $this->rootConfig = $filename;
+        $this->rootConfig = $rootConfig;
 
         parent::__construct($environment, $debug);
     }
@@ -74,6 +88,16 @@ class AppKernel extends Kernel
         $loader->load($this->rootConfig);
     }
 
+    public function serialize()
+    {
+        return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug()));
+    }
+
+    public function unserialize($str)
+    {
+        call_user_func_array(array($this, '__construct'), unserialize($str));
+    }
+
     protected function getKernelParameters()
     {
         $parameters = parent::getKernelParameters();

+ 2 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml

@@ -21,9 +21,11 @@ security:
         default:
             form_login:
                 check_path: /login_check
+                default_target_path: /profile
             anonymous: ~
 
     access_control:
         - { path: ^/unprotected_resource$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/secure-but-not-covered-by-access-control$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/highly_protected_resource$, roles: IS_ADMIN }
         - { path: .*, roles: IS_AUTHENTICATED_FULLY }

+ 26 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml

@@ -0,0 +1,26 @@
+imports:
+    - { resource: ./../config/default.yml }
+    
+security:
+    encoders:
+        Symfony\Component\Security\Core\User\User: plaintext
+
+    providers:
+        in_memory:
+            users:
+                johannes: { password: test, roles: [ROLE_USER] }
+                
+    firewalls:
+        default:
+            form_login:
+                login_path: localized_login_path
+                check_path: localized_check_path
+                default_target_path: localized_default_target_path
+            logout:
+                path: localized_logout_path
+                target: localized_logout_target_path
+            anonymous: ~
+            
+    access_control:
+        - { path: '^/(?:[a-z]{2})/secure/.*', roles: ROLE_USER }       
+    

+ 9 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml

@@ -0,0 +1,9 @@
+imports:
+    - { resource: ./localized_routes.yml }
+    
+security:
+    firewalls: 
+        default:
+            form_login:
+                use_forward: true
+                failure_forward: true

+ 13 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml

@@ -0,0 +1,13 @@
+imports:
+    - { resource: ./config.yml }
+    
+security:
+    firewalls: 
+        default:
+            form_login:
+                login_path: form_login
+                check_path: form_login_check
+                default_target_path: form_login_default_target_path
+            logout:
+                path: form_logout
+                target: form_login_homepage

+ 3 - 0
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routing.yml

@@ -1,2 +1,5 @@
 _form_login_bundle:
     resource: @FormLoginBundle/Resources/config/routing.yml
+
+_form_login_localized:
+    resource: @FormLoginBundle/Resources/config/localized_routing.yml

+ 1 - 2
src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml

@@ -9,9 +9,8 @@ framework:
     test: ~
     session:
         default_locale: en
-        lifetime:       3600
         auto_start:     true
-        storage_id: session.storage.filesystem
+        storage_id:     session.storage.filesystem
 
 services:
     logger: { class: Symfony\Component\HttpKernel\Log\NullLogger }

+ 1 - 5
src/Symfony/Component/HttpFoundation/RequestMatcher.php

@@ -104,11 +104,7 @@ class RequestMatcher implements RequestMatcherInterface
         }
 
         if (null !== $this->path) {
-            if (null !== $session = $request->getSession()) {
-                $path = strtr($this->path, array('{_locale}' => $session->getLocale(), '#' => '\\#'));
-            } else {
-                $path = str_replace('#', '\\#', $this->path);
-            }
+            $path = str_replace('#', '\\#', $this->path);
 
             if (!preg_match('#'.$path.'#', $request->getPathInfo())) {
                 return false;

+ 5 - 0
src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php

@@ -76,6 +76,11 @@ class ExceptionListener
         $exception = $event->getException();
         $request = $event->getRequest();
 
+        // determine the actual cause for the exception
+        while (null !== $previous = $exception->getPrevious()) {
+            $exception = $previous;
+        }
+
         if ($exception instanceof AuthenticationException) {
             if (null !== $this->logger) {
                 $this->logger->info(sprintf('Authentication exception occurred; redirecting to authentication entry point (%s)', $exception->getMessage()));

+ 42 - 15
src/Symfony/Component/Security/Http/HttpUtils.php

@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Security\Http;
 
+use Symfony\Component\Security\Core\SecurityContextInterface;
+
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\Routing\RouterInterface;
@@ -45,22 +47,10 @@ class HttpUtils
      */
     public function createRedirectResponse(Request $request, $path, $status = 302)
     {
-        if (0 === strpos($path, '/')) {
+        if ('/' === $path[0]) {
             $path = $request->getUriForPath($path);
         } elseif (0 !== strpos($path, 'http')) {
-            // hack (don't have a better solution for now)
-            $context = $this->router->getContext();
-            try {
-                $parameters = $this->router->match($request->getPathInfo());
-            } catch (\Exception $e) {
-            }
-
-            if (isset($parameters['_locale'])) {
-                $context->setParameter('_locale', $parameters['_locale']);
-            } elseif ($session = $request->getSession()) {
-                $context->setParameter('_locale', $session->getLocale());
-            }
-
+            $this->resetLocale($request);
             $path = $this->generateUrl($path, true);
         }
 
@@ -78,10 +68,26 @@ class HttpUtils
     public function createRequest(Request $request, $path)
     {
         if ($path && '/' !== $path[0] && 0 !== strpos($path, 'http')) {
+            $this->resetLocale($request);
             $path = $this->generateUrl($path, true);
         }
 
-        return Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all());
+        $newRequest = Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all());
+        if ($session = $request->getSession()) {
+            $newRequest->setSession($session);
+        }
+
+        if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
+            $newRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR));
+        }
+        if ($request->attributes->has(SecurityContextInterface::ACCESS_DENIED_ERROR)) {
+            $newRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $request->attributes->get(SecurityContextInterface::ACCESS_DENIED_ERROR));
+        }
+        if ($request->attributes->has(SecurityContextInterface::LAST_USERNAME)) {
+            $newRequest->attributes->set(SecurityContextInterface::LAST_USERNAME, $request->attributes->get(SecurityContextInterface::LAST_USERNAME));
+        }
+
+        return $newRequest;
     }
 
     /**
@@ -107,6 +113,27 @@ class HttpUtils
         return $path === $request->getPathInfo();
     }
 
+    // hack (don't have a better solution for now)
+    private function resetLocale(Request $request)
+    {
+        $context = $this->router->getContext();
+        if ($context->getParameter('_locale')) {
+            return;
+        }
+
+        try {
+            $parameters = $this->router->match($request->getPathInfo());
+
+            if (isset($parameters['_locale'])) {
+                $context->setParameter('_locale', $parameters['_locale']);
+            } elseif ($session = $request->getSession()) {
+                $context->setParameter('_locale', $session->getLocale());
+            }
+        } catch (\Exception $e) {
+            // let's hope user doesn't use the locale in the path
+        }
+    }
+
     private function generateUrl($route, $absolute = false)
     {
         if (null === $this->router) {

+ 1 - 4
tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php

@@ -142,7 +142,7 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
         $this->assertFalse($matcher->matches($request));
     }
 
-    public function testPathWithLocale()
+    public function testPathWithLocaleIsNotSupported()
     {
         $matcher = new RequestMatcher();
         $request = Request::create('/en/login');
@@ -152,9 +152,6 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
         $request->setSession($session);
 
         $matcher->matchPath('^/{_locale}/login$');
-        $this->assertTrue($matcher->matches($request));
-
-        $session->setLocale('de');
         $this->assertFalse($matcher->matches($request));
     }
 

+ 11 - 0
tests/Symfony/Tests/Component/Security/Http/HttpUtilsTest.php

@@ -61,6 +61,17 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('bar', $subRequest->server->get('Foo'));
 
         // route name
+        $utils = new HttpUtils($router = $this->getMockBuilder('Symfony\Component\Routing\Router')->disableOriginalConstructor()->getMock());
+        $router
+            ->expects($this->once())
+            ->method('generate')
+            ->will($this->returnValue('/foo/bar'))
+        ;
+        $router
+            ->expects($this->any())
+            ->method('getContext')
+            ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')))
+        ;
         $subRequest = $utils->createRequest($this->getRequest(), 'foobar');
         $this->assertEquals('/foo/bar', $subRequest->getPathInfo());