/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php
475 строк
18 KB
Nicolas Grekas
Merge branch '6.4' into 7.4
13 май 2026, 15:04
13 май 2026, 15:04
288306d
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Security\Http\Tests; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\SecurityRequestAttributes; class HttpUtilsTest extends TestCase { public function testCreateRedirectResponseWithPath() { $utils = new HttpUtils($this->getUrlGenerator()); $response = $utils->createRedirectResponse($this->getRequest(), '/foobar'); $this->assertTrue($response->isRedirect('http://localhost/foobar')); $this->assertEquals(302, $response->getStatusCode()); } public function testCreateRedirectResponseWithAbsoluteUrl() { $utils = new HttpUtils($this->getUrlGenerator()); $response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/'); $this->assertTrue($response->isRedirect('http://symfony.com/')); } public function testCreateRedirectResponseWithDomainRegexp() { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://symfony\.com$#i'); $response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/blog'); $this->assertTrue($response->isRedirect('http://symfony.com/blog')); } public function testCreateRedirectResponseWithRequestsDomain() { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i'); $response = $utils->createRedirectResponse($this->getRequest(), 'http://localhost/blog'); $this->assertTrue($response->isRedirect('http://localhost/blog')); } #[DataProvider('validRequestDomainUrls')] public function testCreateRedirectResponse(?string $domainRegexp, string $path, string $expectedRedirectUri) { $utils = new HttpUtils($this->getUrlGenerator(), null, $domainRegexp); $response = $utils->createRedirectResponse($this->getRequest(), $path); $this->assertTrue($response->isRedirect($expectedRedirectUri)); $this->assertEquals(302, $response->getStatusCode()); } public static function validRequestDomainUrls() { return [ '/foobar' => [ null, '/foobar', 'http://localhost/foobar', ], 'http://symfony.com/ without domain regex' => [ null, 'http://symfony.com/', 'http://symfony.com/', ], 'http://localhost/blog with #^https?://symfony\.com$#i' => [ '#^https?://symfony\.com$#i', 'http://symfony.com/blog', 'http://symfony.com/blog', ], 'http://localhost/blog with #^https?://%s$#i' => [ '#^https?://%s$#i', 'http://localhost/blog', 'http://localhost/blog', ], 'custom scheme' => [ null, 'android-app://com.google.android.gm/', 'android-app://com.google.android.gm/', ], 'custom scheme with all URL components' => [ null, 'android-app://foo:bar@www.example.com:8080/software/index.html?lite=true#section1', 'android-app://foo:bar@www.example.com:8080/software/index.html?lite=true#section1', ], ]; } #[DataProvider('badRequestDomainUrls')] public function testCreateRedirectResponseWithBadRequestsDomain($url) { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i'); $response = $utils->createRedirectResponse($this->getRequest(), $url); $this->assertTrue($response->isRedirect('http://localhost/')); } public static function badRequestDomainUrls() { return [ ['http://pirate.net/foo'], ['http:\\\\pirate.net/foo'], ['http:/\\pirate.net/foo'], ['http:\\/pirate.net/foo'], ['http://////pirate.net/foo'], ['http:///foo'], ]; } public function testCreateRedirectResponseWithProtocolRelativeTarget() { $utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i'); $response = $utils->createRedirectResponse($this->getRequest(), '//evil.com/do-bad-things'); $this->assertTrue($response->isRedirect('http://localhost//evil.com/do-bad-things'), 'Protocol-relative redirection should not be supported for security reasons'); } public function testCreateRedirectResponseWithRouteName() { $utils = new HttpUtils($urlGenerator = $this->createMock(UrlGeneratorInterface::class)); $urlGenerator ->expects($this->once()) ->method('generate') ->with('foobar', [], UrlGeneratorInterface::ABSOLUTE_URL) ->willReturn('http://localhost/foo/bar') ; $urlGenerator ->method('getContext') ->willReturn(new RequestContext()) ; $response = $utils->createRedirectResponse($this->getRequest(), 'foobar'); $this->assertTrue($response->isRedirect('http://localhost/foo/bar')); } public function testCreateRequestWithPath() { $request = $this->getRequest(); $request->server->set('Foo', 'bar'); $utils = new HttpUtils($this->getUrlGenerator()); $subRequest = $utils->createRequest($request, '/foobar'); $this->assertEquals('GET', $subRequest->getMethod()); $this->assertEquals('/foobar', $subRequest->getPathInfo()); $this->assertEquals('bar', $subRequest->server->get('Foo')); } public function testCreateRequestWithRouteName() { $utils = new HttpUtils($urlGenerator = $this->createMock(UrlGeneratorInterface::class)); $urlGenerator ->expects($this->once()) ->method('generate') ->willReturn('/foo/bar') ; $urlGenerator ->method('getContext') ->willReturn(new RequestContext()) ; $subRequest = $utils->createRequest($this->getRequest(), 'foobar'); $this->assertEquals('/foo/bar', $subRequest->getPathInfo()); } public function testCreateRequestWithAbsoluteUrl() { $utils = new HttpUtils($this->createStub(UrlGeneratorInterface::class)); $subRequest = $utils->createRequest($this->getRequest(), 'http://symfony.com/'); $this->assertEquals('/', $subRequest->getPathInfo()); } public function testCreateRequestPassesSessionToTheNewRequest() { $request = $this->getRequest(); $request->setSession($session = new Session(new MockArraySessionStorage())); $utils = new HttpUtils($this->getUrlGenerator()); $subRequest = $utils->createRequest($request, '/foobar'); $this->assertSame($session, $subRequest->getSession()); } #[DataProvider('provideSecurityRequestAttributes')] public function testCreateRequestPassesSecurityRequestAttributesToTheNewRequest($attribute) { $request = $this->getRequest(); $request->attributes->set($attribute, 'foo'); $utils = new HttpUtils($this->getUrlGenerator()); $subRequest = $utils->createRequest($request, '/foobar'); $this->assertSame('foo', $subRequest->attributes->get($attribute)); } public static function provideSecurityRequestAttributes() { return [ [SecurityRequestAttributes::AUTHENTICATION_ERROR], [SecurityRequestAttributes::ACCESS_DENIED_ERROR], [SecurityRequestAttributes::LAST_USERNAME], ]; } public function testCreateRequestFromPathHandlesTrustedHeaders() { Request::setTrustedProxies(['127.0.0.1'], Request::HEADER_X_FORWARDED_PREFIX); $this->assertSame( 'http://localhost/foo/', (new HttpUtils())->createRequest(Request::create('/', 'GET', [], [], [], ['HTTP_X_FORWARDED_PREFIX' => '/foo']), '/')->getUri(), ); } public function testCreateRequestFromRouteHandlesTrustedHeaders() { Request::setTrustedProxies(['127.0.0.1'], Request::HEADER_X_FORWARDED_PREFIX); $request = Request::create('/', 'GET', [], [], [], ['HTTP_X_FORWARDED_PREFIX' => '/foo']); $urlGenerator = new UrlGenerator( $routeCollection = new RouteCollection(), (new RequestContext())->fromRequest($request), ); $routeCollection->add('root', new Route('/')); $this->assertSame( 'http://localhost/foo/', (new HttpUtils($urlGenerator))->createRequest($request, 'root')->getUri(), ); } public function testCreateRequestFromRoutePreservesScriptNameBaseUrl() { // Sub-directory install (Apache "Alias /myapp /var/www/myapp/public" + mod_rewrite). // The master request's base URL comes from SCRIPT_NAME, NOT from X-Forwarded-Prefix. // The sub-request created for a `form_login.use_forward` login MUST inherit that base // URL so the URL generator (re-initialized from the sub-request via // RouterListener::onKernelRequest) emits form action URLs prefixed with `/myapp`. $server = [ 'REQUEST_URI' => '/myapp/', 'SCRIPT_NAME' => '/myapp/index.php', 'PHP_SELF' => '/myapp/index.php', 'SCRIPT_FILENAME' => '/var/www/myapp/public/index.php', ]; $request = new Request([], [], [], [], [], $server); $this->assertSame('/myapp', $request->getBaseUrl()); $urlGenerator = new UrlGenerator( $routeCollection = new RouteCollection(), (new RequestContext())->fromRequest($request), ); $routeCollection->add('app_login', new Route('/login')); $subRequest = (new HttpUtils($urlGenerator))->createRequest($request, 'app_login'); $this->assertSame('/myapp', $subRequest->getBaseUrl()); $this->assertSame('http://localhost/myapp/login', $subRequest->getUri()); } public function testCreateRequestFromRouteBehindProxyPreservesScriptNameBaseUrl() { // Sub-directory install (Apache "Alias /myapp …") behind a trusted proxy adding // an extra prefix: getBaseUrl() === "/proxy-prefix" + "/myapp". Only the // "/proxy-prefix" part may be dropped from the generated sub-request URI; the // "/myapp" part stays so the sub-request re-detects it from SCRIPT_NAME, and the // proxy prefix is re-added (not doubled) once the sub-request is processed. Request::setTrustedProxies(['127.0.0.1'], Request::HEADER_X_FORWARDED_PREFIX); $server = [ 'REQUEST_URI' => '/myapp/', 'SCRIPT_NAME' => '/myapp/index.php', 'PHP_SELF' => '/myapp/index.php', 'SCRIPT_FILENAME' => '/var/www/myapp/public/index.php', 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_PREFIX' => '/proxy-prefix', ]; $request = new Request([], [], [], [], [], $server); $this->assertSame('/proxy-prefix/myapp', $request->getBaseUrl()); $urlGenerator = new UrlGenerator( $routeCollection = new RouteCollection(), (new RequestContext())->fromRequest($request), ); $routeCollection->add('app_login', new Route('/login')); $subRequest = (new HttpUtils($urlGenerator))->createRequest($request, 'app_login'); $this->assertSame('/proxy-prefix/myapp', $subRequest->getBaseUrl()); $this->assertSame('http://localhost/proxy-prefix/myapp/login', $subRequest->getUri()); } public function testCheckRequestPath() { $utils = new HttpUtils($this->getUrlGenerator()); $this->assertTrue($utils->checkRequestPath($this->getRequest(), '/')); $this->assertFalse($utils->checkRequestPath($this->getRequest(), '/foo')); $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo%20bar'), '/foo bar')); // Plus must not decoded to space $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo+bar'), '/foo+bar')); // Checking unicode $this->assertTrue($utils->checkRequestPath($this->getRequest('/'.urlencode('вход')), '/вход')); } public function testCheckRequestPathWithUrlMatcherAndResourceNotFound() { $urlMatcher = $this->createMock(UrlMatcherInterface::class); $urlMatcher ->expects($this->once()) ->method('match') ->with('/') ->willThrowException(new ResourceNotFoundException()) ; $utils = new HttpUtils(null, $urlMatcher); $this->assertFalse($utils->checkRequestPath($this->getRequest(), 'foobar')); } public function testCheckRequestPathWithUrlMatcherAndMethodNotAllowed() { $request = $this->getRequest(); $urlMatcher = $this->createMock(RequestMatcherInterface::class); $urlMatcher ->expects($this->once()) ->method('matchRequest') ->with($request) ->willThrowException(new MethodNotAllowedException([])) ; $utils = new HttpUtils(null, $urlMatcher); $this->assertFalse($utils->checkRequestPath($request, 'foobar')); } public function testCheckRequestPathWithUrlMatcherAndResourceFoundByUrl() { $urlMatcher = $this->createMock(UrlMatcherInterface::class); $urlMatcher ->expects($this->once()) ->method('match') ->with('/foo/bar') ->willReturn(['_route' => 'foobar']) ; $utils = new HttpUtils(null, $urlMatcher); $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo/bar'), 'foobar')); } public function testCheckRequestPathWithUrlMatcherAndResourceFoundByRequest() { $request = $this->getRequest(); $urlMatcher = $this->createMock(RequestMatcherInterface::class); $urlMatcher ->expects($this->once()) ->method('matchRequest') ->with($request) ->willReturn(['_route' => 'foobar']) ; $utils = new HttpUtils(null, $urlMatcher); $this->assertTrue($utils->checkRequestPath($request, 'foobar')); } public function testCheckRequestPathWithUrlMatcherLoadingException() { $urlMatcher = $this->createStub(UrlMatcherInterface::class); $urlMatcher ->method('match') ->willThrowException(new \RuntimeException()) ; $utils = new HttpUtils(null, $urlMatcher); $this->expectException(\RuntimeException::class); $utils->checkRequestPath($this->getRequest(), 'foobar'); } public function testCheckRequestPathWithRequestAlreadyMatchedBefore() { $urlMatcher = $this->createMock(RequestMatcherInterface::class); $urlMatcher ->expects($this->never()) ->method('matchRequest') ; $request = $this->getRequest(); $request->attributes->set('_route', 'route_name'); $utils = new HttpUtils(null, $urlMatcher); $this->assertTrue($utils->checkRequestPath($request, 'route_name')); $this->assertFalse($utils->checkRequestPath($request, 'foobar')); } public function testCheckPathWithoutRouteParam() { $urlMatcher = $this->createStub(UrlMatcherInterface::class); $urlMatcher ->method('match') ->willReturn(['_controller' => 'PathController']) ; $utils = new HttpUtils(null, $urlMatcher); $this->assertFalse($utils->checkRequestPath($this->getRequest(), 'path/index.html')); } public function testGenerateUriRemovesQueryString() { $utils = new HttpUtils($this->getUrlGenerator('/foo/bar')); $this->assertEquals('/foo/bar', $utils->generateUri(new Request(), 'route_name')); $utils = new HttpUtils($this->getUrlGenerator('/foo/bar?param=value')); $this->assertEquals('/foo/bar', $utils->generateUri(new Request(), 'route_name')); } public function testGenerateUriPreservesFragment() { $utils = new HttpUtils($this->getUrlGenerator('/foo/bar?param=value#fragment')); $this->assertEquals('/foo/bar#fragment', $utils->generateUri(new Request(), 'route_name')); $utils = new HttpUtils($this->getUrlGenerator('/foo/bar#fragment')); $this->assertEquals('/foo/bar#fragment', $utils->generateUri(new Request(), 'route_name')); } public function testUrlGeneratorIsRequiredToGenerateUrl() { $this->expectException(\LogicException::class); $this->expectExceptionMessage('You must provide a UrlGeneratorInterface instance to be able to use routes.'); (new HttpUtils())->generateUri(new Request(), 'route_name'); } private function getUrlGenerator($generatedUrl = '/foo/bar') { $urlGenerator = $this->createStub(UrlGeneratorInterface::class); $urlGenerator ->method('generate') ->willReturn($generatedUrl) ; return $urlGenerator; } private function getRequest($path = '/') { return Request::create($path, 'get'); } }