added authentication trust resolver
This commit is contained in:
parent
763bba9b89
commit
abe8047262
@ -450,7 +450,7 @@ class SecurityExtension extends Extension
|
||||
$exceptionListenerId = 'security.exception_listener.'.$id;
|
||||
$listener = $container->setDefinition($exceptionListenerId, clone $container->getDefinition('security.exception_listener'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[1] = null === $defaultEntryPoint ? null : new Reference($defaultEntryPoint);
|
||||
$arguments[2] = null === $defaultEntryPoint ? null : new Reference($defaultEntryPoint);
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
return $exceptionListenerId;
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
<parameter key="security.user.provider.in_memory.class">Symfony\Component\Security\User\InMemoryUserProvider</parameter>
|
||||
|
||||
<parameter key="security.authentication.trust_resolver.class">Symfony\Component\Security\Authentication\AuthenticationTrustResolver</parameter>
|
||||
<parameter key="security.authentication.trust_resolver.anonymous_class">Symfony\Component\Security\Authentication\Token\AnonymousToken</parameter>
|
||||
<parameter key="security.authentication.trust_resolver.rememberme_class">Symfony\Component\Security\Authentication\Token\RememberMeToken</parameter>
|
||||
|
||||
<parameter key="security.authentication.provider.dao.class">Symfony\Component\Security\Authentication\Provider\DaoAuthenticationProvider</parameter>
|
||||
<parameter key="security.authentication.provider.pre_authenticated.class">Symfony\Component\Security\Authentication\Provider\PreAuthenticatedAuthenticationProvider</parameter>
|
||||
|
||||
@ -101,6 +105,11 @@
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.trust_resolver" class="%security.authentication.trust_resolver.class%">
|
||||
<argument>%security.authentication.trust_resolver.anonymous_class%</argument>
|
||||
<argument>%security.authentication.trust_resolver.rememberme_class%</argument>
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.retry_entry_point" class="%security.authentication.retry_entry_point.class%" />
|
||||
|
||||
<service id="security.authentication.form_entry_point" class="%security.authentication.form_entry_point.class%">
|
||||
@ -132,6 +141,7 @@
|
||||
<tag name="security.voter" />
|
||||
</service>
|
||||
<service id="security.access.authenticated_voter" class="%security.access.authenticated_voter.class%">
|
||||
<argument type="service" id="security.authentication.trust_resolver" />
|
||||
<tag name="security.voter" />
|
||||
</service>
|
||||
<service id="security.access.role_hierarchy_voter" class="%security.access.role_hierarchy_voter.class%">
|
||||
|
@ -62,6 +62,7 @@
|
||||
|
||||
<service id="security.exception_listener" class="%security.exception_listener.class%">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.trust_resolver" />
|
||||
<argument type="service" id="security.authentication.entry_point" on-invalid="null" />
|
||||
<argument>%security.access_denied.url%</argument>
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Symfony\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationTrustResolverInterface;
|
||||
use Symfony\Component\Security\Authentication\EntryPoint\AuthenticationEntryPointInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
@ -33,13 +34,15 @@ class ExceptionListener implements ListenerInterface
|
||||
{
|
||||
protected $context;
|
||||
protected $authenticationEntryPoint;
|
||||
protected $authenticationTrustResolver;
|
||||
protected $errorPage;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(SecurityContext $context, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->context = $context;
|
||||
$this->authenticationEntryPoint = $authenticationEntryPoint;
|
||||
$this->authenticationTrustResolver = $trustResolver;
|
||||
$this->errorPage = $errorPage;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
@ -87,9 +90,9 @@ class ExceptionListener implements ListenerInterface
|
||||
}
|
||||
} elseif ($exception instanceof AccessDeniedException) {
|
||||
$token = $this->context->getToken();
|
||||
if (null === $token || $token instanceof AnonymousToken) {
|
||||
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Access denied (user is anonymous); redirecting to authentication entry point');
|
||||
$this->logger->info('Access denied (user is not fully authenticated); redirecting to authentication entry point');
|
||||
}
|
||||
|
||||
try {
|
||||
@ -101,7 +104,7 @@ class ExceptionListener implements ListenerInterface
|
||||
}
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Access is denied (and user is not anonymous)');
|
||||
$this->logger->info('Access is denied (and user is neither anonymous, nor remember-me)');
|
||||
}
|
||||
|
||||
if (null === $this->errorPage) {
|
||||
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
|
||||
/**
|
||||
* The default implementation of the authentication trust resolver.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AuthenticationTrustResolver implements AuthenticationTrustResolverInterface
|
||||
{
|
||||
protected $anonymousClass;
|
||||
protected $rememberMeClass;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $anonymousClass
|
||||
* @param string $rememberMeClass
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($anonymousClass, $rememberMeClass)
|
||||
{
|
||||
$this->anonymousClass = $anonymousClass;
|
||||
$this->rememberMeClass = $rememberMeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isAnonymous(TokenInterface $token = null)
|
||||
{
|
||||
if (null === $token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $token instanceof $this->anonymousClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isRememberMe(TokenInterface $token = null)
|
||||
{
|
||||
if (null === $token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $token instanceof $this->rememberMeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isFullFledged(TokenInterface $token = null)
|
||||
{
|
||||
if (null === $token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !$this->isAnonymous($token) && !$this->isRememberMe($token);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for resolving the authentication status of a given token.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface AuthenticationTrustResolverInterface
|
||||
{
|
||||
/**
|
||||
* Resolves whether the passed token implementation is authenticated
|
||||
* anonymously.
|
||||
*
|
||||
* If null is passed, the method must return false.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
function isAnonymous(TokenInterface $token = null);
|
||||
|
||||
/**
|
||||
* Resolves whether the passed token implementation is authenticated
|
||||
* using remember-me capabilities.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
function isRememberMe(TokenInterface $token = null);
|
||||
|
||||
/**
|
||||
* Resolves whether the passed token implementation is fully authenticated.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
function isFullFledged(TokenInterface $token = null);
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\Token;
|
||||
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentTokenInterface;
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
|
||||
/**
|
||||
* Base class for "Remember Me" tokens
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class RememberMeToken extends Token
|
||||
{
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* The persistent token which resulted in this authentication token.
|
||||
*
|
||||
* @var PersistentTokenInterface
|
||||
*/
|
||||
protected $persistentToken;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $key
|
||||
*/
|
||||
public function __construct(AccountInterface $user, $key) {
|
||||
parent::__construct($user->getRoles());
|
||||
|
||||
if (0 === strlen($key)) {
|
||||
throw new \InvalidArgumentException('$key cannot be empty.');
|
||||
}
|
||||
|
||||
$this->user = $user;
|
||||
$this->key = $key;
|
||||
$this->setAuthenticated(true);
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setPersistentToken(PersistentTokenInterface $persistentToken)
|
||||
{
|
||||
$this->persistentToken = $persistentToken;
|
||||
}
|
||||
|
||||
public function getPersistentToken()
|
||||
{
|
||||
return $this->persistentToken;
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Symfony\Component\Security\Authorization\Voter;
|
||||
|
||||
use Symfony\Component\Security\Authentication\AuthenticationTrustResolverInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\AnonymousToken;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
@ -15,22 +15,40 @@ use Symfony\Component\Security\Authentication\Token\AnonymousToken;
|
||||
*/
|
||||
|
||||
/**
|
||||
* AuthenticatedVoter votes if an attribute like IS_AUTHENTICATED_FULLY or
|
||||
* IS_AUTHENTICATED_ANONYMOUSLY is present.
|
||||
* AuthenticatedVoter votes if an attribute like IS_AUTHENTICATED_FULLY,
|
||||
* IS_AUTHENTICATED_REMEMBERED, or IS_AUTHENTICATED_ANONYMOUSLY is present.
|
||||
*
|
||||
* This list is most restrictive to least restrictive checking.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AuthenticatedVoter implements VoterInterface
|
||||
{
|
||||
const IS_AUTHENTICATED_FULLY = 'IS_AUTHENTICATED_FULLY';
|
||||
const IS_AUTHENTICATED_REMEMBERED = 'IS_AUTHENTICATED_REMEMBERED';
|
||||
const IS_AUTHENTICATED_ANONYMOUSLY = 'IS_AUTHENTICATED_ANONYMOUSLY';
|
||||
|
||||
protected $authenticationTrustResolver;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AuthenticationTrustResolverInterface $authenticationTrustResolver
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver)
|
||||
{
|
||||
$this->authenticationTrustResolver = $authenticationTrustResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsAttribute($attribute)
|
||||
{
|
||||
return null !== $attribute && (self::IS_AUTHENTICATED_FULLY === $attribute || self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute);
|
||||
return null !== $attribute && (self::IS_AUTHENTICATED_FULLY === $attribute || self::IS_AUTHENTICATED_REMEMBERED === $attribute || self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,11 +72,21 @@ class AuthenticatedVoter implements VoterInterface
|
||||
|
||||
$result = VoterInterface::ACCESS_DENIED;
|
||||
|
||||
if (self::IS_AUTHENTICATED_FULLY === $attribute && !$token instanceof AnonymousToken) {
|
||||
if (self::IS_AUTHENTICATED_FULLY === $attribute
|
||||
&& $this->authenticationTrustResolver->isFullFledged($token)) {
|
||||
return VoterInterface::ACCESS_GRANTED;
|
||||
}
|
||||
|
||||
if (self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute) {
|
||||
if (self::IS_AUTHENTICATED_REMEMBERED === $attribute
|
||||
&& ($this->authenticationTrustResolver->isRememberMe($token)
|
||||
|| $this->authenticationTrustResolver->isFullFledged($token))) {
|
||||
return VoterInterface::ACCESS_GRANTED;
|
||||
}
|
||||
|
||||
if (self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute
|
||||
&& ($this->authenticationTrustResolver->isAnonymous($token)
|
||||
|| $this->authenticationTrustResolver->isRememberMe($token)
|
||||
|| $this->authenticationTrustResolver->isFullFledged($token))) {
|
||||
return VoterInterface::ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authentication;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\AnonymousToken;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationTrustResolver;
|
||||
|
||||
class AuthenticationTrustResolverTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testIsAnonymous()
|
||||
{
|
||||
$resolver = $this->getResolver();
|
||||
|
||||
$this->assertFalse($resolver->isAnonymous(null));
|
||||
$this->assertFalse($resolver->isAnonymous($this->getToken()));
|
||||
$this->assertFalse($resolver->isAnonymous($this->getRememberMeToken()));
|
||||
$this->assertTrue($resolver->isAnonymous($this->getAnonymousToken()));
|
||||
}
|
||||
|
||||
public function testIsRememberMe()
|
||||
{
|
||||
$resolver = $this->getResolver();
|
||||
|
||||
$this->assertFalse($resolver->isRememberMe(null));
|
||||
$this->assertFalse($resolver->isRememberMe($this->getToken()));
|
||||
$this->assertFalse($resolver->isRememberMe($this->getAnonymousToken()));
|
||||
$this->assertTrue($resolver->isRememberMe($this->getRememberMeToken()));
|
||||
}
|
||||
|
||||
public function testisFullFledged()
|
||||
{
|
||||
$resolver = $this->getResolver();
|
||||
|
||||
$this->assertFalse($resolver->isFullFledged(null));
|
||||
$this->assertFalse($resolver->isFullFledged($this->getAnonymousToken()));
|
||||
$this->assertFalse($resolver->isFullFledged($this->getRememberMeToken()));
|
||||
$this->assertTrue($resolver->isFullFledged($this->getToken()));
|
||||
}
|
||||
|
||||
protected function getToken()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
}
|
||||
|
||||
protected function getAnonymousToken()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\AnonymousToken', null, array('', ''));
|
||||
}
|
||||
|
||||
protected function getRememberMeToken()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\RememberMeToken', array('setPersistent'), array(), '', false);
|
||||
}
|
||||
|
||||
protected function getResolver()
|
||||
{
|
||||
return new AuthenticationTrustResolver(
|
||||
'Symfony\\Component\\Security\\Authentication\\Token\\AnonymousToken',
|
||||
'Symfony\\Component\\Security\\Authentication\\Token\\RememberMeToken'
|
||||
);
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authorization\Voter;
|
||||
|
||||
use Symfony\Component\Security\Authentication\AuthenticationTrustResolver;
|
||||
use Symfony\Component\Security\Authorization\Voter\AuthenticatedVoter;
|
||||
use Symfony\Component\Security\Authorization\Voter\VoterInterface;
|
||||
use Symfony\Component\Security\Role\Role;
|
||||
@ -18,7 +19,7 @@ class AuthenticatedVoterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSupportsClass()
|
||||
{
|
||||
$voter = new AuthenticatedVoter();
|
||||
$voter = new AuthenticatedVoter($this->getResolver());
|
||||
$this->assertTrue($voter->supportsClass('stdClass'));
|
||||
}
|
||||
|
||||
@ -27,7 +28,7 @@ class AuthenticatedVoterTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testVote($authenticated, $attributes, $expected)
|
||||
{
|
||||
$voter = new AuthenticatedVoter();
|
||||
$voter = new AuthenticatedVoter($this->getResolver());
|
||||
|
||||
$this->assertSame($expected, $voter->vote($this->getToken($authenticated), null, $attributes));
|
||||
}
|
||||
@ -35,23 +36,41 @@ class AuthenticatedVoterTest extends \PHPUnit_Framework_TestCase
|
||||
public function getVoteTests()
|
||||
{
|
||||
return array(
|
||||
array(true, array(), VoterInterface::ACCESS_ABSTAIN),
|
||||
array(true, array('FOO'), VoterInterface::ACCESS_ABSTAIN),
|
||||
array(false, array(), VoterInterface::ACCESS_ABSTAIN),
|
||||
array(false, array('FOO'), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('fully', array(), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('fully', array('FOO'), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('remembered', array(), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('remembered', array('FOO'), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('anonymously', array(), VoterInterface::ACCESS_ABSTAIN),
|
||||
array('anonymously', array('FOO'), VoterInterface::ACCESS_ABSTAIN),
|
||||
|
||||
array(true, array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array(false, array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array('fully', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array('remembered', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array('anonymously', array('IS_AUTHENTICATED_ANONYMOUSLY'), VoterInterface::ACCESS_GRANTED),
|
||||
|
||||
array(true, array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array(false, array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_DENIED),
|
||||
array('fully', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_GRANTED),
|
||||
array('remembered', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_GRANTED),
|
||||
array('anonymously', array('IS_AUTHENTICATED_REMEMBERED'), VoterInterface::ACCESS_DENIED),
|
||||
|
||||
array('fully', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_GRANTED),
|
||||
array('remembered', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_DENIED),
|
||||
array('anonymously', array('IS_AUTHENTICATED_FULLY'), VoterInterface::ACCESS_DENIED),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getResolver()
|
||||
{
|
||||
return new AuthenticationTrustResolver(
|
||||
'Symfony\\Component\\Security\\Authentication\\Token\\AnonymousToken',
|
||||
'Symfony\\Component\\Security\\Authentication\\Token\\RememberMeToken'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getToken($authenticated)
|
||||
{
|
||||
if ($authenticated) {
|
||||
if ('fully' === $authenticated) {
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
} else if ('remembered' === $authenticated) {
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\RememberMeToken', array('setPersistent'), array(), '', false);
|
||||
} else {
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\AnonymousToken', null, array('', ''));
|
||||
}
|
||||
|
Reference in New Issue
Block a user