Merge branch '3.0' into 3.1

* 3.0:
  [HttpKernel] fixed internal subrequests having an if-modified-since-header
  [Security] Fix deprecated usage of DigestAuthenticationEntryPoint::getKey() in DigestAuthenticationListener
  [Validator] Added additional MasterCard range to the CardSchemeValidator
  Make the exception message more clear.
  [Form] fixed bug - name in ButtonBuilder
  [DoctrineBridge] added missing error code for constraint.
  [ClassLoader] Fix declared classes being computed when not needed
  [varDumper] Fix missing usage of ExceptionCaster::$traceArgs
This commit is contained in:
Nicolas Grekas 2016-07-10 10:05:47 +02:00
commit f2599e4db2
13 changed files with 188 additions and 7 deletions

View File

@ -166,6 +166,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->atPath('property.path.name') ->atPath('property.path.name')
->setParameter('{{ value }}', 'Foo') ->setParameter('{{ value }}', 'Foo')
->setInvalidValue('Foo') ->setInvalidValue('Foo')
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised(); ->assertRaised();
} }
@ -190,6 +191,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->atPath('property.path.bar') ->atPath('property.path.bar')
->setParameter('{{ value }}', 'Foo') ->setParameter('{{ value }}', 'Foo')
->setInvalidValue('Foo') ->setInvalidValue('Foo')
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised(); ->assertRaised();
} }
@ -242,6 +244,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->atPath('property.path.name') ->atPath('property.path.name')
->setParameter('{{ value }}', 'Foo') ->setParameter('{{ value }}', 'Foo')
->setInvalidValue('Foo') ->setInvalidValue('Foo')
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised(); ->assertRaised();
} }
@ -274,6 +277,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->atPath('property.path.name2') ->atPath('property.path.name2')
->setParameter('{{ value }}', 'Bar') ->setParameter('{{ value }}', 'Bar')
->setInvalidValue('Bar') ->setInvalidValue('Bar')
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised(); ->assertRaised();
} }
@ -407,6 +411,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->atPath('property.path.single') ->atPath('property.path.single')
->setParameter('{{ value }}', $entity1) ->setParameter('{{ value }}', $entity1)
->setInvalidValue($entity1) ->setInvalidValue($entity1)
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised(); ->assertRaised();
} }

View File

@ -23,6 +23,8 @@ use Symfony\Component\Validator\Constraint;
*/ */
class UniqueEntity extends Constraint class UniqueEntity extends Constraint
{ {
const NOT_UNIQUE_ERROR = '23bd9dbf-6b9b-41cd-a99e-4844bcf3077f';
public $message = 'This value is already used.'; public $message = 'This value is already used.';
public $service = 'doctrine.orm.validator.unique'; public $service = 'doctrine.orm.validator.unique';
public $em = null; public $em = null;
@ -31,6 +33,10 @@ class UniqueEntity extends Constraint
public $errorPath = null; public $errorPath = null;
public $ignoreNull = true; public $ignoreNull = true;
protected static $errorNames = array(
self::NOT_UNIQUE_ERROR => 'NOT_UNIQUE_ERROR',
);
public function getRequiredOptions() public function getRequiredOptions()
{ {
return array('fields'); return array('fields');

View File

@ -135,6 +135,7 @@ class UniqueEntityValidator extends ConstraintValidator
->atPath($errorPath) ->atPath($errorPath)
->setParameter('{{ value }}', $invalidValue) ->setParameter('{{ value }}', $invalidValue)
->setInvalidValue($invalidValue) ->setInvalidValue($invalidValue)
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->addViolation(); ->addViolation();
} }
} }

View File

