merged branch jakzal/2.3-more-security-tests (PR #8177)

This PR was merged into the 2.3 branch.

Discussion
----------

[Security] Added more tests

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Continuation of #8136

Commits
-------

5409852 [Security] Added few new test cases for the HttpUtils and improved readability of existing tests.
d6ab77e [Security] Added tests for the SwitchUserListener.
cccd005 [Security] Added tests for the ContextListener.
307bc91 [Security] Added a test to the BasicAuthenticationListener.
314f29a [Security] Removed an unnecessary call to sprintf() and added a test case.
This commit is contained in:
Fabien Potencier 2013-06-04 17:03:43 +02:00
commit e1f530eb7a
6 changed files with 412 additions and 29 deletions

View File

@ -49,7 +49,7 @@ class AnonymousAuthenticationListener implements ListenerInterface
$this->context->setToken(new AnonymousToken($this->key, 'anon.', array()));
if (null !== $this->logger) {
$this->logger->info(sprintf('Populated SecurityContext with an anonymous Token'));
$this->logger->info('Populated SecurityContext with an anonymous Token');
}
}
}

View File

@ -59,4 +59,21 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
$listener = new AnonymousAuthenticationListener($context, 'TheKey');
$listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false));
}
public function testHandledEventIsLogged()
{
if (!interface_exists('Psr\Log\LoggerInterface')) {
$this->markTestSkipped('The "LoggerInterface" is not available');
}
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('info')
->with('Populated SecurityContext with an anonymous Token')
;
$listener = new AnonymousAuthenticationListener($context, 'TheKey', $logger);
$listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false));
}
}

View File

@ -195,4 +195,18 @@ class BasicAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
$listener->handle($event);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $providerKey must not be empty
*/
public function testItRequiresProviderKey()
{
new BasicAuthenticationListener(
$this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'),
$this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'),
'',
$this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')
);
}
}

View File

@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Http\Firewall\ContextListener;
@ -48,6 +49,32 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
unset($this->securityContext);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $contextKey must not be empty
*/
public function testItRequiresContextKey()
{
new ContextListener(
$this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'),
array(),
''
);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage User provider "stdClass" must implement "Symfony\Component\Security\Core\User\UserProviderInterface
*/
public function testUserProvidersNeedToImplementAnInterface()
{
new ContextListener(
$this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'),
array(new \stdClass()),
'key123'
);
}
public function testOnKernelResponseWillAddSession()
{
$session = $this->runSessionOnKernelResponse(
@ -131,9 +158,7 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
->disableOriginalConstructor()
->getMock();
$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')
->disableOriginalConstructor()
->getMock();
$session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
$event->expects($this->any())
->method('getRequest')
@ -147,7 +172,7 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
$session->expects($this->any())
->method('get')
->with('_security_key123')
->will($this->returnValue(serialize($token)));
->will($this->returnValue($token));
$context->expects($this->once())
->method('setToken')
->with(null);
@ -159,11 +184,53 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
public function provideInvalidToken()
{
return array(
array(new \__PHP_Incomplete_Class()),
array(null),
array(serialize(new \__PHP_Incomplete_Class())),
array(serialize(null)),
array(null)
);
}
public function testHandleAddsKernelResponseListener()
{
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->getMock();
$listener = new ContextListener($context, array(), 'key123', null, $dispatcher);
$event->expects($this->any())
->method('getRequestType')
->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST));
$event->expects($this->any())
->method('getRequest')
->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Request')));
$dispatcher->expects($this->once())
->method('addListener')
->with(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
$listener->handle($event);
}
public function testHandleRemovesTokenIfNoPreviousSessionWasFound()
{
$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$request->expects($this->any())->method('hasPreviousSession')->will($this->returnValue(false));
$event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->any())->method('getRequest')->will($this->returnValue($request));
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$context->expects($this->once())->method('setToken')->with(null);
$listener = new ContextListener($context, array(), 'key123');
$listener->handle($event);
}
protected function runSessionOnKernelResponse($newToken, $original = null)
{
$session = new Session(new MockArraySessionStorage());
@ -189,4 +256,5 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
$listener->onKernelResponse($event);
return $session;
}}
}
}

View File

