Skip to content
This repository has been archived by the owner on May 31, 2024. It is now read-only.

Commit

Permalink
Merge branch '2.8'
Browse files Browse the repository at this point in the history
* 2.8: (28 commits)
  Detect Mintty for color support on Windows
  Detect Mintty for color support on Windows
  [WebProfilerBundle] Fix search button click listener
  [Form][Type Date/Time] added choice_translation_domain option.
  Massively simplifying the BC and deprecated-throwing code thanks to suggestions by stof in #15870
  Making all "debug" messages use the debug router
  Making GuardTokenInterface extend TokenInterface
  Updating behavior to not continue after an authenticator has set the response
  Add a group for tests of the finder against the FTP server
  Fix trigger_error calls
  Fix legacy security tests
  tweaking message related to configuration edge case that we want to be helpful with
  Minor tweaks - lowering the required security-http requirement and nulling out a test field
  Fix license headers
  Fix license headers
  Fix license headers
  Ensure the ClockMock is loaded before using it in the testsuite
  Allow serializer 3.0 in the PropertyInfo component
  Add the replace rules for the security-guard component
  Forbid serializing a Crawler
  ...
  • Loading branch information
fabpot committed Sep 27, 2015
2 parents 1e2b642 + ae2c4c8 commit 085fdff
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 46 deletions.
4 changes: 2 additions & 2 deletions Core/Authorization/AccessDecisionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
*/
public function supportsAttribute($attribute)
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);

foreach ($this->voters as $voter) {
if ($voter->supportsAttribute($attribute)) {
Expand All @@ -93,7 +93,7 @@ public function supportsAttribute($attribute)
*/
public function supportsClass($class)
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);

foreach ($this->voters as $voter) {
if ($voter->supportsClass($class)) {
Expand Down
37 changes: 14 additions & 23 deletions Core/Authorization/Voter/AbstractVoter.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract class AbstractVoter implements VoterInterface
*/
public function supportsAttribute($attribute)
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);

return in_array($attribute, $this->getSupportedAttributes());
}
Expand All @@ -36,7 +36,7 @@ public function supportsAttribute($attribute)
*/
public function supportsClass($class)
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);