@ -43,9 +43,9 @@ class ClassCollectionLoader
self::$loaded[$name] = true; self::$loaded[$name] = true;
$declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
if ($adaptive) { if ($adaptive) {
$declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
// don't include already declared classes // don't include already declared classes
$classes = array_diff($classes, $declared); $classes = array_diff($classes, $declared);
@ -84,11 +84,14 @@ class ClassCollectionLoader
} }
} }
if (!$reload && is_file($cache)) { if (!$reload && file_exists($cache)) {
require_once $cache; require_once $cache;
return; return;
} }
if (!$adaptive) {
$declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
}
$files = array(); $files = array();
$content = ''; $content = '';

View File

@ -62,11 +62,12 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
*/ */
public function __construct($name, array $options = array()) public function __construct($name, array $options = array())
{ {
if (empty($name) && 0 != $name) { $name = (string) $name;
if ('' === $name) {
throw new InvalidArgumentException('Buttons cannot have empty names.'); throw new InvalidArgumentException('Buttons cannot have empty names.');
} }
$this->name = (string) $name; $this->name = $name;
$this->options = $options; $this->options = $options;
} }

View File

@ -0,0 +1,61 @@
<?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\Form\Tests;
use Symfony\Component\Form\ButtonBuilder;
/**
* @author Alexander Cheprasov <cheprasov.84@ya.ru>
*/
class ButtonBuilderTest extends \PHPUnit_Framework_TestCase
{
public function getValidNames()
{
return array(
array('reset'),
array('submit'),
array('foo'),
array('0'),
array(0),
array('button[]'),
);
}
/**
* @dataProvider getValidNames
*/
public function testValidNames($name)
{
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder($name));
}
public function getInvalidNames()
{
return array(
array(''),
array(false),
array(null),
);
}
/**
* @dataProvider getInvalidNames
*/
public function testInvalidNames($name)
{
$this->setExpectedException(
'\Symfony\Component\Form\Exception\InvalidArgumentException',
'Buttons cannot have empty names.'
);
new ButtonBuilder($name);
}
}

View File

@ -129,6 +129,8 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
} }
$server['REMOTE_ADDR'] = '127.0.0.1'; $server['REMOTE_ADDR'] = '127.0.0.1';
unset($server['HTTP_IF_MODIFIED_SINCE']);
unset($server['HTTP_IF_NONE_MATCH']);
$subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server); $subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server);
if ($request->headers->has('Surrogate-Capability')) { if ($request->headers->has('Surrogate-Capability')) {

View File

@ -226,6 +226,19 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName); Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName);
} }
public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest()
{
$expectedSubRequest = Request::create('/');
if (Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) {
$expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1'));
$expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
}
$strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
$request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*'));
$strategy->render('/', $request);
}
} }
class Bar class Bar

View File

@ -0,0 +1,79 @@
<?php
namespace Symfony\Component\Security\Http\Tests\Firewall;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
use Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener;
class DigestAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
{
public function testHandleWithValidDigest()
{
$time = microtime(true) + 1000;
$secret = 'ThisIsASecret';
$nonce = base64_encode($time.':'.md5($time.':'.$secret));
$username = 'user';
$password = 'password';
$realm = 'Welcome, robot!';
$cnonce = 'MDIwODkz';
$nc = '00000001';
$qop = 'auth';
$uri = '/path/info?p1=5&p2=5';
$serverDigest = $this->calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, 'GET', $uri);
$digestData =
'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", '.
'uri="'.$uri.'", cnonce="'.$cnonce.'", nc='.$nc.', qop="'.$qop.'", '.
'response="'.$serverDigest.'"'
;
$request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_DIGEST' => $digestData));
$entryPoint = new DigestAuthenticationEntryPoint($realm, $secret);
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
$user->method('getPassword')->willReturn($password);
$providerKey = 'TheProviderKey';
$tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue(null))
;
$tokenStorage
->expects($this->once())
->method('setToken')
->with($this->equalTo(new UsernamePasswordToken($user, $password, $providerKey)))
;
$userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
$userProvider->method('loadUserByUsername')->willReturn($user);
$listener = new DigestAuthenticationListener($tokenStorage, $userProvider, $providerKey, $entryPoint);
$event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
$event
->expects($this->any())
->method('getRequest')
->will($this->returnValue($request))
;
$listener->handle($event);
}
private function calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, $method, $uri)
{
$response = md5(
md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri)
);
return sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"',
$username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response
);
}
}