@ -0,0 +1,175 @@
<?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\Tests\Http\Firewall;
use Symfony\Component\Security\Http\Firewall\SwitchUserListener;
class SwitchUserListenerTest extends \PHPUnit_Framework_TestCase
{
private $securityContext;
private $userProvider;
private $userChecker;
private $accessDecisionManager;
private $request;
private $event;
protected function setUp()
{
if (!class_exists('Symfony\Component\HttpFoundation\Request')) {
$this->markTestSkipped('The "HttpFoundation" component is not available');
}
if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) {
$this->markTestSkipped('The "HttpKernel" component is not available');
}
$this->securityContext = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
$this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
$this->accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');
$this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$this->request->server = $this->getMock('Symfony\Component\HttpFoundation\ServerBag');
$this->event = $this->getEvent($this->request);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $providerKey must not be empty
*/
public function testProviderKeyIsRequired()
{
new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, '', $this->accessDecisionManager);
}
public function testEventIsIgnoredIfUsernameIsNotPassedWithTheRequest()
{
$this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue(null));
$this->event->expects($this->never())->method('setResopnse');
$this->securityContext->expects($this->never())->method('setToken');
$listener = new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
$listener->handle($this->event);
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
*/
public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBeFound()
{
$token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
$this->securityContext->expects($this->any())->method('getToken')->will($this->returnValue($token));
$this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit'));
$listener = new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
$listener->handle($this->event);
}
public function testExitUserUpdatesToken()
{
$originalToken = $this->getToken();
$role = $this->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole')
->disableOriginalConstructor()
->getMock();
$role->expects($this->any())->method('getSource')->will($this->returnValue($originalToken));
$this->securityContext->expects($this->any())
->method('getToken')
->will($this->returnValue($this->getToken(array($role))));
$this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit'));
$this->request->expects($this->any())->method('getUri')->will($this->returnValue('/'));
$this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', '');
$this->securityContext->expects($this->once())
->method('setToken')->with($originalToken);
$this->event->expects($this->once())
->method('setResponse')->with($this->isInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse'));
$listener = new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
$listener->handle($this->event);
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
*/
public function testSwitchUserIsDissallowed()
{
$token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
$this->securityContext->expects($this->any())->method('getToken')->will($this->returnValue($token));
$this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba'));
$this->accessDecisionManager->expects($this->once())
->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
->will($this->returnValue(false));
$listener = new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
$listener->handle($this->event);
}
public function testSwitchUser()
{
$token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
$user->expects($this->any())->method('getRoles')->will($this->returnValue(array()));
$this->securityContext->expects($this->any())->method('getToken')->will($this->returnValue($token));
$this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba'));
$this->request->expects($this->any())->method('getUri')->will($this->returnValue('/'));
$this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', '');
$this->accessDecisionManager->expects($this->once())
->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
->will($this->returnValue(true));
$this->userProvider->expects($this->once())
->method('loadUserByUsername')->with('kuba')
->will($this->returnValue($user));
$this->userChecker->expects($this->once())
->method('checkPostAuth')->with($user);
$this->securityContext->expects($this->once())
->method('setToken')->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken'));
$listener = new SwitchUserListener($this->securityContext, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
$listener->handle($this->event);
}
private function getEvent($request)
{
$event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->any())
->method('getRequest')
->will($this->returnValue($request));
return $event;
}
private function getToken(array $roles = array())
{
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$token->expects($this->any())
->method('getRoles')
->will($this->returnValue($roles));
return $token;
}
}

View File

@ -12,8 +12,10 @@
namespace Symfony\Component\Security\Tests\Http;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\HttpUtils;
class HttpUtilsTest extends \PHPUnit_Framework_TestCase
{
@ -28,21 +30,27 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
}
}
public function testCreateRedirectResponse()
public function testCreateRedirectResponseWithPath()
{
$utils = new HttpUtils($this->getUrlGenerator());
// absolute path
$response = $utils->createRedirectResponse($this->getRequest(), '/foobar');
$this->assertTrue($response->isRedirect('http://localhost/foobar'));
$this->assertEquals(302, $response->getStatusCode());
}
// absolute URL
public function testCreateRedirectResponseWithAbsoluteUrl()
{
$utils = new HttpUtils($this->getUrlGenerator());
$response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/');
$this->assertTrue($response->isRedirect('http://symfony.com/'));
// route name
$this->assertTrue($response->isRedirect('http://symfony.com/'));
}
public function testCreateRedirectResponseWithRouteName()
{
$utils = new HttpUtils($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
$urlGenerator
->expects($this->any())
->method('generate')
@ -54,25 +62,29 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
->method('getContext')
->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')))
;
$response = $utils->createRedirectResponse($this->getRequest(), 'foobar');
$this->assertTrue($response->isRedirect('http://localhost/foo/bar'));
}
public function testCreateRequest()
public function testCreateRequestWithPath()
{
$utils = new HttpUtils($this->getUrlGenerator());
// absolute path
$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'));
}
// route name
public function testCreateRequestWithRouteName()
{
$utils = new HttpUtils($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
$urlGenerator
->expects($this->once())
->method('generate')
@ -83,14 +95,54 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
->method('getContext')
->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')))
;
$subRequest = $utils->createRequest($this->getRequest(), 'foobar');
$this->assertEquals('/foo/bar', $subRequest->getPathInfo());
// absolute URL
$subRequest = $utils->createRequest($this->getRequest(), 'foobar');
$this->assertEquals('/foo/bar', $subRequest->getPathInfo());
}
public function testCreateRequestWithAbsoluteUrl()
{
$utils = new HttpUtils($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
$subRequest = $utils->createRequest($this->getRequest(), 'http://symfony.com/');
$this->assertEquals('/', $subRequest->getPathInfo());
}
public function testCreateRequestPassesSessionToTheNewRequest()
{
$request = $this->getRequest();
$request->setSession($session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
$utils = new HttpUtils($this->getUrlGenerator());
$subRequest = $utils->createRequest($request, '/foobar');
$this->assertSame($session, $subRequest->getSession());
}
/**
* @dataProvider provideSecurityContextAttributes
*/
public function testCreateRequestPassesSecurityContextAttributesToTheNewRequest($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 function provideSecurityContextAttributes()
{
return array(
array(SecurityContextInterface::AUTHENTICATION_ERROR),
array(SecurityContextInterface::ACCESS_DENIED_ERROR),
array(SecurityContextInterface::LAST_USERNAME)
);
}
public function testCheckRequestPath()
{
$utils = new HttpUtils($this->getUrlGenerator());
@ -102,26 +154,66 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($utils->checkRequestPath($this->getRequest('/foo+bar'), '/foo+bar'));
// Checking unicode
$this->assertTrue($utils->checkRequestPath($this->getRequest(urlencode('/вход')), '/вход'));
}
public function testCheckRequestPathWithUrlMatcherAndResourceNotFound()
{
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$urlMatcher
->expects($this->any())
->method('match')
->with('/')
->will($this->throwException(new ResourceNotFoundException()))
;
$utils = new HttpUtils(null, $urlMatcher);
$this->assertFalse($utils->checkRequestPath($this->getRequest(), 'foobar'));
}
public function testCheckRequestPathWithUrlMatcherAndMethodNotAllowed()
{
$request = $this->getRequest();
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$urlMatcher
->expects($this->any())
->method('matchRequest')
->with($request)
->will($this->throwException(new MethodNotAllowedException(array())))
;
$utils = new HttpUtils(null, $urlMatcher);
$this->assertFalse($utils->checkRequestPath($request, 'foobar'));
}
public function testCheckRequestPathWithUrlMatcherAndResourceFoundByUrl()
{
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$urlMatcher
->expects($this->any())
->method('match')
->with('/foo/bar')
->will($this->returnValue(array('_route' => 'foobar')))
;
$utils = new HttpUtils(null, $urlMatcher);
$this->assertTrue($utils->checkRequestPath($this->getRequest('/foo/bar'), 'foobar'));
}
public function testCheckRequestPathWithUrlMatcherAndResourceFoundByRequest()
{
$request = $this->getRequest();
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$urlMatcher
->expects($this->any())
->method('matchRequest')
->with($request)
->will($this->returnValue(array('_route' => 'foobar')))
;
$utils = new HttpUtils(null, $urlMatcher);
$this->assertTrue($utils->checkRequestPath($request, 'foobar'));
}
/**
* @expectedException \RuntimeException
*/
@ -133,20 +225,37 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
->method('match')
->will($this->throwException(new \RuntimeException()))
;
$utils = new HttpUtils(null, $urlMatcher);
$utils->checkRequestPath($this->getRequest(), 'foobar');
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Matcher must either implement UrlMatcherInterface or RequestMatcherInterface
*/
public function testUrlMatcher()
{
new HttpUtils($this->getUrlGenerator(), new \stdClass());
}
public function testGenerateUriRemovesQueryString()
{
$method = new \ReflectionMethod('Symfony\Component\Security\Http\HttpUtils', 'generateUri');
$method->setAccessible(true);
$utils = new HttpUtils($this->getUrlGenerator());
$this->assertEquals('/foo/bar', $method->invoke($utils, new Request(), 'route_name'));
$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', $method->invoke($utils, new Request(), 'route_name'));
$this->assertEquals('/foo/bar', $utils->generateUri(new Request(), 'route_name'));
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage You must provide a UrlGeneratorInterface instance to be able to use routes.
*/
public function testUrlGeneratorIsRequiredToGenerateUrl()
{
$utils = new HttpUtils();
$utils->generateUri(new Request(), 'route_name');
}
private function getUrlGenerator($generatedUrl = '/foo/bar')