foreach ($this->getSupportedClasses() as $supportedClass) {
if ($supportedClass === $class || is_subclass_of($class, $supportedClass)) {
Expand Down Expand Up @@ -70,12 +70,6 @@ public function vote(TokenInterface $token, $object, array $attributes)
$vote = self::ACCESS_ABSTAIN;
$class = get_class($object);

$reflector = new \ReflectionMethod($this, 'voteOnAttribute');
$isNewOverwritten = $reflector->getDeclaringClass()->getName() !== 'Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter';
if (!$isNewOverwritten) {
@trigger_error(sprintf("The AbstractVoter::isGranted method is deprecated since 2.8 and won't be called anymore in 3.0. Override voteOnAttribute() instead.", $reflector->class), E_USER_DEPRECATED);
}

foreach ($attributes as $attribute) {
if (!$this->supports($attribute, $class)) {
continue;
Expand All @@ -84,16 +78,9 @@ public function vote(TokenInterface $token, $object, array $attributes)
// as soon as at least one attribute is supported, default is to deny access
$vote = self::ACCESS_DENIED;

if ($isNewOverwritten) {
if ($this->voteOnAttribute($attribute, $object, $token)) {
// grant access as soon as at least one voter returns a positive response
return self::ACCESS_GRANTED;
}
} else {
if ($this->isGranted($attribute, $object, $token->getUser())) {
// grant access as soon as at least one voter returns a positive response
return self::ACCESS_GRANTED;
}
if ($this->voteOnAttribute($attribute, $object, $token)) {
// grant access as soon as at least one voter returns a positive response
return self::ACCESS_GRANTED;
}
}

Expand All @@ -115,7 +102,7 @@ public function vote(TokenInterface $token, $object, array $attributes)
*/
protected function supports($attribute, $class)
{
@trigger_error('The getSupportedClasses and getSupportedAttributes methods are deprecated since version 2.8 and will be removed in version 3.0. Overwrite supports instead.');
@trigger_error('The getSupportedClasses and getSupportedAttributes methods are deprecated since version 2.8 and will be removed in version 3.0. Overwrite supports instead.', E_USER_DEPRECATED);

$classIsSupported = false;
foreach ($this->getSupportedClasses() as $supportedClass) {
Expand Down Expand Up @@ -159,7 +146,7 @@ protected function isClassInstanceOf($actualClass, $expectedClass)
*/
protected function getSupportedClasses()
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);
}

/**
Expand All @@ -171,7 +158,7 @@ protected function getSupportedClasses()
*/
protected function getSupportedAttributes()
{
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.');
@trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED);
}

/**
Expand All @@ -191,7 +178,8 @@ protected function getSupportedAttributes()
*/
protected function isGranted($attribute, $object, $user = null)
{
return false;
// forces isGranted() or voteOnAttribute() to be overridden
throw new \BadMethodCallException(sprintf('You must override the voteOnAttribute() method in "%s".', get_class($this)));
}

/**
Expand All @@ -211,6 +199,9 @@ protected function isGranted($attribute, $object, $user = null)
*/
protected function voteOnAttribute($attribute, $object, TokenInterface $token)
{
return false;
// the user should override this method, and not rely on the deprecated isGranted()
@trigger_error(sprintf("The AbstractVoter::isGranted() method is deprecated since 2.8 and won't be called anymore in 3.0. Override voteOnAttribute() in %s instead.", get_class($this)), E_USER_DEPRECATED);

return $this->isGranted($attribute, $object, $token->getUser());
}
}
38 changes: 30 additions & 8 deletions Core/Tests/Authorization/Voter/AbstractVoterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,10 @@
*/
class AbstractVoterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var AbstractVoter
*/
private $voter;

private $token;

protected function setUp()
{
$this->voter = new VoterFixture();

$tokenMock = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$tokenMock
->expects($this->any())
Expand All @@ -44,7 +37,9 @@ protected function setUp()
*/
public function testVote($expectedVote, $object, $attributes, $message)
{
$this->assertEquals($expectedVote, $this->voter->vote($this->token, $object, $attributes), $message);
$voter = new VoterFixture();

$this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message);
}

/**
Expand All @@ -58,6 +53,16 @@ public function testVoteUsingDeprecatedIsGranted($expectedVote, $object, $attrib
$this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message);
}

/**
* @group legacy
* @expectedException \BadMethodCallException
*/
public function testNoOverriddenMethodsThrowsException()
{
$voter = new DeprecatedVoterNothingImplementedFixture();
$voter->vote($this->token, new ObjectFixture(), array('foo'));
}

public function getData()
{
return array(
Expand Down Expand Up @@ -113,6 +118,23 @@ protected function isGranted($attribute, $object, $user = null)
}
}

class DeprecatedVoterNothingImplementedFixture extends AbstractVoter
{
protected function getSupportedClasses()
{
return array(
'Symfony\Component\Security\Core\Tests\Authorization\Voter\ObjectFixture',
);
}

protected function getSupportedAttributes()
{
return array('foo', 'bar', 'baz');
}

// this is a bad voter that hasn't overridden isGranted or voteOnAttribute
}

class ObjectFixture
{
}
Expand Down
9 changes: 9 additions & 0 deletions Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<?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\Core\Tests\Authorization\Voter;

use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
Expand Down
8 changes: 6 additions & 2 deletions Core/Util/SecureRandom.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public function __construct($seedFile = null, LoggerInterface $logger = null)
$this->logger = $logger;

// determine whether to use OpenSSL
if (!function_exists('openssl_random_pseudo_bytes')) {
if (!function_exists('random_bytes') && !function_exists('openssl_random_pseudo_bytes')) {
if (null !== $this->logger) {
$this->logger->notice('It is recommended that you enable the "openssl" extension for random number generation.');
$this->logger->notice('It is recommended that you install the "paragonie/random_compat" library or enable the "openssl" extension for random number generation.');
}
$this->useOpenSsl = false;
} else {
Expand All @@ -58,6 +58,10 @@ public function __construct($seedFile = null, LoggerInterface $logger = null)
*/
public function nextBytes($nbBytes)
{
if (function_exists('random_bytes')) {
return random_bytes($nbBytes);
}

// try OpenSSL
if ($this->useOpenSsl) {
$bytes = openssl_random_pseudo_bytes($nbBytes, $strong);
Expand Down
20 changes: 13 additions & 7 deletions Guard/Firewall/GuardAuthenticationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function __construct(GuardAuthenticatorHandler $guardHandler, Authenticat
public function handle(GetResponseEvent $event)
{
if (null !== $this->logger) {
$this->logger->info('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
$this->logger->debug('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
}

foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
Expand All @@ -75,6 +75,12 @@ public function handle(GetResponseEvent $event)
$uniqueGuardKey = $this->providerKey.'_'.$key;

$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);

if ($event->hasResponse()) {
$this->logger->debug(sprintf('The "%s" authenticator set the response. Any later authenticator will not be called', get_class($guardAuthenticator)));

break;
}
}
}

Expand All @@ -83,7 +89,7 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn
$request = $event->getRequest();
try {
if (null !== $this->logger) {
$this->logger->info('Calling getCredentials on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
}

// allow the authenticator to fetch authentication info from the request
Expand All @@ -98,7 +104,7 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn
$token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);

if (null !== $this->logger) {
$this->logger->info('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
}
// pass the token into the AuthenticationManager system
// this indirectly calls GuardAuthenticationProvider::authenticate()
Expand Down Expand Up @@ -130,13 +136,13 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
if ($response instanceof Response) {
if (null !== $this->logger) {
$this->logger->info('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
}

$event->setResponse($response);
} else {
if (null !== $this->logger) {
$this->logger->info('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator)));
}
}

Expand Down Expand Up @@ -167,15 +173,15 @@ private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticat
{
if (null === $this->rememberMeServices) {
if (null !== $this->logger) {
$this->logger->info('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator)));
}

return;
}

if (!$guardAuthenticator->supportsRememberMe()) {
if (null !== $this->logger) {
$this->logger->info('Remember me skipped: your authenticator does not support it.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Remember me skipped: your authenticator does not support it.', array('authenticator' => get_class($guardAuthenticator)));
}

return;
Expand Down
36 changes: 35 additions & 1 deletion Guard/Tests/Firewall/GuardAuthenticationListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,36 @@ public function testHandleSuccess()
$listener->handle($this->event);
}

public function testHandleSuccessStopsAfterResponseIsSet()
{
$authenticator1 = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
$authenticator2 = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');

// mock the first authenticator to fail, and set a Response
$authenticator1
->expects($this->once())
->method('getCredentials')
->willThrowException(new AuthenticationException());
$this->guardAuthenticatorHandler
->expects($this->once())
->method('handleAuthenticationFailure')
->willReturn(new Response());
// the second authenticator should *never* be called
$authenticator2
->expects($this->never())
->method('getCredentials');

$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
'my_firewall',
array($authenticator1, $authenticator2),
$this->logger
);

$listener->handle($this->event);
}

public function testHandleSuccessWithRememberMe()
{
$authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
Expand Down Expand Up @@ -201,7 +231,10 @@ protected function setUp()

$this->request = new Request(array(), array(), array(), array(), array(), array());

$this->event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
$this->event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->setMethods(array('getRequest'))
->getMock();
$this->event
->expects($this->any())
->method('getRequest')
Expand All @@ -218,5 +251,6 @@ protected function tearDown()
$this->event = null;
$this->logger = null;
$this->request = null;
$this->rememberMeServices = null;
}
}
4 changes: 3 additions & 1 deletion Guard/Token/GuardTokenInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Security\Guard\Token;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

/**
* A marker interface that both guard tokens implement.
*
Expand All @@ -20,6 +22,6 @@
*
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
interface GuardTokenInterface
interface GuardTokenInterface extends TokenInterface
{
}
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"replace": {
"symfony/security-core": "self.version",
"symfony/security-csrf": "self.version",
"symfony/security-guard": "self.version",
"symfony/security-http": "self.version"
},
"require-dev": {
Expand All @@ -41,10 +42,11 @@
"suggest": {
"symfony/class-loader": "For using the ACL generateSql script",
"symfony/finder": "For using the ACL generateSql script",
"symfony/form": "",
"symfony/validator": "For using the user password constraint",
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
"doctrine/dbal": "For using the built-in ACL implementation",
"symfony/expression-language": "For using the expression voter"
"symfony/expression-language": "For using the expression voter",
"paragonie/random_compat": ""
},
"autoload": {
"psr-4": { "Symfony\\Component\\Security\\": "" }
Expand Down

0 comments on commit 085fdff

Please sign in to comment.