View File

@ -73,8 +73,10 @@ class CardSchemeValidator extends ConstraintValidator
'/^6[0-9]{11,18}$/', '/^6[0-9]{11,18}$/',
), ),
// All MasterCard numbers start with the numbers 51 through 55. All have 16 digits. // All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
// October 2016 MasterCard numbers can also start with 222100 through 272099.
'MASTERCARD' => array( 'MASTERCARD' => array(
'/^5[1-5][0-9]{14}$/', '/^5[1-5][0-9]{14}$/',
'/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/',
), ),
// All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13. // All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13.
'VISA' => array( 'VISA' => array(

View File

@ -39,7 +39,7 @@ class PropertyMetadata extends MemberMetadata
public function __construct($class, $name) public function __construct($class, $name)
{ {
if (!property_exists($class, $name)) { if (!property_exists($class, $name)) {
throw new ValidatorException(sprintf('Property %s does not exist in class %s', $name, $class)); throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s"', $name, $class));
} }
parent::__construct($class, $name, $name); parent::__construct($class, $name, $name);

View File

@ -96,6 +96,12 @@ class CardSchemeValidatorTest extends AbstractConstraintValidatorTest
array('MAESTRO', '6594371785970435599'), array('MAESTRO', '6594371785970435599'),
array('MASTERCARD', '5555555555554444'), array('MASTERCARD', '5555555555554444'),
array('MASTERCARD', '5105105105105100'), array('MASTERCARD', '5105105105105100'),
array('MASTERCARD', '2221005555554444'),
array('MASTERCARD', '2230000000000000'),
array('MASTERCARD', '2300000000000000'),
array('MASTERCARD', '2699999999999999'),
array('MASTERCARD', '2709999999999999'),
array('MASTERCARD', '2720995105105100'),
array('VISA', '4111111111111111'), array('VISA', '4111111111111111'),
array('VISA', '4012888888881881'), array('VISA', '4012888888881881'),
array('VISA', '4222222222222'), array('VISA', '4222222222222'),
@ -123,6 +129,8 @@ class CardSchemeValidatorTest extends AbstractConstraintValidatorTest
array('AMEX', '000000000000', CardScheme::INVALID_FORMAT_ERROR), // a lone number array('AMEX', '000000000000', CardScheme::INVALID_FORMAT_ERROR), // a lone number
array('DINERS', '3056930', CardScheme::INVALID_FORMAT_ERROR), // only first part of the number array('DINERS', '3056930', CardScheme::INVALID_FORMAT_ERROR), // only first part of the number
array('DISCOVER', '1117', CardScheme::INVALID_FORMAT_ERROR), // only last 4 digits array('DISCOVER', '1117', CardScheme::INVALID_FORMAT_ERROR), // only last 4 digits
array('MASTERCARD', '2721001234567890', CardScheme::INVALID_FORMAT_ERROR), // Not assigned yet
array('MASTERCARD', '2220991234567890', CardScheme::INVALID_FORMAT_ERROR), // Not assigned yet
); );
} }
} }

View File

@ -198,7 +198,7 @@ class ExceptionCaster
'file' => $a[Caster::PREFIX_PROTECTED.'file'], 'file' => $a[Caster::PREFIX_PROTECTED.'file'],
'line' => $a[Caster::PREFIX_PROTECTED.'line'], 'line' => $a[Caster::PREFIX_PROTECTED.'line'],
)); ));
$a[$xPrefix.'trace'] = new TraceStub($trace); $a[$xPrefix.'trace'] = new TraceStub($trace, self::$traceArgs);
} }
if (empty($a[$xPrefix.'previous'])) { if (empty($a[$xPrefix.'previous'])) {
unset($a[$xPrefix.'previous']); unset($a[$xPrefix.'previous']);