[Security] Refactored security context, moved getUser() implementation to templating

This commit is contained in:
Johannes Schmitt 2011-02-11 01:07:59 +01:00 committed by Fabien Potencier
parent 66fbbd6b17
commit 19bbafc441
18 changed files with 100 additions and 77 deletions

View File

@ -11,7 +11,7 @@
namespace Symfony\Bundle\SecurityBundle\DataCollector;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
@ -25,7 +25,7 @@ class SecurityDataCollector extends DataCollector
{
protected $context;
public function __construct(SecurityContext $context = null)
public function __construct(SecurityContextInterface $context = null)
{
$this->context = $context;
}
@ -53,7 +53,7 @@ class SecurityDataCollector extends DataCollector
$this->data = array(
'enabled' => true,
'authenticated' => $token->isAuthenticated(),
'user' => (string) $token->getUser(),
'user' => (string) $token,
'roles' => array_map(function ($role){ return $role->getRole();}, $token->getRoles()),
);
}

View File

@ -11,8 +11,9 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\Security\Acl\Voter\FieldVote;
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
/**
* SecurityHelper provides read-only access to the security context.
@ -28,7 +29,7 @@ class SecurityHelper extends Helper
*
* @param SecurityContext $context A SecurityContext instance
*/
public function __construct(SecurityContext $context = null)
public function __construct(SecurityContextInterface $context = null)
{
$this->context = $context;
}
@ -39,7 +40,11 @@ class SecurityHelper extends Helper
return false;
}
return $this->context->vote($role, $object, $field);
if (null !== $field) {
$object = new FieldVote($object, $field);
}
return $this->context->vote($role, $object);
}
/**

View File

@ -11,7 +11,8 @@
namespace Symfony\Bundle\SecurityBundle\Twig\Extension;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Acl\Voter\FieldVote;
use Symfony\Component\Security\Core\SecurityContextInterface;
/**
* SecurityExtension exposes security context features.
@ -22,7 +23,7 @@ class SecurityExtension extends \Twig_Extension
{
protected $context;
public function __construct(SecurityContext $context = null)
public function __construct(SecurityContextInterface $context = null)
{
$this->context = $context;
}
@ -33,7 +34,11 @@ class SecurityExtension extends \Twig_Extension
return false;
}
return $this->context->vote($role, $object, $field);
if (null !== $field) {
$object = new FieldVote($object, $field);
}
return $this->context->vote($role, $object);
}
/**

View File

@ -36,10 +36,20 @@ class GlobalVariables
public function getUser()
{
$security = $this->getSecurity();
if ($security && $user = $security->getUser()) {
return $user;
if (!$security = $this->getSecurity()) {
return;
}
if (!$token = $security->getToken()) {
return;
}
$user = $token->getUser();
if (!is_object($user)) {
return;
}
return $user;
}
public function getRequest()

View File

@ -85,9 +85,7 @@ abstract class Token implements TokenInterface
*/
public function __toString()
{
if (is_string($this->user)) {
return $this->user;
} else if ($this->user instanceof AccountInterface) {
if ($this->user instanceof AccountInterface) {
return $this->user->getUsername();
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\Security\Core;
use Symfony\Component\Security\Core\User\AccountInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
@ -22,13 +24,10 @@ use Symfony\Component\Security\Acl\Voter\FieldVote;
* It gives access to the token representing the current user authentication.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class SecurityContext
class SecurityContext implements SecurityContextInterface
{
const ACCESS_DENIED_ERROR = '_security.403_error';
const AUTHENTICATION_ERROR = '_security.last_error';
const LAST_USERNAME = '_security.last_username';
protected $token;
protected $accessDecisionManager;
protected $authenticationManager;
@ -39,30 +38,17 @@ class SecurityContext
*
* @param AccessDecisionManagerInterface|null $accessDecisionManager An AccessDecisionManager instance
*/
public function __construct(AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager = null, $alwaysAuthenticate = false)
public function __construct(AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false)
{
$this->authenticationManager = $authenticationManager;
$this->accessDecisionManager = $accessDecisionManager;
$this->alwaysAuthenticate = $alwaysAuthenticate;
}
public function getUser()
public final function vote($attributes, $object = null)
{
return null === $this->token ? null : $this->token->getUser();
}
public function vote($attributes, $object = null, $field = null)
{
if (null === $this->token || null === $this->accessDecisionManager) {
return false;
}
if ($field !== null) {
if (null === $object) {
throw new \InvalidArgumentException('$object cannot be null when field is not null.');
}
$object = new FieldVote($object, $field);
if (null === $this->token) {
throw new AuthenticationCredentialsNotFoundException('The security context contains no authentication token.');
}
if ($this->alwaysAuthenticate || !$this->token->isAuthenticated()) {

View File

@ -0,0 +1,21 @@
<?php
namespace Symfony\Component\Security\Core;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
/**
* The SecurityContextInterface.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface SecurityContextInterface
{
const ACCESS_DENIED_ERROR = '_security.403_error';
const AUTHENTICATION_ERROR = '_security.last_error';
const LAST_USERNAME = '_security.last_username';
function getToken();
function setToken(TokenInterface $account);
function vote($attributes, $object = null);
}

View File

@ -16,7 +16,6 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**

View File

@ -17,7 +17,7 @@ use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterfa
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -61,12 +61,12 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
/**
* Constructor.
*
* @param SecurityContext $securityContext A SecurityContext instance
* @param SecurityContextInterface $securityContext A SecurityContext instance
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
* @param array $options An array of options for the processing of a successful, or failed authentication attempt
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -36,7 +36,7 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
protected $logger;
protected $eventDispatcher;
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventInterface;
@ -29,7 +29,7 @@ class AnonymousAuthenticationListener implements ListenerInterface
protected $key;
protected $logger;
public function __construct(SecurityContext $context, $key, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $context, $key, LoggerInterface $logger = null)
{
$this->context = $context;
$this->key = $key;
@ -47,7 +47,7 @@ class AnonymousAuthenticationListener implements ListenerInterface
{
$dispatcher->connect('core.security', array($this, 'handle'), 0);
}
/**
* {@inheritDoc}
*/

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@ -34,7 +34,7 @@ class BasicAuthenticationListener implements ListenerInterface
protected $logger;
protected $ignoreFailure;
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@ -38,7 +38,7 @@ class DigestAuthenticationListener implements ListenerInterface
protected $authenticationEntryPoint;
protected $logger;
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@ -41,7 +41,7 @@ class ExceptionListener implements ListenerInterface
protected $errorPage;
protected $logger;
public function __construct(SecurityContext $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDeniedHandler = $accessDeniedHandler;

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventInterface;
use Symfony\Component\HttpFoundation\Response;
@ -32,11 +32,11 @@ class LogoutListener implements ListenerInterface
/**
* Constructor
*
* @param SecurityContext $securityContext
* @param SecurityContextInterface $securityContext
* @param string $logoutPath The path that starts the logout process
* @param string $targetUrl The URL to redirect to after logout
*/
public function __construct(SecurityContext $securityContext, $logoutPath, $targetUrl = '/')
public function __construct(SecurityContextInterface $securityContext, $logoutPath, $targetUrl = '/')
{
$this->securityContext = $securityContext;
$this->logoutPath = $logoutPath;

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\AccountCheckerInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
@ -48,7 +48,7 @@ class SwitchUserListener implements ListenerInterface
/**
* Constructor.
*/
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, AccountCheckerInterface $accountChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH')
public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, AccountCheckerInterface $accountChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH')
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
@ -27,7 +27,7 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener
protected $userKey;
protected $credentialKey;
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null)
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null)
{
parent::__construct($securityContext, $authenticationManager, $providerKey, $logger);

View File

@ -16,18 +16,6 @@ use Symfony\Component\Security\Core\SecurityContext;
class SecurityContextTest extends \PHPUnit_Framework_TestCase
{
public function testGetUser()
{
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'));
$this->assertNull($context->getUser());
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$token->expects($this->once())->method('getUser')->will($this->returnValue('foo'));
$context->setToken($token);
$this->assertEquals('foo', $context->getUser());
}
public function testVoteAuthenticatesTokenIfNecessary()
{
$authManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
@ -53,13 +41,21 @@ class SecurityContextTest extends \PHPUnit_Framework_TestCase
$this->assertSame($newToken, $context->getToken());
}
/**
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
*/
public function testVoteWithoutAuthenticationToken()
{
$context = new SecurityContext(
$this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'),
$this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')
);
$context->vote('ROLE_FOO');
}
public function testVote()
{
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'));
$this->assertFalse($context->vote('ROLE_FOO'));
$context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'));
$this->assertFalse($context->vote('ROLE_FOO'));
$manager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');
$manager->expects($this->once())->method('decide')->will($this->returnValue(false));
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), $manager);
@ -85,7 +81,10 @@ class SecurityContextTest extends \PHPUnit_Framework_TestCase
public function testGetSetToken()
{
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'));
$context = new SecurityContext(
$this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'),
$this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')
);
$this->assertNull($context->getToken());
$context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'));